aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/archive/tar/reader.go23
-rw-r--r--src/archive/tar/writer.go2
-rw-r--r--src/archive/zip/reader.go2
-rw-r--r--src/bufio/bufio.go2
-rw-r--r--src/bytes/bytes.go10
-rw-r--r--src/bytes/bytes_test.go51
-rw-r--r--src/bytes/reader.go5
-rw-r--r--src/bytes/reader_test.go41
-rw-r--r--src/cmd/api/goapi.go5
-rw-r--r--src/cmd/asm/internal/asm/asm.go53
-rw-r--r--src/cmd/asm/internal/asm/endtoend_test.go5
-rw-r--r--src/cmd/asm/internal/asm/parse.go16
-rw-r--r--src/cmd/asm/internal/asm/testdata/s390x.s4
-rw-r--r--src/cmd/asm/main.go31
-rw-r--r--src/cmd/cgo/ast.go10
-rw-r--r--src/cmd/cgo/gcc.go2
-rw-r--r--src/cmd/cgo/main.go2
-rw-r--r--src/cmd/cgo/out.go2
-rw-r--r--src/cmd/compile/internal/amd64/galign.go12
-rw-r--r--src/cmd/compile/internal/amd64/gsubr.go2
-rw-r--r--src/cmd/compile/internal/amd64/ssa.go261
-rw-r--r--src/cmd/compile/internal/arm/cgen64.go6
-rw-r--r--src/cmd/compile/internal/arm/galign.go7
-rw-r--r--src/cmd/compile/internal/arm/gsubr.go4
-rw-r--r--src/cmd/compile/internal/arm/ssa.go8
-rw-r--r--src/cmd/compile/internal/arm64/galign.go9
-rw-r--r--src/cmd/compile/internal/arm64/ggen.go47
-rw-r--r--src/cmd/compile/internal/arm64/gsubr.go18
-rw-r--r--src/cmd/compile/internal/arm64/peep.go3
-rw-r--r--src/cmd/compile/internal/arm64/prog.go3
-rw-r--r--src/cmd/compile/internal/gc/alg.go20
-rw-r--r--src/cmd/compile/internal/gc/align.go54
-rw-r--r--src/cmd/compile/internal/gc/bexport.go301
-rw-r--r--src/cmd/compile/internal/gc/bimport.go363
-rw-r--r--src/cmd/compile/internal/gc/builtin.go231
-rw-r--r--src/cmd/compile/internal/gc/builtin/runtime.go2
-rw-r--r--src/cmd/compile/internal/gc/cgen.go239
-rw-r--r--src/cmd/compile/internal/gc/closure.go4
-rw-r--r--src/cmd/compile/internal/gc/const.go179
-rw-r--r--src/cmd/compile/internal/gc/cplx.go9
-rw-r--r--src/cmd/compile/internal/gc/dcl.go107
-rw-r--r--src/cmd/compile/internal/gc/esc.go30
-rw-r--r--src/cmd/compile/internal/gc/export.go32
-rw-r--r--src/cmd/compile/internal/gc/fmt.go190
-rw-r--r--src/cmd/compile/internal/gc/gen.go20
-rw-r--r--src/cmd/compile/internal/gc/go.go219
-rw-r--r--src/cmd/compile/internal/gc/gsubr.go51
-rw-r--r--src/cmd/compile/internal/gc/init.go2
-rw-r--r--src/cmd/compile/internal/gc/inl.go60
-rw-r--r--src/cmd/compile/internal/gc/lex.go238
-rw-r--r--src/cmd/compile/internal/gc/lex_test.go79
-rw-r--r--src/cmd/compile/internal/gc/main.go151
-rw-r--r--src/cmd/compile/internal/gc/obj.go79
-rw-r--r--src/cmd/compile/internal/gc/opnames.go8
-rw-r--r--src/cmd/compile/internal/gc/order.go46
-rw-r--r--src/cmd/compile/internal/gc/parser.go39
-rw-r--r--src/cmd/compile/internal/gc/pgen.go18
-rw-r--r--src/cmd/compile/internal/gc/pgen_test.go20
-rw-r--r--src/cmd/compile/internal/gc/plive.go24
-rw-r--r--src/cmd/compile/internal/gc/popt.go28
-rw-r--r--src/cmd/compile/internal/gc/racewalk.go30
-rw-r--r--src/cmd/compile/internal/gc/range.go18
-rw-r--r--src/cmd/compile/internal/gc/reflect.go490
-rw-r--r--src/cmd/compile/internal/gc/reg.go17
-rw-r--r--src/cmd/compile/internal/gc/select.go8
-rw-r--r--src/cmd/compile/internal/gc/sinit.go111
-rw-r--r--src/cmd/compile/internal/gc/sizeof_test.go15
-rw-r--r--src/cmd/compile/internal/gc/ssa.go377
-rw-r--r--src/cmd/compile/internal/gc/ssa_test.go4
-rw-r--r--src/cmd/compile/internal/gc/subr.go134
-rw-r--r--src/cmd/compile/internal/gc/swt.go2
-rw-r--r--src/cmd/compile/internal/gc/syntax.go1
-rw-r--r--src/cmd/compile/internal/gc/testdata/dupLoad.go83
-rw-r--r--src/cmd/compile/internal/gc/testdata/namedReturn.go105
-rw-r--r--src/cmd/compile/internal/gc/type.go518
-rw-r--r--src/cmd/compile/internal/gc/typecheck.go309
-rw-r--r--src/cmd/compile/internal/gc/universe.go13
-rw-r--r--src/cmd/compile/internal/gc/unsafe.go4
-rw-r--r--src/cmd/compile/internal/gc/walk.go191
-rw-r--r--src/cmd/compile/internal/mips64/galign.go10
-rw-r--r--src/cmd/compile/internal/mips64/gsubr.go6
-rw-r--r--src/cmd/compile/internal/ppc64/galign.go13
-rw-r--r--src/cmd/compile/internal/ppc64/gsubr.go6
-rw-r--r--src/cmd/compile/internal/ppc64/reg.go2
-rw-r--r--src/cmd/compile/internal/s390x/cgen.go178
-rw-r--r--src/cmd/compile/internal/s390x/galign.go66
-rw-r--r--src/cmd/compile/internal/s390x/ggen.go577
-rw-r--r--src/cmd/compile/internal/s390x/gsubr.go1115
-rw-r--r--src/cmd/compile/internal/s390x/peep.go1664
-rw-r--r--src/cmd/compile/internal/s390x/prog.go179
-rw-r--r--src/cmd/compile/internal/s390x/reg.go130
-rw-r--r--src/cmd/compile/internal/ssa/TODO2
-rw-r--r--src/cmd/compile/internal/ssa/check.go20
-rw-r--r--src/cmd/compile/internal/ssa/compile.go13
-rw-r--r--src/cmd/compile/internal/ssa/config.go7
-rw-r--r--src/cmd/compile/internal/ssa/cse.go60
-rw-r--r--src/cmd/compile/internal/ssa/cse_test.go1
-rw-r--r--src/cmd/compile/internal/ssa/decompose.go29
-rw-r--r--src/cmd/compile/internal/ssa/dom.go290
-rw-r--r--src/cmd/compile/internal/ssa/dom_test.go208
-rw-r--r--src/cmd/compile/internal/ssa/export_test.go3
-rw-r--r--src/cmd/compile/internal/ssa/func.go15
-rw-r--r--src/cmd/compile/internal/ssa/gen/AMD64.rules749
-rw-r--r--src/cmd/compile/internal/ssa/gen/AMD64Ops.go56
-rw-r--r--src/cmd/compile/internal/ssa/gen/ARMOps.go6
-rw-r--r--src/cmd/compile/internal/ssa/gen/generic.rules71
-rw-r--r--src/cmd/compile/internal/ssa/gen/genericOps.go9
-rw-r--r--src/cmd/compile/internal/ssa/gen/main.go8
-rw-r--r--src/cmd/compile/internal/ssa/gen/rulegen.go127
-rw-r--r--src/cmd/compile/internal/ssa/id.go2
-rw-r--r--src/cmd/compile/internal/ssa/likelyadjust.go142
-rw-r--r--src/cmd/compile/internal/ssa/loopbce.go57
-rw-r--r--src/cmd/compile/internal/ssa/nilcheck.go4
-rw-r--r--src/cmd/compile/internal/ssa/nilcheck_test.go12
-rw-r--r--src/cmd/compile/internal/ssa/op.go5
-rw-r--r--src/cmd/compile/internal/ssa/opGen.go825
-rw-r--r--src/cmd/compile/internal/ssa/phielim.go6
-rw-r--r--src/cmd/compile/internal/ssa/phiopt.go70
-rw-r--r--src/cmd/compile/internal/ssa/prove.go9
-rw-r--r--src/cmd/compile/internal/ssa/regalloc.go843
-rw-r--r--src/cmd/compile/internal/ssa/regalloc_test.go4
-rw-r--r--src/cmd/compile/internal/ssa/rewrite.go70
-rw-r--r--src/cmd/compile/internal/ssa/rewriteAMD64.go4205
-rw-r--r--src/cmd/compile/internal/ssa/rewritegeneric.go1120
-rw-r--r--src/cmd/compile/internal/ssa/sparsemap.go16
-rw-r--r--src/cmd/compile/internal/ssa/sparsetree.go12
-rw-r--r--src/cmd/compile/internal/ssa/stackalloc.go25
-rw-r--r--src/cmd/compile/internal/ssa/type.go67
-rw-r--r--src/cmd/compile/internal/ssa/type_test.go49
-rw-r--r--src/cmd/compile/internal/ssa/value.go1
-rw-r--r--src/cmd/compile/internal/x86/cgen64.go6
-rw-r--r--src/cmd/compile/internal/x86/galign.go7
-rw-r--r--src/cmd/compile/internal/x86/ggen.go2
-rw-r--r--src/cmd/compile/internal/x86/gsubr.go6
-rw-r--r--src/cmd/compile/internal/x86/reg.go2
-rw-r--r--src/cmd/compile/main.go3
-rw-r--r--src/cmd/dist/buildgo.go16
-rw-r--r--src/cmd/dist/buildtool.go3
-rw-r--r--src/cmd/dist/test.go4
-rw-r--r--src/cmd/doc/pkg.go34
-rw-r--r--src/cmd/go/alldocs.go14
-rw-r--r--src/cmd/go/build.go114
-rw-r--r--src/cmd/go/get.go8
-rw-r--r--src/cmd/go/go_test.go125
-rw-r--r--src/cmd/go/http.go2
-rw-r--r--src/cmd/go/list.go1
-rw-r--r--src/cmd/go/pkg.go66
-rw-r--r--src/cmd/go/test.go16
-rw-r--r--src/cmd/go/vcs.go17
-rw-r--r--src/cmd/go/vcs_test.go35
-rw-r--r--src/cmd/go/vendor_test.go26
-rw-r--r--src/cmd/gofmt/simplify.go28
-rw-r--r--src/cmd/gofmt/testdata/slices2.golden63
-rw-r--r--src/cmd/gofmt/testdata/slices2.input63
-rw-r--r--src/cmd/internal/bio/buf.go99
-rw-r--r--src/cmd/internal/bio/must.go43
-rw-r--r--src/cmd/internal/goobj/read.go7
-rw-r--r--src/cmd/internal/obj/arm/asm5.go8
-rw-r--r--src/cmd/internal/obj/arm/obj5.go15
-rw-r--r--src/cmd/internal/obj/arm64/asm7.go8
-rw-r--r--src/cmd/internal/obj/arm64/list7.go2
-rw-r--r--src/cmd/internal/obj/arm64/obj7.go19
-rw-r--r--src/cmd/internal/obj/data.go38
-rw-r--r--src/cmd/internal/obj/link.go78
-rw-r--r--src/cmd/internal/obj/mips/asm0.go8
-rw-r--r--src/cmd/internal/obj/mips/obj0.go22
-rw-r--r--src/cmd/internal/obj/objfile.go204
-rw-r--r--src/cmd/internal/obj/pcln.go23
-rw-r--r--src/cmd/internal/obj/plist.go218
-rw-r--r--src/cmd/internal/obj/ppc64/asm9.go24
-rw-r--r--src/cmd/internal/obj/ppc64/obj9.go26
-rw-r--r--src/cmd/internal/obj/s390x/a.out.go1
-rw-r--r--src/cmd/internal/obj/s390x/anames.go1
-rw-r--r--src/cmd/internal/obj/s390x/asmz.go38
-rw-r--r--src/cmd/internal/obj/s390x/objz.go15
-rw-r--r--src/cmd/internal/obj/sym.go3
-rw-r--r--src/cmd/internal/obj/util.go142
-rw-r--r--src/cmd/internal/obj/x86/a.out.go2
-rw-r--r--src/cmd/internal/obj/x86/anames.go1
-rw-r--r--src/cmd/internal/obj/x86/asm6.go49
-rw-r--r--src/cmd/internal/obj/x86/obj6.go59
-rw-r--r--src/cmd/internal/obj/x86/obj6_test.go1
-rw-r--r--src/cmd/internal/objfile/pe.go2
-rw-r--r--src/cmd/internal/objfile/plan9obj.go2
-rw-r--r--src/cmd/internal/pprof/commands/commands.go (renamed from src/cmd/pprof/internal/commands/commands.go)8
-rw-r--r--src/cmd/internal/pprof/driver/driver.go (renamed from src/cmd/pprof/internal/driver/driver.go)10
-rw-r--r--src/cmd/internal/pprof/driver/interactive.go (renamed from src/cmd/pprof/internal/driver/interactive.go)6
-rw-r--r--src/cmd/internal/pprof/fetch/fetch.go (renamed from src/cmd/pprof/internal/fetch/fetch.go)4
-rw-r--r--src/cmd/internal/pprof/plugin/plugin.go (renamed from src/cmd/pprof/internal/plugin/plugin.go)2
-rw-r--r--src/cmd/internal/pprof/profile/encode.go (renamed from src/cmd/pprof/internal/profile/encode.go)0
-rw-r--r--src/cmd/internal/pprof/profile/filter.go (renamed from src/cmd/pprof/internal/profile/filter.go)0
-rw-r--r--src/cmd/internal/pprof/profile/legacy_profile.go (renamed from src/cmd/pprof/internal/profile/legacy_profile.go)51
-rw-r--r--src/cmd/internal/pprof/profile/profile.go (renamed from src/cmd/pprof/internal/profile/profile.go)0
-rw-r--r--src/cmd/internal/pprof/profile/profile_test.go (renamed from src/cmd/pprof/internal/profile/profile_test.go)0
-rw-r--r--src/cmd/internal/pprof/profile/proto.go (renamed from src/cmd/pprof/internal/profile/proto.go)0
-rw-r--r--src/cmd/internal/pprof/profile/proto_test.go (renamed from src/cmd/pprof/internal/profile/proto_test.go)0
-rw-r--r--src/cmd/internal/pprof/profile/prune.go (renamed from src/cmd/pprof/internal/profile/prune.go)0
-rw-r--r--src/cmd/internal/pprof/report/report.go (renamed from src/cmd/pprof/internal/report/report.go)4
-rw-r--r--src/cmd/internal/pprof/report/source.go (renamed from src/cmd/pprof/internal/report/source.go)4
-rw-r--r--src/cmd/internal/pprof/report/source_html.go (renamed from src/cmd/pprof/internal/report/source_html.go)0
-rw-r--r--src/cmd/internal/pprof/svg/svg.go (renamed from src/cmd/pprof/internal/svg/svg.go)0
-rw-r--r--src/cmd/internal/pprof/svg/svgpan.go (renamed from src/cmd/pprof/internal/svg/svgpan.go)0
-rw-r--r--src/cmd/internal/pprof/symbolizer/symbolizer.go (renamed from src/cmd/pprof/internal/symbolizer/symbolizer.go)4
-rw-r--r--src/cmd/internal/pprof/symbolz/symbolz.go (renamed from src/cmd/pprof/internal/symbolz/symbolz.go)2
-rw-r--r--src/cmd/internal/pprof/tempfile/tempfile.go (renamed from src/cmd/pprof/internal/tempfile/tempfile.go)0
-rw-r--r--src/cmd/internal/sys/arch.go148
-rw-r--r--src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/decode.go8
-rw-r--r--src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/decode.go2
-rw-r--r--src/cmd/link/internal/amd64/asm.go55
-rw-r--r--src/cmd/link/internal/amd64/l.go5
-rw-r--r--src/cmd/link/internal/amd64/obj.go11
-rw-r--r--src/cmd/link/internal/amd64/z.go1
-rw-r--r--src/cmd/link/internal/arm/asm.go77
-rw-r--r--src/cmd/link/internal/arm/l.go2
-rw-r--r--src/cmd/link/internal/arm/obj.go9
-rw-r--r--src/cmd/link/internal/arm64/asm.go48
-rw-r--r--src/cmd/link/internal/arm64/l.go2
-rw-r--r--src/cmd/link/internal/arm64/obj.go9
-rw-r--r--src/cmd/link/internal/ld/ar.go23
-rw-r--r--src/cmd/link/internal/ld/arch.go97
-rw-r--r--src/cmd/link/internal/ld/data.go1032
-rw-r--r--src/cmd/link/internal/ld/deadcode.go63
-rw-r--r--src/cmd/link/internal/ld/decodesym.go126
-rw-r--r--src/cmd/link/internal/ld/dwarf.go1590
-rw-r--r--src/cmd/link/internal/ld/elf.go146
-rw-r--r--src/cmd/link/internal/ld/go.go36
-rw-r--r--src/cmd/link/internal/ld/ldelf.go214
-rw-r--r--src/cmd/link/internal/ld/ldmacho.go85
-rw-r--r--src/cmd/link/internal/ld/ldpe.go46
-rw-r--r--src/cmd/link/internal/ld/lib.go302
-rw-r--r--src/cmd/link/internal/ld/link.go45
-rw-r--r--src/cmd/link/internal/ld/macho.go126
-rw-r--r--src/cmd/link/internal/ld/macho_combine_dwarf.go4
-rw-r--r--src/cmd/link/internal/ld/objfile.go74
-rw-r--r--src/cmd/link/internal/ld/pcln.go146
-rw-r--r--src/cmd/link/internal/ld/pe.go51
-rw-r--r--src/cmd/link/internal/ld/pobj.go26
-rw-r--r--src/cmd/link/internal/ld/sym.go33
-rw-r--r--src/cmd/link/internal/ld/symtab.go121
-rw-r--r--src/cmd/link/internal/mips64/asm.go39
-rw-r--r--src/cmd/link/internal/mips64/l.go2
-rw-r--r--src/cmd/link/internal/mips64/obj.go15
-rw-r--r--src/cmd/link/internal/ppc64/asm.go69
-rw-r--r--src/cmd/link/internal/ppc64/l.go2
-rw-r--r--src/cmd/link/internal/ppc64/obj.go17
-rw-r--r--src/cmd/link/internal/s390x/asm.go27
-rw-r--r--src/cmd/link/internal/s390x/l.go5
-rw-r--r--src/cmd/link/internal/s390x/obj.go9
-rw-r--r--src/cmd/link/internal/x86/asm.go58
-rw-r--r--src/cmd/link/internal/x86/l.go3
-rw-r--r--src/cmd/link/internal/x86/obj.go9
-rw-r--r--src/cmd/link/link_test.go30
-rw-r--r--src/cmd/link/main.go3
-rw-r--r--src/cmd/objdump/objdump_test.go4
-rw-r--r--src/cmd/pprof/pprof.go14
-rw-r--r--src/cmd/trace/main.go7
-rw-r--r--src/cmd/trace/pprof.go78
-rw-r--r--src/cmd/trace/trace.go91
-rw-r--r--src/cmd/vet/asmdecl.go5
-rw-r--r--src/cmd/vet/atomic.go3
-rw-r--r--src/cmd/vet/composite.go6
-rw-r--r--src/cmd/vet/doc.go20
-rw-r--r--src/cmd/vet/internal/whitelist/whitelist.go3
-rw-r--r--src/cmd/vet/main.go3
-rw-r--r--src/cmd/vet/print.go104
-rw-r--r--src/cmd/vet/structtag.go2
-rw-r--r--src/cmd/vet/testdata/asm.go2
-rw-r--r--src/cmd/vet/testdata/asm1.s11
-rw-r--r--src/cmd/vet/testdata/atomic.go9
-rw-r--r--src/cmd/vet/testdata/composite.go8
-rw-r--r--src/cmd/vet/testdata/print.go38
-rw-r--r--src/cmd/vet/types.go66
-rw-r--r--src/cmp.bash61
-rw-r--r--src/compress/bzip2/bzip2.go8
-rw-r--r--src/compress/flate/deflate.go37
-rw-r--r--src/compress/flate/huffman_bit_writer.go174
-rw-r--r--src/compress/flate/huffman_code.go10
-rw-r--r--src/compress/flate/reverse_bits.go2
-rw-r--r--src/compress/flate/testdata/huffman-rand-1k.dyn.expectbin1054 -> 1005 bytes
-rw-r--r--src/compress/flate/writer_test.go12
-rw-r--r--src/compress/gzip/gunzip.go121
-rw-r--r--src/compress/gzip/gunzip_test.go47
-rw-r--r--src/compress/gzip/gzip.go21
-rw-r--r--src/compress/lzw/writer.go2
-rw-r--r--src/container/heap/heap_test.go2
-rw-r--r--src/context/context.go10
-rw-r--r--src/context/context_test.go65
-rw-r--r--src/context/withtimeout_test.go4
-rw-r--r--src/crypto/aes/aes_gcm.go2
-rw-r--r--src/crypto/aes/aes_test.go36
-rw-r--r--src/crypto/aes/asm_s390x.s35
-rw-r--r--src/crypto/aes/cipher.go18
-rw-r--r--src/crypto/aes/cipher_amd64.go83
-rw-r--r--src/crypto/aes/cipher_asm.go48
-rw-r--r--src/crypto/aes/cipher_generic.go27
-rw-r--r--src/crypto/aes/cipher_s390x.go90
-rw-r--r--src/crypto/aes/gcm_amd64.s32
-rw-r--r--src/crypto/cipher/xor.go2
-rw-r--r--src/crypto/des/block.go2
-rw-r--r--src/crypto/ecdsa/ecdsa.go11
-rw-r--r--src/crypto/md5/md5block_decl.go2
-rw-r--r--src/crypto/md5/md5block_generic.go2
-rw-r--r--src/crypto/md5/md5block_ppc64le.s192
-rw-r--r--src/crypto/rsa/rsa.go5
-rw-r--r--src/crypto/sha1/fallback_test.go34
-rw-r--r--src/crypto/sha1/sha1_test.go2
-rw-r--r--src/crypto/sha1/sha1block_decl.go2
-rw-r--r--src/crypto/sha1/sha1block_generic.go2
-rw-r--r--src/crypto/sha1/sha1block_s390x.go12
-rw-r--r--src/crypto/sha1/sha1block_s390x.s34
-rw-r--r--src/crypto/sha256/fallback_test.go35
-rw-r--r--src/crypto/sha256/sha256_test.go13
-rw-r--r--src/crypto/sha256/sha256block.go4
-rw-r--r--src/crypto/sha256/sha256block_decl.go2
-rw-r--r--src/crypto/sha256/sha256block_generic.go9
-rw-r--r--src/crypto/sha256/sha256block_s390x.go12
-rw-r--r--src/crypto/sha256/sha256block_s390x.s34
-rw-r--r--src/crypto/sha512/fallback_test.go37
-rw-r--r--src/crypto/sha512/sha512_test.go13
-rw-r--r--src/crypto/sha512/sha512block.go4
-rw-r--r--src/crypto/sha512/sha512block_decl.go2
-rw-r--r--src/crypto/sha512/sha512block_generic.go9
-rw-r--r--src/crypto/sha512/sha512block_s390x.go12
-rw-r--r--src/crypto/sha512/sha512block_s390x.s34
-rw-r--r--src/crypto/tls/alert.go4
-rw-r--r--src/crypto/tls/common.go4
-rw-r--r--src/crypto/tls/conn.go2
-rw-r--r--src/crypto/tls/handshake_client.go16
-rw-r--r--src/crypto/tls/handshake_messages.go4
-rw-r--r--src/crypto/tls/handshake_server.go12
-rw-r--r--src/crypto/tls/key_agreement.go20
-rw-r--r--src/crypto/tls/prf.go2
-rw-r--r--src/crypto/tls/tls.go26
-rw-r--r--src/crypto/x509/pkcs8.go6
-rw-r--r--src/crypto/x509/sec1.go4
-rw-r--r--src/crypto/x509/x509.go16
-rw-r--r--src/crypto/x509/x509_test.go105
-rw-r--r--src/debug/dwarf/buf.go2
-rw-r--r--src/debug/dwarf/line.go2
-rw-r--r--src/debug/dwarf/typeunit.go4
-rw-r--r--src/debug/elf/elf.go4
-rw-r--r--src/debug/elf/file.go40
-rw-r--r--src/debug/gosym/pclntab.go6
-rw-r--r--src/debug/gosym/symtab.go4
-rw-r--r--src/debug/pe/file.go199
-rw-r--r--src/debug/pe/pe.go24
-rw-r--r--src/debug/pe/section.go111
-rw-r--r--src/debug/pe/string.go63
-rw-r--r--src/debug/pe/symbol.go95
-rw-r--r--src/encoding/asn1/asn1.go2
-rw-r--r--src/encoding/asn1/marshal.go6
-rw-r--r--src/encoding/binary/binary.go2
-rw-r--r--src/encoding/gob/doc.go6
-rw-r--r--src/encoding/gob/encode.go2
-rw-r--r--src/encoding/json/decode.go2
-rw-r--r--src/encoding/json/encode.go155
-rw-r--r--src/encoding/json/encode_test.go111
-rw-r--r--src/encoding/json/stream.go23
-rw-r--r--src/encoding/json/stream_test.go33
-rw-r--r--src/expvar/expvar.go6
-rw-r--r--src/expvar/expvar_test.go10
-rw-r--r--src/flag/flag_test.go2
-rw-r--r--src/fmt/doc.go10
-rw-r--r--src/fmt/fmt_test.go64
-rw-r--r--src/fmt/format.go73
-rw-r--r--src/fmt/print.go19
-rw-r--r--src/go/ast/ast.go2
-rw-r--r--src/go/build/build.go2
-rw-r--r--src/go/build/deps_test.go8
-rw-r--r--src/go/build/read.go3
-rw-r--r--src/go/importer/importer.go2
-rw-r--r--src/go/internal/gcimporter/bimport.go193
-rw-r--r--src/go/scanner/scanner.go6
-rw-r--r--src/go/types/api_test.go42
-rw-r--r--src/go/types/call.go9
-rw-r--r--src/go/types/predicates.go2
-rw-r--r--src/go/types/return.go9
-rw-r--r--src/go/types/stmt.go10
-rw-r--r--src/go/types/testdata/stmt0.src17
-rw-r--r--src/go/types/testdata/stmt1.src76
-rw-r--r--src/hash/adler32/adler32.go13
-rw-r--r--src/hash/crc32/crc32_generic.go2
-rw-r--r--src/hash/crc32/crc32_s390x.go101
-rw-r--r--src/hash/crc32/crc32_s390x.s245
-rw-r--r--src/html/escape.go2
-rw-r--r--src/html/template/css.go2
-rw-r--r--src/html/template/examplefiles_test.go226
-rw-r--r--src/html/template/template.go14
-rw-r--r--src/html/template/url.go2
-rw-r--r--src/image/color/ycbcr.go110
-rw-r--r--src/image/color/ycbcr_test.go44
-rw-r--r--src/image/draw/draw.go8
-rw-r--r--src/image/internal/imageutil/gen.go59
-rw-r--r--src/image/internal/imageutil/impl.go232
-rw-r--r--src/internal/testenv/testenv.go9
-rw-r--r--src/internal/trace/order.go278
-rw-r--r--src/internal/trace/parser.go487
-rw-r--r--src/internal/trace/parser_test.go113
-rw-r--r--src/internal/trace/testdata/http_1_5_goodbin0 -> 42218 bytes
-rw-r--r--src/internal/trace/testdata/stress_1_5_goodbin0 -> 7446 bytes
-rw-r--r--src/internal/trace/testdata/stress_1_5_unorderedbin0 -> 8194 bytes
-rw-r--r--src/internal/trace/testdata/stress_start_stop_1_5_goodbin0 -> 6997 bytes
-rw-r--r--src/io/io.go11
-rw-r--r--src/math/big/arith_s390x.s565
-rw-r--r--src/math/big/float.go4
-rw-r--r--src/math/big/floatmarsh.go89
-rw-r--r--src/math/big/floatmarsh_test.go82
-rw-r--r--src/math/big/gcd_test.go16
-rw-r--r--src/math/big/int.go4
-rw-r--r--src/math/big/nat.go29
-rw-r--r--src/math/big/natconv.go2
-rw-r--r--src/math/big/ratconv.go2
-rw-r--r--src/math/dim_s390x.s132
-rw-r--r--src/math/sqrt_s390x.s12
-rw-r--r--src/math/stubs_s390x.s77
-rw-r--r--src/mime/encodedword.go2
-rwxr-xr-xsrc/naclmake.bash48
-rwxr-xr-xsrc/nacltest.bash38
-rw-r--r--src/net/cgo_unix_test.go7
-rw-r--r--src/net/dial.go297
-rw-r--r--src/net/dial_gen.go40
-rw-r--r--src/net/dial_test.go100
-rw-r--r--src/net/dnsclient_unix.go166
-rw-r--r--src/net/dnsclient_unix_test.go217
-rw-r--r--src/net/dnsconfig_unix.go29
-rw-r--r--src/net/dnsconfig_unix_test.go54
-rw-r--r--src/net/dnsmsg.go20
-rw-r--r--src/net/dnsmsg_test.go118
-rw-r--r--src/net/dnsname_test.go2
-rw-r--r--src/net/error_test.go59
-rw-r--r--src/net/external_test.go11
-rw-r--r--src/net/fd_plan9.go13
-rw-r--r--src/net/fd_unix.go54
-rw-r--r--src/net/fd_windows.go71
-rw-r--r--src/net/hook.go16
-rw-r--r--src/net/http/client.go5
-rw-r--r--src/net/http/client_test.go28
-rw-r--r--src/net/http/clientserver_test.go28
-rw-r--r--src/net/http/cookiejar/punycode.go2
-rw-r--r--src/net/http/fs.go10
-rw-r--r--src/net/http/fs_test.go6
-rw-r--r--src/net/http/http.go25
-rw-r--r--src/net/http/httptest/httptest_test.go4
-rw-r--r--src/net/http/httputil/dump.go1
-rw-r--r--src/net/http/httputil/dump_test.go40
-rw-r--r--src/net/http/httputil/example_test.go2
-rw-r--r--src/net/http/httputil/persist.go10
-rw-r--r--src/net/http/main_test.go23
-rw-r--r--src/net/http/pprof/pprof.go9
-rw-r--r--src/net/http/request.go14
-rw-r--r--src/net/http/request_test.go2
-rw-r--r--src/net/http/response.go2
-rw-r--r--src/net/http/serve_test.go86
-rw-r--r--src/net/http/server.go73
-rw-r--r--src/net/http/transfer.go4
-rw-r--r--src/net/http/transport.go80
-rw-r--r--src/net/http/transport_test.go51
-rw-r--r--src/net/interface.go81
-rw-r--r--src/net/interface_bsd.go5
-rw-r--r--src/net/interface_linux.go3
-rw-r--r--src/net/interface_test.go21
-rw-r--r--src/net/interface_windows.go3
-rw-r--r--src/net/ip.go38
-rw-r--r--src/net/ip_test.go134
-rw-r--r--src/net/iprawsock.go13
-rw-r--r--src/net/iprawsock_plan9.go6
-rw-r--r--src/net/iprawsock_posix.go14
-rw-r--r--src/net/ipsock.go29
-rw-r--r--src/net/ipsock_plan9.go48
-rw-r--r--src/net/ipsock_posix.go41
-rw-r--r--src/net/listen_test.go19
-rw-r--r--src/net/lookup.go64
-rw-r--r--src/net/lookup_plan9.go55
-rw-r--r--src/net/lookup_stub.go25
-rw-r--r--src/net/lookup_test.go62
-rw-r--r--src/net/lookup_unix.go48
-rw-r--r--src/net/lookup_windows.go205
-rw-r--r--src/net/lookup_windows_test.go17
-rw-r--r--src/net/mail/message.go170
-rw-r--r--src/net/mail/message_test.go56
-rw-r--r--src/net/main_test.go2
-rw-r--r--src/net/mockserver_test.go10
-rw-r--r--src/net/net.go17
-rw-r--r--src/net/netgo_unix_test.go7
-rw-r--r--src/net/parse.go4
-rw-r--r--src/net/platform_test.go3
-rw-r--r--src/net/port.go62
-rw-r--r--src/net/port_test.go52
-rw-r--r--src/net/sendfile_dragonfly.go2
-rw-r--r--src/net/sendfile_freebsd.go2
-rw-r--r--src/net/sendfile_solaris.go11
-rw-r--r--src/net/sendfile_test.go90
-rw-r--r--src/net/sock_posix.go10
-rw-r--r--src/net/tcpsock.go7
-rw-r--r--src/net/tcpsock_plan9.go19
-rw-r--r--src/net/tcpsock_posix.go19
-rw-r--r--src/net/tcpsock_test.go10
-rw-r--r--src/net/testdata/Mark.Twain-Tom.Sawyer.txt8465
-rw-r--r--src/net/timeout_test.go7
-rw-r--r--src/net/udpsock.go13
-rw-r--r--src/net/udpsock_plan9.go15
-rw-r--r--src/net/udpsock_posix.go14
-rw-r--r--src/net/udpsock_test.go47
-rw-r--r--src/net/unixsock.go7
-rw-r--r--src/net/unixsock_plan9.go8
-rw-r--r--src/net/unixsock_posix.go18
-rw-r--r--src/net/unixsock_test.go4
-rw-r--r--src/net/url/url.go6
-rw-r--r--src/net/url/url_test.go6
-rw-r--r--src/os/exec/exec_test.go2
-rw-r--r--src/os/exec_windows.go2
-rw-r--r--src/os/file_windows.go14
-rw-r--r--src/os/stat_darwin.go2
-rw-r--r--src/os/stat_dragonfly.go4
-rw-r--r--src/os/stat_freebsd.go2
-rw-r--r--src/os/stat_linux.go2
-rw-r--r--src/os/stat_nacl.go2
-rw-r--r--src/os/stat_netbsd.go4
-rw-r--r--src/os/stat_openbsd.go4
-rw-r--r--src/os/stat_plan9.go2
-rw-r--r--src/os/stat_solaris.go4
-rw-r--r--src/os/stat_windows.go2
-rw-r--r--src/os/types_windows.go2
-rw-r--r--src/os/user/lookup_unix.go7
-rw-r--r--src/path/filepath/path_test.go2
-rw-r--r--src/reflect/all_test.go86
-rw-r--r--src/reflect/asm_s390x.s30
-rw-r--r--src/reflect/export_test.go22
-rw-r--r--src/reflect/type.go572
-rw-r--r--src/reflect/value.go49
-rw-r--r--src/regexp/exec_test.go12
-rw-r--r--src/regexp/onepass.go2
-rw-r--r--src/runtime/alg.go8
-rw-r--r--src/runtime/append_test.go137
-rw-r--r--src/runtime/asm_amd64.s157
-rw-r--r--src/runtime/asm_s390x.s1130
-rw-r--r--src/runtime/atomic_pointer.go14
-rw-r--r--src/runtime/cgo/asm_s390x.s44
-rw-r--r--src/runtime/cgo/gcc_libinit.c1
-rw-r--r--src/runtime/cgo/gcc_libinit_linux_ppc64x.c26
-rw-r--r--src/runtime/cgo/gcc_linux_s390x.c68
-rw-r--r--src/runtime/cgo/gcc_s390x.S43
-rw-r--r--src/runtime/cgo/signal_darwin_armx.go6
-rw-r--r--src/runtime/cgocall.go6
-rw-r--r--src/runtime/chan.go14
-rw-r--r--src/runtime/crash_test.go46
-rw-r--r--src/runtime/defs_linux_s390x.go167
-rw-r--r--src/runtime/error.go15
-rw-r--r--src/runtime/export_windows_test.go7
-rw-r--r--src/runtime/extern.go6
-rw-r--r--src/runtime/gcinfo_test.go4
-rw-r--r--src/runtime/hash64.go2
-rw-r--r--src/runtime/hashmap.go77
-rw-r--r--src/runtime/hashmap_fast.go33
-rw-r--r--src/runtime/heapdump.go9
-rw-r--r--src/runtime/iface.go89
-rw-r--r--src/runtime/internal/atomic/asm_386.s2
-rw-r--r--src/runtime/internal/atomic/asm_amd64.s2
-rw-r--r--src/runtime/internal/atomic/asm_amd64p32.s2
-rw-r--r--src/runtime/internal/atomic/asm_mips64x.s2
-rw-r--r--src/runtime/internal/atomic/asm_ppc64x.s2
-rw-r--r--src/runtime/internal/atomic/asm_s390x.s174
-rw-r--r--src/runtime/internal/atomic/atomic_386.go2
-rw-r--r--src/runtime/internal/atomic/atomic_amd64x.go5
-rw-r--r--src/runtime/internal/atomic/atomic_arm.go2
-rw-r--r--src/runtime/internal/atomic/atomic_arm64.go2
-rw-r--r--src/runtime/internal/atomic/atomic_arm64.s2
-rw-r--r--src/runtime/internal/atomic/atomic_mips64x.go2
-rw-r--r--src/runtime/internal/atomic/atomic_ppc64x.go2
-rw-r--r--src/runtime/internal/atomic/atomic_s390x.go73
-rw-r--r--src/runtime/internal/sys/arch.go17
-rw-r--r--src/runtime/internal/sys/arch_386.go2
-rw-r--r--src/runtime/internal/sys/arch_amd64.go2
-rw-r--r--src/runtime/internal/sys/arch_amd64p32.go2
-rw-r--r--src/runtime/internal/sys/arch_arm.go2
-rw-r--r--src/runtime/internal/sys/arch_arm64.go2
-rw-r--r--src/runtime/internal/sys/arch_mips64.go2
-rw-r--r--src/runtime/internal/sys/arch_mips64le.go2
-rw-r--r--src/runtime/internal/sys/arch_ppc64.go2
-rw-r--r--src/runtime/internal/sys/arch_ppc64le.go2
-rw-r--r--src/runtime/internal/sys/arch_s390x.go2
-rw-r--r--src/runtime/internal/sys/gengoos.go4
-rw-r--r--src/runtime/internal/sys/intrinsics.go135
-rw-r--r--src/runtime/internal/sys/intrinsics_test.go54
-rw-r--r--src/runtime/internal/sys/zgoarch_386.go2
-rw-r--r--src/runtime/internal/sys/zgoarch_amd64.go2
-rw-r--r--src/runtime/internal/sys/zgoarch_amd64p32.go2
-rw-r--r--src/runtime/internal/sys/zgoarch_arm.go2
-rw-r--r--src/runtime/internal/sys/zgoarch_arm64.go2
-rw-r--r--src/runtime/internal/sys/zgoarch_mips64.go2
-rw-r--r--src/runtime/internal/sys/zgoarch_mips64le.go2
-rw-r--r--src/runtime/internal/sys/zgoarch_ppc64.go2
-rw-r--r--src/runtime/internal/sys/zgoarch_ppc64le.go2
-rw-r--r--src/runtime/internal/sys/zgoarch_s390x.go2
-rw-r--r--src/runtime/internal/sys/zgoos_android.go2
-rw-r--r--src/runtime/internal/sys/zgoos_darwin.go2
-rw-r--r--src/runtime/internal/sys/zgoos_dragonfly.go2
-rw-r--r--src/runtime/internal/sys/zgoos_freebsd.go2
-rw-r--r--src/runtime/internal/sys/zgoos_linux.go2
-rw-r--r--src/runtime/internal/sys/zgoos_nacl.go2
-rw-r--r--src/runtime/internal/sys/zgoos_netbsd.go2
-rw-r--r--src/runtime/internal/sys/zgoos_openbsd.go2
-rw-r--r--src/runtime/internal/sys/zgoos_plan9.go2
-rw-r--r--src/runtime/internal/sys/zgoos_solaris.go2
-rw-r--r--src/runtime/internal/sys/zgoos_windows.go2
-rw-r--r--src/runtime/lfstack.go7
-rw-r--r--src/runtime/lfstack_32bit.go6
-rw-r--r--src/runtime/lfstack_64bit.go48
-rw-r--r--src/runtime/lfstack_amd64.go24
-rw-r--r--src/runtime/lfstack_darwin_arm64.go25
-rw-r--r--src/runtime/lfstack_linux_arm64.go25
-rw-r--r--src/runtime/lfstack_linux_mips64x.go32
-rw-r--r--src/runtime/lfstack_linux_ppc64x.go32
-rw-r--r--src/runtime/lock_futex.go6
-rw-r--r--src/runtime/malloc.go72
-rw-r--r--src/runtime/map_test.go16
-rw-r--r--src/runtime/mbarrier.go19
-rw-r--r--src/runtime/mbitmap.go63
-rw-r--r--src/runtime/mem_linux.go7
-rw-r--r--src/runtime/memclr_s390x.s122
-rw-r--r--src/runtime/memmove_ppc64x.s109
-rw-r--r--src/runtime/memmove_s390x.s189
-rw-r--r--src/runtime/mfinal.go12
-rw-r--r--src/runtime/mgc.go84
-rw-r--r--src/runtime/mgcmark.go264
-rw-r--r--src/runtime/mheap.go34
-rw-r--r--src/runtime/mmap.go3
-rw-r--r--src/runtime/mprof.go2
-rw-r--r--src/runtime/mstkbar.go32
-rw-r--r--src/runtime/noasm.go2
-rw-r--r--src/runtime/os1_darwin.go538
-rw-r--r--src/runtime/os1_dragonfly.go270
-rw-r--r--src/runtime/os1_linux.go393
-rw-r--r--src/runtime/os1_linux_generic.go27
-rw-r--r--src/runtime/os1_linux_mips64x.go26
-rw-r--r--src/runtime/os1_netbsd.go275
-rw-r--r--src/runtime/os1_plan9.go4
-rw-r--r--src/runtime/os1_windows.go703
-rw-r--r--src/runtime/os2_darwin.go14
-rw-r--r--src/runtime/os2_dragonfly.go15
-rw-r--r--src/runtime/os2_linux_mips64x.go25
-rw-r--r--src/runtime/os2_netbsd.go18
-rw-r--r--src/runtime/os2_windows.go19
-rw-r--r--src/runtime/os_darwin.go542
-rw-r--r--src/runtime/os_dragonfly.go273
-rw-r--r--src/runtime/os_linux.go435
-rw-r--r--src/runtime/os_linux_386.go33
-rw-r--r--src/runtime/os_linux_arm.go47
-rw-r--r--src/runtime/os_linux_arm64.go16
-rw-r--r--src/runtime/os_linux_generic.go (renamed from src/runtime/os2_linux_generic.go)19
-rw-r--r--src/runtime/os_linux_mips64x.go37
-rw-r--r--src/runtime/os_linux_noauxv.go10
-rw-r--r--src/runtime/os_linux_s390x.go46
-rw-r--r--src/runtime/os_netbsd.go283
-rw-r--r--src/runtime/os_netbsd_386.go (renamed from src/runtime/os1_netbsd_386.go)0
-rw-r--r--src/runtime/os_netbsd_amd64.go (renamed from src/runtime/os1_netbsd_amd64.go)0
-rw-r--r--src/runtime/os_windows.go720
-rw-r--r--src/runtime/panic.go2
-rw-r--r--src/runtime/pprof/mprof_test.go2
-rw-r--r--src/runtime/pprof/pprof_test.go18
-rw-r--r--src/runtime/proc.go111
-rw-r--r--src/runtime/race/testdata/io_test.go37
-rw-r--r--src/runtime/rdebug.go7
-rw-r--r--src/runtime/rt0_linux_s390x.s20
-rw-r--r--src/runtime/runtime-gdb_test.go124
-rw-r--r--src/runtime/runtime1.go47
-rw-r--r--src/runtime/runtime2.go144
-rw-r--r--src/runtime/select.go4
-rw-r--r--src/runtime/signal_dragonfly.go14
-rw-r--r--src/runtime/signal_freebsd.go14
-rw-r--r--src/runtime/signal_linux_s390x.go208
-rw-r--r--src/runtime/signal_openbsd.go14
-rw-r--r--src/runtime/sigtab_linux_generic.go2
-rw-r--r--src/runtime/sigtab_linux_mips64x.go2
-rw-r--r--src/runtime/slice.go63
-rw-r--r--src/runtime/softfloat_arm.go11
-rw-r--r--src/runtime/stack.go9
-rw-r--r--src/runtime/string.go12
-rw-r--r--src/runtime/string_test.go26
-rw-r--r--src/runtime/symtab.go5
-rw-r--r--src/runtime/sys_linux_s390x.s440
-rw-r--r--src/runtime/sys_s390x.go45
-rw-r--r--src/runtime/syscall_windows_test.go66
-rw-r--r--src/runtime/tls_s390x.s51
-rw-r--r--src/runtime/trace.go217
-rw-r--r--src/runtime/trace/trace_stack_test.go12
-rw-r--r--src/runtime/trace/trace_test.go52
-rw-r--r--src/runtime/type.go428
-rw-r--r--src/runtime/unaligned1.go2
-rw-r--r--src/runtime/vdso_linux_amd64.go50
-rw-r--r--src/runtime/vdso_none.go4
-rw-r--r--src/runtime/vlop_arm_test.go44
-rw-r--r--src/runtime/vlrt.go1
-rw-r--r--src/strconv/atof.go4
-rw-r--r--src/strconv/atof_test.go24
-rw-r--r--src/strconv/extfloat.go4
-rw-r--r--src/strings/reader.go5
-rw-r--r--src/strings/reader_test.go41
-rw-r--r--src/strings/strings.go27
-rw-r--r--src/strings/strings_test.go37
-rw-r--r--src/sync/atomic/asm_s390x.s143
-rw-r--r--src/sync/pool.go4
-rw-r--r--src/syscall/asm_linux_s390x.s156
-rw-r--r--src/syscall/mksyscall_windows.go4
-rw-r--r--src/syscall/sockcmsg_unix.go2
-rw-r--r--src/syscall/types_linux.go3
-rw-r--r--src/syscall/ztypes_linux_ppc64.go7
-rw-r--r--src/syscall/ztypes_linux_ppc64le.go7
-rw-r--r--src/testing/benchmark.go45
-rw-r--r--src/testing/match.go71
-rw-r--r--src/testing/match_test.go118
-rw-r--r--src/testing/sub_test.go180
-rw-r--r--src/testing/testing.go16
-rw-r--r--src/text/template/exec.go12
-rw-r--r--src/text/template/exec_test.go17
-rw-r--r--src/text/template/helper.go14
-rw-r--r--src/time/time.go10
-rw-r--r--src/time/time_test.go2
-rw-r--r--src/time/zoneinfo_windows.go2
-rw-r--r--src/unicode/letter.go6
-rw-r--r--src/unicode/maketables.go20
-rw-r--r--src/unicode/tables.go131
722 files changed, 40790 insertions, 16744 deletions
diff --git a/src/archive/tar/reader.go b/src/archive/tar/reader.go
index c8cb69a178..e2a2a5440e 100644
--- a/src/archive/tar/reader.go
+++ b/src/archive/tar/reader.go
@@ -13,7 +13,6 @@ import (
"io"
"io/ioutil"
"math"
- "os"
"strconv"
"strings"
"time"
@@ -307,7 +306,7 @@ func mergePAX(hdr *Header, headers map[string]string) error {
if err != nil {
return err
}
- hdr.Size = int64(size)
+ hdr.Size = size
default:
if strings.HasPrefix(k, paxXattr) {
if hdr.Xattrs == nil {
@@ -337,17 +336,17 @@ func parsePAXTime(t string) (time.Time, error) {
if err != nil {
return time.Time{}, err
}
- nano_buf := string(buf[pos+1:])
+ nanoBuf := string(buf[pos+1:])
// Pad as needed before converting to a decimal.
// For example .030 -> .030000000 -> 30000000 nanoseconds
- if len(nano_buf) < maxNanoSecondIntSize {
+ if len(nanoBuf) < maxNanoSecondIntSize {
// Right pad
- nano_buf += strings.Repeat("0", maxNanoSecondIntSize-len(nano_buf))
- } else if len(nano_buf) > maxNanoSecondIntSize {
+ nanoBuf += strings.Repeat("0", maxNanoSecondIntSize-len(nanoBuf))
+ } else if len(nanoBuf) > maxNanoSecondIntSize {
// Right truncate
- nano_buf = nano_buf[:maxNanoSecondIntSize]
+ nanoBuf = nanoBuf[:maxNanoSecondIntSize]
}
- nanoseconds, err = strconv.ParseInt(string(nano_buf), 10, 0)
+ nanoseconds, err = strconv.ParseInt(nanoBuf, 10, 0)
if err != nil {
return time.Time{}, err
}
@@ -379,14 +378,14 @@ func parsePAX(r io.Reader) (map[string]string, error) {
}
sbuf = residual
- keyStr := string(key)
+ keyStr := key
if keyStr == paxGNUSparseOffset || keyStr == paxGNUSparseNumBytes {
// GNU sparse format 0.0 special key. Write to sparseMap instead of using the headers map.
sparseMap.WriteString(value)
sparseMap.Write([]byte{','})
} else {
// Normal key. Set the value in the headers map.
- headers[keyStr] = string(value)
+ headers[keyStr] = value
}
}
if sparseMap.Len() != 0 {
@@ -523,10 +522,10 @@ func (tr *Reader) skipUnread() error {
// io.Seeker, but calling Seek always returns an error and performs
// no action. Thus, we try an innocent seek to the current position
// to see if Seek is really supported.
- pos1, err := sr.Seek(0, os.SEEK_CUR)
+ pos1, err := sr.Seek(0, io.SeekCurrent)
if err == nil {
// Seek seems supported, so perform the real Seek.
- pos2, err := sr.Seek(dataSkip-1, os.SEEK_CUR)
+ pos2, err := sr.Seek(dataSkip-1, io.SeekCurrent)
if err != nil {
tr.err = err
return tr.err
diff --git a/src/archive/tar/writer.go b/src/archive/tar/writer.go
index 600ee4be09..944b2d4952 100644
--- a/src/archive/tar/writer.go
+++ b/src/archive/tar/writer.go
@@ -278,7 +278,7 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
return err
}
}
- tw.nb = int64(hdr.Size)
+ tw.nb = hdr.Size
tw.pad = (blockSize - (tw.nb % blockSize)) % blockSize
_, tw.err = tw.w.Write(header)
diff --git a/src/archive/zip/reader.go b/src/archive/zip/reader.go
index d741d105dc..f6c3ead3be 100644
--- a/src/archive/zip/reader.go
+++ b/src/archive/zip/reader.go
@@ -87,7 +87,7 @@ func (z *Reader) init(r io.ReaderAt, size int64) error {
z.File = make([]*File, 0, end.directoryRecords)
z.Comment = end.comment
rs := io.NewSectionReader(r, 0, size)
- if _, err = rs.Seek(int64(end.directoryOffset), os.SEEK_SET); err != nil {
+ if _, err = rs.Seek(int64(end.directoryOffset), io.SeekStart); err != nil {
return err
}
buf := bufio.NewReader(rs)
diff --git a/src/bufio/bufio.go b/src/bufio/bufio.go
index d2ccc74f52..3b30b8b80c 100644
--- a/src/bufio/bufio.go
+++ b/src/bufio/bufio.go
@@ -266,7 +266,7 @@ func (b *Reader) ReadRune() (r rune, size int, err error) {
return 0, 0, b.readErr()
}
r, size = rune(b.buf[b.r]), 1
- if r >= 0x80 {
+ if r >= utf8.RuneSelf {
r, size = utf8.DecodeRune(b.buf[b.r:b.w])
}
b.r += size
diff --git a/src/bytes/bytes.go b/src/bytes/bytes.go
index 8a4409cb6b..305c85d9f4 100644
--- a/src/bytes/bytes.go
+++ b/src/bytes/bytes.go
@@ -83,6 +83,16 @@ func Contains(b, subslice []byte) bool {
return Index(b, subslice) != -1
}
+// ContainsAny reports whether any of the UTF-8-encoded Unicode code points in chars are within b.
+func ContainsAny(b []byte, chars string) bool {
+ return IndexAny(b, chars) >= 0
+}
+
+// ContainsRune reports whether the Unicode code point r is within b.
+func ContainsRune(b []byte, r rune) bool {
+ return IndexRune(b, r) >= 0
+}
+
// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s.
func Index(s, sep []byte) int {
n := len(sep)
diff --git a/src/bytes/bytes_test.go b/src/bytes/bytes_test.go
index 1be29d6cc6..620cfd1bce 100644
--- a/src/bytes/bytes_test.go
+++ b/src/bytes/bytes_test.go
@@ -1218,6 +1218,57 @@ func TestContains(t *testing.T) {
}
}
+var ContainsAnyTests = []struct {
+ b []byte
+ substr string
+ expected bool
+}{
+ {[]byte(""), "", false},
+ {[]byte(""), "a", false},
+ {[]byte(""), "abc", false},
+ {[]byte("a"), "", false},
+ {[]byte("a"), "a", true},
+ {[]byte("aaa"), "a", true},
+ {[]byte("abc"), "xyz", false},
+ {[]byte("abc"), "xcz", true},
+ {[]byte("a☺b☻c☹d"), "uvw☻xyz", true},
+ {[]byte("aRegExp*"), ".(|)*+?^$[]", true},
+ {[]byte(dots + dots + dots), " ", false},
+}
+
+func TestContainsAny(t *testing.T) {
+ for _, ct := range ContainsAnyTests {
+ if ContainsAny(ct.b, ct.substr) != ct.expected {
+ t.Errorf("ContainsAny(%s, %s) = %v, want %v",
+ ct.b, ct.substr, !ct.expected, ct.expected)
+ }
+ }
+}
+
+var ContainsRuneTests = []struct {
+ b []byte
+ r rune
+ expected bool
+}{
+ {[]byte(""), 'a', false},
+ {[]byte("a"), 'a', true},
+ {[]byte("aaa"), 'a', true},
+ {[]byte("abc"), 'y', false},
+ {[]byte("abc"), 'c', true},
+ {[]byte("a☺b☻c☹d"), 'x', false},
+ {[]byte("a☺b☻c☹d"), '☻', true},
+ {[]byte("aRegExp*"), '*', true},
+}
+
+func TestContainsRune(t *testing.T) {
+ for _, ct := range ContainsRuneTests {
+ if ContainsRune(ct.b, ct.r) != ct.expected {
+ t.Errorf("ContainsRune(%q, %q) = %v, want %v",
+ ct.b, ct.r, !ct.expected, ct.expected)
+ }
+ }
+}
+
var makeFieldsInput = func() []byte {
x := make([]byte, 1<<20)
// Input is ~10% space, ~10% 2-byte UTF-8, rest ASCII non-space.
diff --git a/src/bytes/reader.go b/src/bytes/reader.go
index 5941ebdab4..aa39890f3b 100644
--- a/src/bytes/reader.go
+++ b/src/bytes/reader.go
@@ -114,7 +114,7 @@ func (r *Reader) Seek(offset int64, whence int) (int64, error) {
case 0:
abs = offset
case 1:
- abs = int64(r.i) + offset
+ abs = r.i + offset
case 2:
abs = int64(len(r.s)) + offset
default:
@@ -146,5 +146,8 @@ func (r *Reader) WriteTo(w io.Writer) (n int64, err error) {
return
}
+// Reset resets the Reader to be reading from b.
+func (r *Reader) Reset(b []byte) { *r = Reader{b, 0, -1} }
+
// NewReader returns a new Reader reading from b.
func NewReader(b []byte) *Reader { return &Reader{b, 0, -1} }
diff --git a/src/bytes/reader_test.go b/src/bytes/reader_test.go
index b929a28260..9341cd5b45 100644
--- a/src/bytes/reader_test.go
+++ b/src/bytes/reader_test.go
@@ -9,7 +9,6 @@ import (
"fmt"
"io"
"io/ioutil"
- "os"
"sync"
"testing"
)
@@ -24,15 +23,15 @@ func TestReader(t *testing.T) {
wantpos int64
seekerr string
}{
- {seek: os.SEEK_SET, off: 0, n: 20, want: "0123456789"},
- {seek: os.SEEK_SET, off: 1, n: 1, want: "1"},
- {seek: os.SEEK_CUR, off: 1, wantpos: 3, n: 2, want: "34"},
- {seek: os.SEEK_SET, off: -1, seekerr: "bytes.Reader.Seek: negative position"},
- {seek: os.SEEK_SET, off: 1 << 33, wantpos: 1 << 33},
- {seek: os.SEEK_CUR, off: 1, wantpos: 1<<33 + 1},
- {seek: os.SEEK_SET, n: 5, want: "01234"},
- {seek: os.SEEK_CUR, n: 5, want: "56789"},
- {seek: os.SEEK_END, off: -1, n: 1, wantpos: 9, want: "9"},
+ {seek: io.SeekStart, off: 0, n: 20, want: "0123456789"},
+ {seek: io.SeekStart, off: 1, n: 1, want: "1"},
+ {seek: io.SeekCurrent, off: 1, wantpos: 3, n: 2, want: "34"},
+ {seek: io.SeekStart, off: -1, seekerr: "bytes.Reader.Seek: negative position"},
+ {seek: io.SeekStart, off: 1 << 33, wantpos: 1 << 33},
+ {seek: io.SeekCurrent, off: 1, wantpos: 1<<33 + 1},
+ {seek: io.SeekStart, n: 5, want: "01234"},
+ {seek: io.SeekCurrent, n: 5, want: "56789"},
+ {seek: io.SeekEnd, off: -1, n: 1, wantpos: 9, want: "9"},
}
for i, tt := range tests {
@@ -63,7 +62,7 @@ func TestReader(t *testing.T) {
func TestReadAfterBigSeek(t *testing.T) {
r := NewReader([]byte("0123456789"))
- if _, err := r.Seek(1<<31+5, os.SEEK_SET); err != nil {
+ if _, err := r.Seek(1<<31+5, io.SeekStart); err != nil {
t.Fatal(err)
}
if n, err := r.Read(make([]byte, 10)); n != 0 || err != io.EOF {
@@ -256,3 +255,23 @@ func TestReaderLenSize(t *testing.T) {
t.Errorf("Size = %d; want 3", r.Size())
}
}
+
+func TestReaderReset(t *testing.T) {
+ r := NewReader([]byte("世界"))
+ if _, _, err := r.ReadRune(); err != nil {
+ t.Errorf("ReadRune: unexpected error: %v", err)
+ }
+
+ const want = "abcdef"
+ r.Reset([]byte(want))
+ if err := r.UnreadRune(); err == nil {
+ t.Errorf("UnreadRune: expected error, got nil")
+ }
+ buf, err := ioutil.ReadAll(r)
+ if err != nil {
+ t.Errorf("ReadAll: unexpected error: %v", err)
+ }
+ if got := string(buf); got != want {
+ t.Errorf("ReadAll: got %q, want %q", got, want)
+ }
+}
diff --git a/src/cmd/api/goapi.go b/src/cmd/api/goapi.go
index 982c40b085..e9c91477c6 100644
--- a/src/cmd/api/goapi.go
+++ b/src/cmd/api/goapi.go
@@ -143,6 +143,11 @@ func main() {
w := NewWalker(context, filepath.Join(build.Default.GOROOT, "src"))
for _, name := range pkgNames {
+ // Vendored packages do not contribute to our
+ // public API surface.
+ if strings.HasPrefix(name, "vendor/") {
+ continue
+ }
// - Package "unsafe" contains special signatures requiring
// extra care when printing them - ignore since it is not
// going to change w/o a language change.
diff --git a/src/cmd/asm/internal/asm/asm.go b/src/cmd/asm/internal/asm/asm.go
index 950fd735c9..24906e2cce 100644
--- a/src/cmd/asm/internal/asm/asm.go
+++ b/src/cmd/asm/internal/asm/asm.go
@@ -13,6 +13,7 @@ import (
"cmd/asm/internal/flags"
"cmd/asm/internal/lex"
"cmd/internal/obj"
+ "cmd/internal/sys"
)
// TODO: configure the architecture
@@ -23,14 +24,14 @@ var testOut *bytes.Buffer // Gathers output when testing.
// If doLabel is set, it also defines the labels collect for this Prog.
func (p *Parser) append(prog *obj.Prog, cond string, doLabel bool) {
if cond != "" {
- switch p.arch.Thechar {
- case '5':
+ switch p.arch.Family {
+ case sys.ARM:
if !arch.ARMConditionCodes(prog, cond) {
p.errorf("unrecognized condition code .%q", cond)
return
}
- case '7':
+ case sys.ARM64:
if !arch.ARM64Suffix(prog, cond) {
p.errorf("unrecognized suffix .%q", cond)
return
@@ -58,7 +59,7 @@ func (p *Parser) append(prog *obj.Prog, cond string, doLabel bool) {
}
p.pendingLabels = p.pendingLabels[0:0]
}
- prog.Pc = int64(p.pc)
+ prog.Pc = p.pc
if *flags.Debug {
fmt.Println(p.histLineNum, prog)
}
@@ -361,7 +362,7 @@ func (p *Parser) asmJump(op obj.As, cond string, a []obj.Addr) {
target = &a[1]
prog.From = a[0]
case 3:
- if p.arch.Thechar == '9' {
+ if p.arch.Family == sys.PPC64 {
// Special 3-operand jumps.
// First two must be constants; a[1] is a register number.
target = &a[2]
@@ -370,7 +371,7 @@ func (p *Parser) asmJump(op obj.As, cond string, a []obj.Addr) {
Offset: p.getConstant(prog, op, &a[0]),
}
reg := int16(p.getConstant(prog, op, &a[1]))
- reg, ok := p.arch.RegisterNumber("R", int16(reg))
+ reg, ok := p.arch.RegisterNumber("R", reg)
if !ok {
p.errorf("bad register number %d", reg)
return
@@ -378,7 +379,7 @@ func (p *Parser) asmJump(op obj.As, cond string, a []obj.Addr) {
prog.Reg = reg
break
}
- if p.arch.Thechar == '0' {
+ if p.arch.Family == sys.MIPS64 {
// 3-operand jumps.
// First two must be registers
target = &a[2]
@@ -386,7 +387,7 @@ func (p *Parser) asmJump(op obj.As, cond string, a []obj.Addr) {
prog.Reg = p.getRegister(prog, op, &a[1])
break
}
- if p.arch.Thechar == 'z' {
+ if p.arch.Family == sys.S390X {
// 3-operand jumps.
target = &a[2]
prog.From = a[0]
@@ -438,7 +439,7 @@ func (p *Parser) asmJump(op obj.As, cond string, a []obj.Addr) {
// JMP 4(R0)
prog.To = *target
// On the ppc64, 9a encodes BR (CTR) as BR CTR. We do the same.
- if p.arch.Thechar == '9' && target.Offset == 0 {
+ if p.arch.Family == sys.PPC64 && target.Offset == 0 {
prog.To.Type = obj.TYPE_REG
}
case target.Type == obj.TYPE_CONST:
@@ -492,14 +493,14 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
prog.From = a[0]
// prog.To is no address.
}
- if p.arch.Thechar == '9' && arch.IsPPC64NEG(op) {
+ if p.arch.Family == sys.PPC64 && arch.IsPPC64NEG(op) {
// NEG: From and To are both a[0].
prog.To = a[0]
prog.From = a[0]
break
}
case 2:
- if p.arch.Thechar == '5' {
+ if p.arch.Family == sys.ARM {
if arch.IsARMCMP(op) {
prog.From = a[0]
prog.Reg = p.getRegister(prog, op, &a[1])
@@ -532,11 +533,11 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
prog.Reg = p.getRegister(prog, op, &a[1])
break
}
- } else if p.arch.Thechar == '7' && arch.IsARM64CMP(op) {
+ } else if p.arch.Family == sys.ARM64 && arch.IsARM64CMP(op) {
prog.From = a[0]
prog.Reg = p.getRegister(prog, op, &a[1])
break
- } else if p.arch.Thechar == '0' {
+ } else if p.arch.Family == sys.MIPS64 {
if arch.IsMIPS64CMP(op) || arch.IsMIPS64MUL(op) {
prog.From = a[0]
prog.Reg = p.getRegister(prog, op, &a[1])
@@ -546,12 +547,12 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
prog.From = a[0]
prog.To = a[1]
case 3:
- switch p.arch.Thechar {
- case '0':
+ switch p.arch.Family {
+ case sys.MIPS64:
prog.From = a[0]
prog.Reg = p.getRegister(prog, op, &a[1])
prog.To = a[2]
- case '5':
+ case sys.ARM:
// Special cases.
if arch.IsARMSTREX(op) {
/*
@@ -567,7 +568,7 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
prog.From = a[0]
prog.Reg = p.getRegister(prog, op, &a[1])
prog.To = a[2]
- case '7':
+ case sys.ARM64:
// ARM64 instructions with one input and two outputs.
if arch.IsARM64STLXR(op) {
prog.From = a[0]
@@ -582,11 +583,11 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
prog.From = a[0]
prog.Reg = p.getRegister(prog, op, &a[1])
prog.To = a[2]
- case '6', '8':
+ case sys.AMD64, sys.I386:
prog.From = a[0]
prog.From3 = newAddr(a[1])
prog.To = a[2]
- case '9':
+ case sys.PPC64:
if arch.IsPPC64CMP(op) {
// CMPW etc.; third argument is a CR register that goes into prog.Reg.
prog.From = a[0]
@@ -612,7 +613,7 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
p.errorf("invalid addressing modes for %s instruction", obj.Aconv(op))
return
}
- case 'z':
+ case sys.S390X:
if arch.IsS390xWithLength(op) || arch.IsS390xWithIndex(op) {
prog.From = a[1]
prog.From3 = newAddr(a[0])
@@ -626,7 +627,7 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
return
}
case 4:
- if p.arch.Thechar == '5' && arch.IsARMMULA(op) {
+ if p.arch.Family == sys.ARM && arch.IsARMMULA(op) {
// All must be registers.
p.getRegister(prog, op, &a[0])
r1 := p.getRegister(prog, op, &a[1])
@@ -639,14 +640,14 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
prog.Reg = r1
break
}
- if p.arch.Thechar == '7' {
+ if p.arch.Family == sys.ARM64 {
prog.From = a[0]
prog.Reg = p.getRegister(prog, op, &a[1])
prog.From3 = newAddr(a[2])
prog.To = a[3]
break
}
- if p.arch.Thechar == '9' && arch.IsPPC64RLD(op) {
+ if p.arch.Family == sys.PPC64 && arch.IsPPC64RLD(op) {
// 2nd operand must always be a register.
// TODO: Do we need to guard this with the instruction type?
// That is, are there 4-operand instructions without this property?
@@ -656,7 +657,7 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
prog.To = a[3]
break
}
- if p.arch.Thechar == 'z' {
+ if p.arch.Family == sys.S390X {
prog.From = a[1]
prog.Reg = p.getRegister(prog, op, &a[2])
prog.From3 = newAddr(a[0])
@@ -666,7 +667,7 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
p.errorf("can't handle %s instruction with 4 operands", obj.Aconv(op))
return
case 5:
- if p.arch.Thechar == '9' && arch.IsPPC64RLD(op) {
+ if p.arch.Family == sys.PPC64 && arch.IsPPC64RLD(op) {
// Always reg, reg, con, con, reg. (con, con is a 'mask').
prog.From = a[0]
prog.Reg = p.getRegister(prog, op, &a[1])
@@ -688,7 +689,7 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
p.errorf("can't handle %s instruction with 5 operands", obj.Aconv(op))
return
case 6:
- if p.arch.Thechar == '5' && arch.IsARMMRC(op) {
+ if p.arch.Family == sys.ARM && arch.IsARMMRC(op) {
// Strange special case: MCR, MRC.
prog.To.Type = obj.TYPE_CONST
x0 := p.getConstant(prog, op, &a[0])
diff --git a/src/cmd/asm/internal/asm/endtoend_test.go b/src/cmd/asm/internal/asm/endtoend_test.go
index 1307c4243f..bc992a7c99 100644
--- a/src/cmd/asm/internal/asm/endtoend_test.go
+++ b/src/cmd/asm/internal/asm/endtoend_test.go
@@ -5,6 +5,7 @@
package asm
import (
+ "bufio"
"bytes"
"fmt"
"io/ioutil"
@@ -33,7 +34,7 @@ func testEndToEnd(t *testing.T, goarch, file string) {
pList := obj.Linknewplist(ctxt)
var ok bool
testOut = new(bytes.Buffer) // The assembler writes test output to this buffer.
- ctxt.Bso = obj.Binitw(os.Stdout)
+ ctxt.Bso = bufio.NewWriter(os.Stdout)
defer ctxt.Bso.Flush()
failed := false
ctxt.DiagFunc = func(format string, args ...interface{}) {
@@ -271,7 +272,7 @@ func testErrors(t *testing.T, goarch, file string) {
pList := obj.Linknewplist(ctxt)
var ok bool
testOut = new(bytes.Buffer) // The assembler writes test output to this buffer.
- ctxt.Bso = obj.Binitw(os.Stdout)
+ ctxt.Bso = bufio.NewWriter(os.Stdout)
defer ctxt.Bso.Flush()
failed := false
var errBuf bytes.Buffer
diff --git a/src/cmd/asm/internal/asm/parse.go b/src/cmd/asm/internal/asm/parse.go
index ee37439962..40206e6dc1 100644
--- a/src/cmd/asm/internal/asm/parse.go
+++ b/src/cmd/asm/internal/asm/parse.go
@@ -19,6 +19,7 @@ import (
"cmd/asm/internal/flags"
"cmd/asm/internal/lex"
"cmd/internal/obj"
+ "cmd/internal/sys"
)
type Parser struct {
@@ -130,7 +131,7 @@ func (p *Parser) line() bool {
for {
tok = p.lex.Next()
if len(operands) == 0 && len(items) == 0 {
- if (p.arch.Thechar == '5' || p.arch.Thechar == '7') && tok == '.' {
+ if p.arch.InFamily(sys.ARM, sys.ARM64) && tok == '.' {
// ARM conditionals.
tok = p.lex.Next()
str := p.lex.Text()
@@ -420,7 +421,7 @@ func (p *Parser) atStartOfRegister(name string) bool {
// We have consumed the register or R prefix.
func (p *Parser) atRegisterShift() bool {
// ARM only.
- if p.arch.Thechar != '5' {
+ if p.arch.Family != sys.ARM {
return false
}
// R1<<...
@@ -476,15 +477,14 @@ func (p *Parser) register(name string, prefix rune) (r1, r2 int16, scale int8, o
if c == ':' || c == ',' || c == '+' {
// 2nd register; syntax (R1+R2) etc. No two architectures agree.
// Check the architectures match the syntax.
- char := p.arch.Thechar
switch p.next().ScanToken {
case ',':
- if char != '5' && char != '7' {
+ if !p.arch.InFamily(sys.ARM, sys.ARM64) {
p.errorf("(register,register) not supported on this architecture")
return
}
case '+':
- if char != '9' {
+ if p.arch.Family != sys.PPC64 {
p.errorf("(register+register) not supported on this architecture")
return
}
@@ -649,7 +649,7 @@ func (p *Parser) registerIndirect(a *obj.Addr, prefix rune) {
a.Reg = r1
if r2 != 0 {
// TODO: Consistency in the encoding would be nice here.
- if p.arch.Thechar == '5' || p.arch.Thechar == '7' {
+ if p.arch.InFamily(sys.ARM, sys.ARM64) {
// Special form
// ARM: destination register pair (R1, R2).
// ARM64: register pair (R1, R2) for LDP/STP.
@@ -662,7 +662,7 @@ func (p *Parser) registerIndirect(a *obj.Addr, prefix rune) {
// Nothing may follow
return
}
- if p.arch.Thechar == '9' {
+ if p.arch.Family == sys.PPC64 {
// Special form for PPC64: (R1+R2); alias for (R1)(R2*1).
if prefix != 0 || scale != 0 {
p.errorf("illegal address mode for register+register")
@@ -752,7 +752,7 @@ ListLoop:
// register number is ARM-specific. It returns the number of the specified register.
func (p *Parser) registerNumber(name string) uint16 {
- if p.arch.Thechar == '5' && name == "g" {
+ if p.arch.Family == sys.ARM && name == "g" {
return 10
}
if name[0] != 'R' {
diff --git a/src/cmd/asm/internal/asm/testdata/s390x.s b/src/cmd/asm/internal/asm/testdata/s390x.s
index 148cd2eaae..f1dc9aff2d 100644
--- a/src/cmd/asm/internal/asm/testdata/s390x.s
+++ b/src/cmd/asm/internal/asm/testdata/s390x.s
@@ -61,6 +61,10 @@ TEXT main·foo(SB),7,$16-0 // TEXT main.foo(SB), 7, $16-0
MULLW R6, R7, R8 // b9040087b91c0086
MULLW $8192, R6 // c26000002000
MULLW $8192, R6, R7 // b9040076c27000002000
+ MULHD R9, R8 // b90400b8b98600a9ebb9003f000ab98000b8b90900abebb8003f000ab98000b9b9e9b08a
+ MULHD R7, R2, R1 // b90400b2b98600a7ebb7003f000ab98000b2b90900abebb2003f000ab98000b7b9e9b01a
+ MULHDU R3, R4 // b90400b4b98600a3b904004a
+ MULHDU R5, R6, R7 // b90400b6b98600a5b904007a
DIVD R1, R2 // b90400b2b90d00a1b904002b
DIVD R1, R2, R3 // b90400b2b90d00a1b904003b
DIVW R4, R5 // b90400b5b91d00a4b904005b
diff --git a/src/cmd/asm/main.go b/src/cmd/asm/main.go
index 4e450bec98..c612583e6b 100644
--- a/src/cmd/asm/main.go
+++ b/src/cmd/asm/main.go
@@ -5,6 +5,7 @@
package main
import (
+ "bufio"
"flag"
"fmt"
"log"
@@ -15,6 +16,7 @@ import (
"cmd/asm/internal/flags"
"cmd/asm/internal/lex"
+ "cmd/internal/bio"
"cmd/internal/obj"
)
@@ -31,25 +33,26 @@ func main() {
flags.Parse()
- // Create object file, write header.
- fd, err := os.Create(*flags.OutputFile)
- if err != nil {
- log.Fatal(err)
- }
ctxt := obj.Linknew(architecture.LinkArch)
if *flags.PrintOut {
ctxt.Debugasm = 1
}
ctxt.LineHist.TrimPathPrefix = *flags.TrimPath
ctxt.Flag_dynlink = *flags.Dynlink
- if *flags.Shared || *flags.Dynlink {
- ctxt.Flag_shared = 1
- }
- ctxt.Bso = obj.Binitw(os.Stdout)
+ ctxt.Flag_shared = *flags.Shared || *flags.Dynlink
+ ctxt.Bso = bufio.NewWriter(os.Stdout)
defer ctxt.Bso.Flush()
- output := obj.Binitw(fd)
- fmt.Fprintf(output, "go object %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion())
- fmt.Fprintf(output, "!\n")
+
+ // Create object file, write header.
+ out, err := os.Create(*flags.OutputFile)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer bio.MustClose(out)
+ buf := bufio.NewWriter(bio.MustWriter(out))
+
+ fmt.Fprintf(buf, "go object %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion())
+ fmt.Fprintf(buf, "!\n")
lexer := lex.NewLexer(flag.Arg(0), ctxt)
parser := asm.NewParser(ctxt, architecture, lexer)
@@ -63,12 +66,12 @@ func main() {
pList.Firstpc, ok = parser.Parse()
if ok {
// reports errors to parser.Errorf
- obj.Writeobjdirect(ctxt, output)
+ obj.Writeobjdirect(ctxt, buf)
}
if !ok || diag {
log.Printf("assembly of %s failed", flag.Arg(0))
os.Remove(*flags.OutputFile)
os.Exit(1)
}
- output.Flush()
+ buf.Flush()
}
diff --git a/src/cmd/cgo/ast.go b/src/cmd/cgo/ast.go
index 2859d59750..823da43c1d 100644
--- a/src/cmd/cgo/ast.go
+++ b/src/cmd/cgo/ast.go
@@ -73,7 +73,7 @@ func (f *File) ReadGo(name string) {
}
for _, spec := range d.Specs {
s, ok := spec.(*ast.ImportSpec)
- if !ok || string(s.Path.Value) != `"C"` {
+ if !ok || s.Path.Value != `"C"` {
continue
}
sawC = true
@@ -106,7 +106,7 @@ func (f *File) ReadGo(name string) {
ws := 0
for _, spec := range d.Specs {
s, ok := spec.(*ast.ImportSpec)
- if !ok || string(s.Path.Value) != `"C"` {
+ if !ok || s.Path.Value != `"C"` {
d.Specs[ws] = spec
ws++
}
@@ -147,7 +147,7 @@ func commentText(g *ast.CommentGroup) string {
}
var pieces []string
for _, com := range g.List {
- c := string(com.Text)
+ c := com.Text
// Remove comment markers.
// The parser has given us exactly the comment text.
switch c[1] {
@@ -242,11 +242,11 @@ func (f *File) saveExport(x interface{}, context string) {
return
}
for _, c := range n.Doc.List {
- if !strings.HasPrefix(string(c.Text), "//export ") {
+ if !strings.HasPrefix(c.Text, "//export ") {
continue
}
- name := strings.TrimSpace(string(c.Text[9:]))
+ name := strings.TrimSpace(c.Text[9:])
if name == "" {
error_(c.Pos(), "export missing name")
}
diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go
index 84cd2e816a..3ee4461352 100644
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -1009,7 +1009,7 @@ func (p *Package) rewriteRef(f *File) {
if r.Name.Kind == "var" {
expr = &ast.StarExpr{Star: (*r.Expr).Pos(), X: expr}
} else {
- error_(r.Pos(), "only C variables allowed in selector expression", fixGo(r.Name.Go))
+ error_(r.Pos(), "only C variables allowed in selector expression %s", fixGo(r.Name.Go))
}
case "type":
diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go
index 5e863549d6..2dc36c20db 100644
--- a/src/cmd/cgo/main.go
+++ b/src/cmd/cgo/main.go
@@ -156,7 +156,7 @@ var intSizeMap = map[string]int64{
"ppc64": 8,
"ppc64le": 8,
"s390": 4,
- "s390x": 4,
+ "s390x": 8,
}
var cPrefix string
diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go
index 621c41c6b2..88b0147364 100644
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -1451,7 +1451,7 @@ const char *_cgoPREFIX_Cfunc_CString(struct __go_string s) {
void *_cgoPREFIX_Cfunc_CBytes(struct __go_open_array b) {
char *p = malloc(b.__count);
- memmove(p, b.__data, b.__count);
+ memmove(p, b.__values, b.__count);
return p;
}
diff --git a/src/cmd/compile/internal/amd64/galign.go b/src/cmd/compile/internal/amd64/galign.go
index 14721ea35b..461ef2ada1 100644
--- a/src/cmd/compile/internal/amd64/galign.go
+++ b/src/cmd/compile/internal/amd64/galign.go
@@ -18,12 +18,7 @@ var (
)
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
@@ -42,12 +37,9 @@ func Main() {
resvd = append(resvd, x86.REG_BP)
}
- gc.Thearch.Thechar = '6'
- gc.Thearch.Thestring = "amd64"
- gc.Thearch.Thelinkarch = &x86.Linkamd64
+ gc.Thearch.LinkArch = &x86.Linkamd64
if obj.Getgoarch() == "amd64p32" {
- gc.Thearch.Thestring = "amd64p32"
- gc.Thearch.Thelinkarch = &x86.Linkamd64p32
+ gc.Thearch.LinkArch = &x86.Linkamd64p32
}
gc.Thearch.REGSP = x86.REGSP
gc.Thearch.REGCTXT = x86.REGCTXT
diff --git a/src/cmd/compile/internal/amd64/gsubr.go b/src/cmd/compile/internal/amd64/gsubr.go
index 456fa7cbae..e3535f3244 100644
--- a/src/cmd/compile/internal/amd64/gsubr.go
+++ b/src/cmd/compile/internal/amd64/gsubr.go
@@ -722,7 +722,7 @@ func optoas(op gc.Op, t *gc.Type) obj.As {
a := obj.AXXX
switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) {
default:
- gc.Fatalf("optoas: no entry %v-%v", gc.Oconv(op, 0), t)
+ gc.Fatalf("optoas: no entry %v-%v", op, t)
case OADDR_ | gc.TPTR32:
a = x86.ALEAL
diff --git a/src/cmd/compile/internal/amd64/ssa.go b/src/cmd/compile/internal/amd64/ssa.go
index 3f8e0ece12..6557287caa 100644
--- a/src/cmd/compile/internal/amd64/ssa.go
+++ b/src/cmd/compile/internal/amd64/ssa.go
@@ -62,7 +62,7 @@ func ssaMarkMoves(s *gc.SSAGenState, b *ssa.Block) {
}
for i := len(b.Values) - 1; i >= 0; i-- {
v := b.Values[i]
- if flive && (v.Op == ssa.OpAMD64MOVBconst || v.Op == ssa.OpAMD64MOVWconst || v.Op == ssa.OpAMD64MOVLconst || v.Op == ssa.OpAMD64MOVQconst) {
+ if flive && (v.Op == ssa.OpAMD64MOVLconst || v.Op == ssa.OpAMD64MOVQconst) {
// The "mark" is any non-nil Aux value.
v.Aux = v
}
@@ -160,7 +160,7 @@ func opregreg(op obj.As, dest, src int16) *obj.Prog {
func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
s.SetLineno(v.Line)
switch v.Op {
- case ssa.OpAMD64ADDQ, ssa.OpAMD64ADDL, ssa.OpAMD64ADDW, ssa.OpAMD64ADDB:
+ case ssa.OpAMD64ADDQ, ssa.OpAMD64ADDL:
r := gc.SSARegNum(v)
r1 := gc.SSARegNum(v.Args[0])
r2 := gc.SSARegNum(v.Args[1])
@@ -192,74 +192,23 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Type = obj.TYPE_REG
p.To.Reg = r
}
- // 2-address opcode arithmetic, symmetric
- case ssa.OpAMD64ADDSS, ssa.OpAMD64ADDSD,
- ssa.OpAMD64ANDQ, ssa.OpAMD64ANDL, ssa.OpAMD64ANDW, ssa.OpAMD64ANDB,
- ssa.OpAMD64ORQ, ssa.OpAMD64ORL, ssa.OpAMD64ORW, ssa.OpAMD64ORB,
- ssa.OpAMD64XORQ, ssa.OpAMD64XORL, ssa.OpAMD64XORW, ssa.OpAMD64XORB,
- ssa.OpAMD64MULQ, ssa.OpAMD64MULL, ssa.OpAMD64MULW, ssa.OpAMD64MULB,
- ssa.OpAMD64MULSS, ssa.OpAMD64MULSD, ssa.OpAMD64PXOR:
- r := gc.SSARegNum(v)
- x := gc.SSARegNum(v.Args[0])
- y := gc.SSARegNum(v.Args[1])
- if x != r && y != r {
- opregreg(moveByType(v.Type), r, x)
- x = r
- }
- p := gc.Prog(v.Op.Asm())
- p.From.Type = obj.TYPE_REG
- p.To.Type = obj.TYPE_REG
- p.To.Reg = r
- if x == r {
- p.From.Reg = y
- } else {
- p.From.Reg = x
- }
- // 2-address opcode arithmetic, not symmetric
- case ssa.OpAMD64SUBQ, ssa.OpAMD64SUBL, ssa.OpAMD64SUBW, ssa.OpAMD64SUBB:
- r := gc.SSARegNum(v)
- x := gc.SSARegNum(v.Args[0])
- y := gc.SSARegNum(v.Args[1])
- var neg bool
- if y == r {
- // compute -(y-x) instead
- x, y = y, x
- neg = true
- }
- if x != r {
- opregreg(moveByType(v.Type), r, x)
- }
- opregreg(v.Op.Asm(), r, y)
-
- if neg {
- if v.Op == ssa.OpAMD64SUBQ {
- p := gc.Prog(x86.ANEGQ)
- p.To.Type = obj.TYPE_REG
- p.To.Reg = r
- } else { // Avoids partial registers write
- p := gc.Prog(x86.ANEGL)
- p.To.Type = obj.TYPE_REG
- p.To.Reg = r
- }
- }
- case ssa.OpAMD64SUBSS, ssa.OpAMD64SUBSD, ssa.OpAMD64DIVSS, ssa.OpAMD64DIVSD:
+ // 2-address opcode arithmetic
+ case ssa.OpAMD64SUBQ, ssa.OpAMD64SUBL,
+ ssa.OpAMD64MULQ, ssa.OpAMD64MULL,
+ ssa.OpAMD64ANDQ, ssa.OpAMD64ANDL,
+ ssa.OpAMD64ORQ, ssa.OpAMD64ORL,
+ ssa.OpAMD64XORQ, ssa.OpAMD64XORL,
+ ssa.OpAMD64SHLQ, ssa.OpAMD64SHLL,
+ ssa.OpAMD64SHRQ, ssa.OpAMD64SHRL, ssa.OpAMD64SHRW, ssa.OpAMD64SHRB,
+ ssa.OpAMD64SARQ, ssa.OpAMD64SARL, ssa.OpAMD64SARW, ssa.OpAMD64SARB,
+ ssa.OpAMD64ADDSS, ssa.OpAMD64ADDSD, ssa.OpAMD64SUBSS, ssa.OpAMD64SUBSD,
+ ssa.OpAMD64MULSS, ssa.OpAMD64MULSD, ssa.OpAMD64DIVSS, ssa.OpAMD64DIVSD,
+ ssa.OpAMD64PXOR:
r := gc.SSARegNum(v)
- x := gc.SSARegNum(v.Args[0])
- y := gc.SSARegNum(v.Args[1])
- if y == r && x != r {
- // r/y := x op r/y, need to preserve x and rewrite to
- // r/y := r/y op x15
- x15 := int16(x86.REG_X15)
- // register move y to x15
- // register move x to y
- // rename y with x15
- opregreg(moveByType(v.Type), x15, y)
- opregreg(moveByType(v.Type), r, x)
- y = x15
- } else if x != r {
- opregreg(moveByType(v.Type), r, x)
+ if r != gc.SSARegNum(v.Args[0]) {
+ v.Fatalf("input[0] and output not in same register %s", v.LongString())
}
- opregreg(v.Op.Asm(), r, y)
+ opregreg(v.Op.Asm(), r, gc.SSARegNum(v.Args[1]))
case ssa.OpAMD64DIVQ, ssa.OpAMD64DIVL, ssa.OpAMD64DIVW,
ssa.OpAMD64DIVQU, ssa.OpAMD64DIVLU, ssa.OpAMD64DIVWU,
@@ -372,48 +321,21 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
// Do a 64-bit add, the overflow goes into the carry.
// Shift right once and pull the carry back into the 63rd bit.
r := gc.SSARegNum(v)
- x := gc.SSARegNum(v.Args[0])
- y := gc.SSARegNum(v.Args[1])
- if x != r && y != r {
- opregreg(moveByType(v.Type), r, x)
- x = r
+ if r != gc.SSARegNum(v.Args[0]) {
+ v.Fatalf("input[0] and output not in same register %s", v.LongString())
}
p := gc.Prog(x86.AADDQ)
p.From.Type = obj.TYPE_REG
p.To.Type = obj.TYPE_REG
p.To.Reg = r
- if x == r {
- p.From.Reg = y
- } else {
- p.From.Reg = x
- }
+ p.From.Reg = gc.SSARegNum(v.Args[1])
p = gc.Prog(x86.ARCRQ)
p.From.Type = obj.TYPE_CONST
p.From.Offset = 1
p.To.Type = obj.TYPE_REG
p.To.Reg = r
- case ssa.OpAMD64SHLQ, ssa.OpAMD64SHLL, ssa.OpAMD64SHLW, ssa.OpAMD64SHLB,
- ssa.OpAMD64SHRQ, ssa.OpAMD64SHRL, ssa.OpAMD64SHRW, ssa.OpAMD64SHRB,
- ssa.OpAMD64SARQ, ssa.OpAMD64SARL, ssa.OpAMD64SARW, ssa.OpAMD64SARB:
- x := gc.SSARegNum(v.Args[0])
- r := gc.SSARegNum(v)
- if x != r {
- if r == x86.REG_CX {
- v.Fatalf("can't implement %s, target and shift both in CX", v.LongString())
- }
- p := gc.Prog(moveByType(v.Type))
- p.From.Type = obj.TYPE_REG
- p.From.Reg = x
- p.To.Type = obj.TYPE_REG
- p.To.Reg = r
- }
- p := gc.Prog(v.Op.Asm())
- p.From.Type = obj.TYPE_REG
- p.From.Reg = gc.SSARegNum(v.Args[1]) // should be CX
- p.To.Type = obj.TYPE_REG
- p.To.Reg = r
- case ssa.OpAMD64ADDQconst, ssa.OpAMD64ADDLconst, ssa.OpAMD64ADDWconst, ssa.OpAMD64ADDBconst:
+ case ssa.OpAMD64ADDQconst, ssa.OpAMD64ADDLconst:
r := gc.SSARegNum(v)
a := gc.SSARegNum(v.Args[0])
if r == a {
@@ -433,7 +355,8 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Type = obj.TYPE_REG
p.To.Reg = r
return
- } else if v.AuxInt == -1 {
+ }
+ if v.AuxInt == -1 {
var asm obj.As
if v.Op == ssa.OpAMD64ADDQconst {
asm = x86.ADECQ
@@ -444,14 +367,13 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Type = obj.TYPE_REG
p.To.Reg = r
return
- } else {
- p := gc.Prog(v.Op.Asm())
- p.From.Type = obj.TYPE_CONST
- p.From.Offset = v.AuxInt
- p.To.Type = obj.TYPE_REG
- p.To.Reg = r
- return
}
+ p := gc.Prog(v.Op.Asm())
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = v.AuxInt
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = r
+ return
}
var asm obj.As
if v.Op == ssa.OpAMD64ADDQconst {
@@ -469,17 +391,11 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
case ssa.OpAMD64CMOVQEQconst, ssa.OpAMD64CMOVLEQconst, ssa.OpAMD64CMOVWEQconst,
ssa.OpAMD64CMOVQNEconst, ssa.OpAMD64CMOVLNEconst, ssa.OpAMD64CMOVWNEconst:
r := gc.SSARegNum(v)
- x := gc.SSARegNum(v.Args[0])
- // Arg0 is in/out, move in to out if not already same
- if r != x {
- p := gc.Prog(moveByType(v.Type))
- p.From.Type = obj.TYPE_REG
- p.From.Reg = x
- p.To.Type = obj.TYPE_REG
- p.To.Reg = r
+ if r != gc.SSARegNum(v.Args[0]) {
+ v.Fatalf("input[0] and output not in same register %s", v.LongString())
}
- // Constant into AX, after arg0 movement in case arg0 is in AX
+ // Constant into AX
p := gc.Prog(moveByType(v.Type))
p.From.Type = obj.TYPE_CONST
p.From.Offset = v.AuxInt
@@ -492,15 +408,10 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Type = obj.TYPE_REG
p.To.Reg = r
- case ssa.OpAMD64MULQconst, ssa.OpAMD64MULLconst, ssa.OpAMD64MULWconst, ssa.OpAMD64MULBconst:
+ case ssa.OpAMD64MULQconst, ssa.OpAMD64MULLconst:
r := gc.SSARegNum(v)
- x := gc.SSARegNum(v.Args[0])
- if r != x {
- p := gc.Prog(moveByType(v.Type))
- p.From.Type = obj.TYPE_REG
- p.From.Reg = x
- p.To.Type = obj.TYPE_REG
- p.To.Reg = r
+ if r != gc.SSARegNum(v.Args[0]) {
+ v.Fatalf("input[0] and output not in same register %s", v.LongString())
}
p := gc.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_CONST
@@ -508,87 +419,22 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Type = obj.TYPE_REG
p.To.Reg = r
// TODO: Teach doasm to compile the three-address multiply imul $c, r1, r2
- // instead of using the MOVQ above.
+ // then we don't need to use resultInArg0 for these ops.
//p.From3 = new(obj.Addr)
//p.From3.Type = obj.TYPE_REG
//p.From3.Reg = gc.SSARegNum(v.Args[0])
- case ssa.OpAMD64SUBQconst, ssa.OpAMD64SUBLconst, ssa.OpAMD64SUBWconst, ssa.OpAMD64SUBBconst:
- x := gc.SSARegNum(v.Args[0])
- r := gc.SSARegNum(v)
- // We have 3-op add (lea), so transforming a = b - const into
- // a = b + (- const), saves us 1 instruction. We can't fit
- // - (-1 << 31) into 4 bytes offset in lea.
- // We handle 2-address just fine below.
- if v.AuxInt == -1<<31 || x == r {
- if x != r {
- // This code compensates for the fact that the register allocator
- // doesn't understand 2-address instructions yet. TODO: fix that.
- p := gc.Prog(moveByType(v.Type))
- p.From.Type = obj.TYPE_REG
- p.From.Reg = x
- p.To.Type = obj.TYPE_REG
- p.To.Reg = r
- }
- p := gc.Prog(v.Op.Asm())
- p.From.Type = obj.TYPE_CONST
- p.From.Offset = v.AuxInt
- p.To.Type = obj.TYPE_REG
- p.To.Reg = r
- } else if x == r && v.AuxInt == -1 {
- var asm obj.As
- // x = x - (-1) is the same as x++
- // See OpAMD64ADDQconst comments about inc vs add $1,reg
- if v.Op == ssa.OpAMD64SUBQconst {
- asm = x86.AINCQ
- } else {
- asm = x86.AINCL
- }
- p := gc.Prog(asm)
- p.To.Type = obj.TYPE_REG
- p.To.Reg = r
- } else if x == r && v.AuxInt == 1 {
- var asm obj.As
- if v.Op == ssa.OpAMD64SUBQconst {
- asm = x86.ADECQ
- } else {
- asm = x86.ADECL
- }
- p := gc.Prog(asm)
- p.To.Type = obj.TYPE_REG
- p.To.Reg = r
- } else {
- var asm obj.As
- if v.Op == ssa.OpAMD64SUBQconst {
- asm = x86.ALEAQ
- } else {
- asm = x86.ALEAL
- }
- p := gc.Prog(asm)
- p.From.Type = obj.TYPE_MEM
- p.From.Reg = x
- p.From.Offset = -v.AuxInt
- p.To.Type = obj.TYPE_REG
- p.To.Reg = r
- }
- case ssa.OpAMD64ANDQconst, ssa.OpAMD64ANDLconst, ssa.OpAMD64ANDWconst, ssa.OpAMD64ANDBconst,
- ssa.OpAMD64ORQconst, ssa.OpAMD64ORLconst, ssa.OpAMD64ORWconst, ssa.OpAMD64ORBconst,
- ssa.OpAMD64XORQconst, ssa.OpAMD64XORLconst, ssa.OpAMD64XORWconst, ssa.OpAMD64XORBconst,
- ssa.OpAMD64SHLQconst, ssa.OpAMD64SHLLconst, ssa.OpAMD64SHLWconst,
- ssa.OpAMD64SHLBconst, ssa.OpAMD64SHRQconst, ssa.OpAMD64SHRLconst, ssa.OpAMD64SHRWconst,
- ssa.OpAMD64SHRBconst, ssa.OpAMD64SARQconst, ssa.OpAMD64SARLconst, ssa.OpAMD64SARWconst,
- ssa.OpAMD64SARBconst, ssa.OpAMD64ROLQconst, ssa.OpAMD64ROLLconst, ssa.OpAMD64ROLWconst,
- ssa.OpAMD64ROLBconst:
- // This code compensates for the fact that the register allocator
- // doesn't understand 2-address instructions yet. TODO: fix that.
- x := gc.SSARegNum(v.Args[0])
+ case ssa.OpAMD64SUBQconst, ssa.OpAMD64SUBLconst,
+ ssa.OpAMD64ANDQconst, ssa.OpAMD64ANDLconst,
+ ssa.OpAMD64ORQconst, ssa.OpAMD64ORLconst,
+ ssa.OpAMD64XORQconst, ssa.OpAMD64XORLconst,
+ ssa.OpAMD64SHLQconst, ssa.OpAMD64SHLLconst,
+ ssa.OpAMD64SHRQconst, ssa.OpAMD64SHRLconst, ssa.OpAMD64SHRWconst, ssa.OpAMD64SHRBconst,
+ ssa.OpAMD64SARQconst, ssa.OpAMD64SARLconst, ssa.OpAMD64SARWconst, ssa.OpAMD64SARBconst,
+ ssa.OpAMD64ROLQconst, ssa.OpAMD64ROLLconst, ssa.OpAMD64ROLWconst, ssa.OpAMD64ROLBconst:
r := gc.SSARegNum(v)
- if x != r {
- p := gc.Prog(moveByType(v.Type))
- p.From.Type = obj.TYPE_REG
- p.From.Reg = x
- p.To.Type = obj.TYPE_REG
- p.To.Reg = r
+ if r != gc.SSARegNum(v.Args[0]) {
+ v.Fatalf("input[0] and output not in same register %s", v.LongString())
}
p := gc.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_CONST
@@ -651,7 +497,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Offset = v.AuxInt
p.To.Type = obj.TYPE_REG
p.To.Reg = gc.SSARegNum(v.Args[0])
- case ssa.OpAMD64MOVBconst, ssa.OpAMD64MOVWconst, ssa.OpAMD64MOVLconst, ssa.OpAMD64MOVQconst:
+ case ssa.OpAMD64MOVLconst, ssa.OpAMD64MOVQconst:
x := gc.SSARegNum(v)
p := gc.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_CONST
@@ -966,17 +812,12 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
if gc.Maxarg < v.AuxInt {
gc.Maxarg = v.AuxInt
}
- case ssa.OpAMD64NEGQ, ssa.OpAMD64NEGL, ssa.OpAMD64NEGW, ssa.OpAMD64NEGB,
+ case ssa.OpAMD64NEGQ, ssa.OpAMD64NEGL,
ssa.OpAMD64BSWAPQ, ssa.OpAMD64BSWAPL,
- ssa.OpAMD64NOTQ, ssa.OpAMD64NOTL, ssa.OpAMD64NOTW, ssa.OpAMD64NOTB:
- x := gc.SSARegNum(v.Args[0])
+ ssa.OpAMD64NOTQ, ssa.OpAMD64NOTL:
r := gc.SSARegNum(v)
- if x != r {
- p := gc.Prog(moveByType(v.Type))
- p.From.Type = obj.TYPE_REG
- p.From.Reg = x
- p.To.Type = obj.TYPE_REG
- p.To.Reg = r
+ if r != gc.SSARegNum(v.Args[0]) {
+ v.Fatalf("input[0] and output not in same register %s", v.LongString())
}
p := gc.Prog(v.Op.Asm())
p.To.Type = obj.TYPE_REG
diff --git a/src/cmd/compile/internal/arm/cgen64.go b/src/cmd/compile/internal/arm/cgen64.go
index 337bf03179..33e840615c 100644
--- a/src/cmd/compile/internal/arm/cgen64.go
+++ b/src/cmd/compile/internal/arm/cgen64.go
@@ -19,7 +19,7 @@ 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.Fatalf("cgen64 %v of %v", gc.Oconv(n.Op, 0), gc.Oconv(res.Op, 0))
+ gc.Fatalf("cgen64 %v of %v", n.Op, res.Op)
}
l := n.Left
@@ -35,7 +35,7 @@ func cgen64(n *gc.Node, res *gc.Node) {
split64(l, &lo1, &hi1)
switch n.Op {
default:
- gc.Fatalf("cgen64 %v", gc.Oconv(n.Op, 0))
+ gc.Fatalf("cgen64 %v", n.Op)
case gc.OMINUS:
var lo2 gc.Node
@@ -793,7 +793,7 @@ func cmp64(nl *gc.Node, nr *gc.Node, op gc.Op, likely int, to *obj.Prog) {
var br *obj.Prog
switch op {
default:
- gc.Fatalf("cmp64 %v %v", gc.Oconv(op, 0), t)
+ gc.Fatalf("cmp64 %v %v", op, t)
// cmp hi
// bne L
diff --git a/src/cmd/compile/internal/arm/galign.go b/src/cmd/compile/internal/arm/galign.go
index e05f4d06bb..afd86e44c8 100644
--- a/src/cmd/compile/internal/arm/galign.go
+++ b/src/cmd/compile/internal/arm/galign.go
@@ -11,15 +11,10 @@ import (
)
func betypeinit() {
- gc.Widthptr = 4
- gc.Widthint = 4
- gc.Widthreg = 4
}
func Main() {
- gc.Thearch.Thechar = '5'
- gc.Thearch.Thestring = "arm"
- gc.Thearch.Thelinkarch = &arm.Linkarm
+ gc.Thearch.LinkArch = &arm.Linkarm
gc.Thearch.REGSP = arm.REGSP
gc.Thearch.REGCTXT = arm.REGCTXT
gc.Thearch.REGCALLX = arm.REG_R1
diff --git a/src/cmd/compile/internal/arm/gsubr.go b/src/cmd/compile/internal/arm/gsubr.go
index 26da2e2081..73905f18ce 100644
--- a/src/cmd/compile/internal/arm/gsubr.go
+++ b/src/cmd/compile/internal/arm/gsubr.go
@@ -719,7 +719,7 @@ func raddr(n *gc.Node, p *obj.Prog) {
gc.Naddr(&a, n)
if a.Type != obj.TYPE_REG {
if n != nil {
- gc.Fatalf("bad in raddr: %v", gc.Oconv(n.Op, 0))
+ gc.Fatalf("bad in raddr: %v", n.Op)
} else {
gc.Fatalf("bad in raddr: <null>")
}
@@ -790,7 +790,7 @@ func optoas(op gc.Op, t *gc.Type) obj.As {
a := obj.AXXX
switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) {
default:
- gc.Fatalf("optoas: no entry %v-%v etype %v simtype %v", gc.Oconv(op, 0), t, gc.Types[t.Etype], gc.Types[gc.Simtype[t.Etype]])
+ gc.Fatalf("optoas: no entry %v-%v etype %v simtype %v", op, t, gc.Types[t.Etype], gc.Types[gc.Simtype[t.Etype]])
/* case CASE(OADDR, TPTR32):
a = ALEAL;
diff --git a/src/cmd/compile/internal/arm/ssa.go b/src/cmd/compile/internal/arm/ssa.go
index e6211d00b7..ca10f1c508 100644
--- a/src/cmd/compile/internal/arm/ssa.go
+++ b/src/cmd/compile/internal/arm/ssa.go
@@ -91,8 +91,10 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
case ssa.OpARMCMP:
p := gc.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
- p.From.Reg = gc.SSARegNum(v.Args[0])
- p.Reg = gc.SSARegNum(v.Args[1])
+ // Special layout in ARM assembly
+ // Comparing to x86, the operands of ARM's CMP are reversed.
+ p.From.Reg = gc.SSARegNum(v.Args[1])
+ p.Reg = gc.SSARegNum(v.Args[0])
case ssa.OpARMMOVWload:
p := gc.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
@@ -142,7 +144,7 @@ func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
case ssa.BlockRet:
gc.Prog(obj.ARET)
case ssa.BlockARMLT:
- p := gc.Prog(arm.ABGE)
+ p := gc.Prog(arm.ABLT)
p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, gc.Branch{p, b.Succs[0]})
p = gc.Prog(obj.AJMP)
diff --git a/src/cmd/compile/internal/arm64/galign.go b/src/cmd/compile/internal/arm64/galign.go
index 7e1226fee1..7acc4e08eb 100644
--- a/src/cmd/compile/internal/arm64/galign.go
+++ b/src/cmd/compile/internal/arm64/galign.go
@@ -10,15 +10,10 @@ import (
)
func betypeinit() {
- gc.Widthptr = 8
- gc.Widthint = 8
- gc.Widthreg = 8
}
func Main() {
- gc.Thearch.Thechar = '7'
- gc.Thearch.Thestring = "arm64"
- gc.Thearch.Thelinkarch = &arm64.Linkarm64
+ gc.Thearch.LinkArch = &arm64.Linkarm64
gc.Thearch.REGSP = arm64.REGSP
gc.Thearch.REGCTXT = arm64.REGCTXT
gc.Thearch.REGCALLX = arm64.REGRT1
@@ -34,6 +29,8 @@ func Main() {
gc.Thearch.Betypeinit = betypeinit
gc.Thearch.Cgen_hmul = cgen_hmul
+ gc.Thearch.AddSetCarry = AddSetCarry
+ gc.Thearch.RightShiftWithCarry = RightShiftWithCarry
gc.Thearch.Cgen_shift = cgen_shift
gc.Thearch.Clearfat = clearfat
gc.Thearch.Defframe = defframe
diff --git a/src/cmd/compile/internal/arm64/ggen.go b/src/cmd/compile/internal/arm64/ggen.go
index 9abd901d7a..bddfed631a 100644
--- a/src/cmd/compile/internal/arm64/ggen.go
+++ b/src/cmd/compile/internal/arm64/ggen.go
@@ -252,6 +252,53 @@ func dodiv(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node) {
}
}
+// RightShiftWithCarry generates a constant unsigned
+// right shift with carry.
+//
+// res = n >> shift // with carry
+func RightShiftWithCarry(n *gc.Node, shift uint, res *gc.Node) {
+ // Extra 1 is for carry bit.
+ maxshift := uint(n.Type.Width*8 + 1)
+ if shift == 0 {
+ gmove(n, res)
+ } else if shift < maxshift {
+ // 1. clear rightmost bit of target
+ var n1 gc.Node
+ gc.Nodconst(&n1, n.Type, 1)
+ gins(optoas(gc.ORSH, n.Type), &n1, n)
+ gins(optoas(gc.OLSH, n.Type), &n1, n)
+ // 2. add carry flag to target
+ var n2 gc.Node
+ gc.Nodconst(&n1, n.Type, 0)
+ gc.Regalloc(&n2, n.Type, nil)
+ gins(optoas(gc.OAS, n.Type), &n1, &n2)
+ gins(arm64.AADC, &n2, n)
+ // 3. right rotate 1 bit
+ gc.Nodconst(&n1, n.Type, 1)
+ gins(arm64.AROR, &n1, n)
+
+ // ARM64 backend doesn't eliminate shifts by 0. It is manually checked here.
+ if shift > 1 {
+ var n3 gc.Node
+ gc.Nodconst(&n3, n.Type, int64(shift-1))
+ cgen_shift(gc.ORSH, true, n, &n3, res)
+ } else {
+ gmove(n, res)
+ }
+ gc.Regfree(&n2)
+ } else {
+ gc.Fatalf("RightShiftWithCarry: shift(%v) is bigger than max size(%v)", shift, maxshift)
+ }
+}
+
+// AddSetCarry generates add and set carry.
+//
+// res = nl + nr // with carry flag set
+func AddSetCarry(nl *gc.Node, nr *gc.Node, res *gc.Node) {
+ gins(arm64.AADDS, nl, nr)
+ gmove(nr, res)
+}
+
/*
* generate high multiply:
* res = (nl*nr) >> width
diff --git a/src/cmd/compile/internal/arm64/gsubr.go b/src/cmd/compile/internal/arm64/gsubr.go
index 4d64e790af..f193291d01 100644
--- a/src/cmd/compile/internal/arm64/gsubr.go
+++ b/src/cmd/compile/internal/arm64/gsubr.go
@@ -567,7 +567,7 @@ func raddr(n *gc.Node, p *obj.Prog) {
gc.Naddr(&a, n)
if a.Type != obj.TYPE_REG {
if n != nil {
- gc.Fatalf("bad in raddr: %v", gc.Oconv(n.Op, 0))
+ gc.Fatalf("bad in raddr: %v", n.Op)
} else {
gc.Fatalf("bad in raddr: <null>")
}
@@ -579,7 +579,7 @@ func raddr(n *gc.Node, p *obj.Prog) {
func gcmp(as obj.As, lhs *gc.Node, rhs *gc.Node) *obj.Prog {
if lhs.Op != gc.OREGISTER {
- gc.Fatalf("bad operands to gcmp: %v %v", gc.Oconv(lhs.Op, 0), gc.Oconv(rhs.Op, 0))
+ gc.Fatalf("bad operands to gcmp: %v %v", lhs.Op, rhs.Op)
}
p := rawgins(as, rhs, nil)
@@ -622,7 +622,7 @@ func optoas(op gc.Op, t *gc.Type) obj.As {
a := obj.AXXX
switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) {
default:
- gc.Fatalf("optoas: no entry for op=%v type=%v", gc.Oconv(op, 0), t)
+ gc.Fatalf("optoas: no entry for op=%v type=%v", op, t)
case OEQ_ | gc.TBOOL,
OEQ_ | gc.TINT8,
@@ -890,18 +890,6 @@ func optoas(op gc.Op, t *gc.Type) obj.As {
ORSH_ | 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 OHMUL_ | gc.TINT64:
a = arm64.ASMULH
diff --git a/src/cmd/compile/internal/arm64/peep.go b/src/cmd/compile/internal/arm64/peep.go
index 887353c889..22be1afebc 100644
--- a/src/cmd/compile/internal/arm64/peep.go
+++ b/src/cmd/compile/internal/arm64/peep.go
@@ -534,10 +534,13 @@ func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int {
return 0
case arm64.AADD, /* read p->from, read p->reg, write p->to */
+ arm64.AADDS,
arm64.ASUB,
+ arm64.AADC,
arm64.AAND,
arm64.AORR,
arm64.AEOR,
+ arm64.AROR,
arm64.AMUL,
arm64.ASMULL,
arm64.AUMULL,
diff --git a/src/cmd/compile/internal/arm64/prog.go b/src/cmd/compile/internal/arm64/prog.go
index 3091c4a840..d504d0f0ee 100644
--- a/src/cmd/compile/internal/arm64/prog.go
+++ b/src/cmd/compile/internal/arm64/prog.go
@@ -59,6 +59,9 @@ var progtable = [arm64.ALAST & obj.AMask]obj.ProgInfo{
arm64.ALSR & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
arm64.AASR & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
arm64.ACMP & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead},
+ arm64.AADC & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite | gc.UseCarry},
+ arm64.AROR & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ arm64.AADDS & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite | gc.SetCarry},
// Floating point.
arm64.AFADDD & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite},
diff --git a/src/cmd/compile/internal/gc/alg.go b/src/cmd/compile/internal/gc/alg.go
index e9b5afe838..136612d56f 100644
--- a/src/cmd/compile/internal/gc/alg.go
+++ b/src/cmd/compile/internal/gc/alg.go
@@ -127,11 +127,10 @@ func algtype1(t *Type) (AlgKind, *Type) {
}
return AINTER, nil
- case TARRAY:
- if t.IsSlice() {
- return ANOEQ, t
- }
+ case TSLICE:
+ return ANOEQ, t
+ case TARRAY:
a, bad := algtype1(t.Elem())
switch a {
case AMEM:
@@ -219,10 +218,6 @@ func genhash(sym *Sym, t *Type) {
Fatalf("genhash %v", t)
case TARRAY:
- if t.IsSlice() {
- Fatalf("genhash %v", t)
- }
-
// An array of pure memory would be handled by the
// standard algorithm, so the element type must not be
// pure memory.
@@ -316,11 +311,12 @@ func genhash(sym *Sym, t *Type) {
// for a struct containing a reflect.Value, which itself has
// an unexported field of type unsafe.Pointer.
old_safemode := safemode
+ safemode = false
- safemode = 0
Disable_checknil++
funccompile(fn)
Disable_checknil--
+
safemode = old_safemode
}
@@ -398,10 +394,6 @@ func geneq(sym *Sym, t *Type) {
Fatalf("geneq %v", t)
case TARRAY:
- if t.IsSlice() {
- Fatalf("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,
@@ -509,7 +501,7 @@ func geneq(sym *Sym, t *Type) {
// for a struct containing a reflect.Value, which itself has
// an unexported field of type unsafe.Pointer.
old_safemode := safemode
- safemode = 0
+ safemode = false
// Disable checknils while compiling this code.
// We are comparing a struct or an array,
diff --git a/src/cmd/compile/internal/gc/align.go b/src/cmd/compile/internal/gc/align.go
index b7ed9f19b9..8123041318 100644
--- a/src/cmd/compile/internal/gc/align.go
+++ b/src/cmd/compile/internal/gc/align.go
@@ -198,11 +198,11 @@ func dowidth(t *Type) {
// make fake type to check later to
// trigger channel argument check.
- t1 := typWrapper(TCHANARGS, t)
+ t1 := typChanArgs(t)
checkwidth(t1)
case TCHANARGS:
- t1 := t.Wrapped()
+ t1 := t.ChanArgs()
dowidth(t1) // just in case
if t1.Elem().Width >= 1<<16 {
Yyerror("channel element type too large (>64kB)")
@@ -238,32 +238,34 @@ func dowidth(t *Type) {
if t.Elem() == nil {
break
}
- if t.IsArray() {
- dowidth(t.Elem())
- if t.Elem().Width != 0 {
- cap := (uint64(Thearch.MAXWIDTH) - 1) / uint64(t.Elem().Width)
- if uint64(t.NumElem()) > cap {
- Yyerror("type %v larger than address space", Tconv(t, FmtLong))
- }
- }
-
- w = t.NumElem() * t.Elem().Width
- t.Align = t.Elem().Align
- } else if t.IsSlice() {
- w = int64(sizeof_Array)
- checkwidth(t.Elem())
- t.Align = uint8(Widthptr)
- } else if t.isDDDArray() {
+ if t.isDDDArray() {
if !t.Broke {
Yyerror("use of [...] array outside of array literal")
t.Broke = true
}
- } else {
- Fatalf("dowidth %v", t) // probably [...]T
+ break
+ }
+
+ dowidth(t.Elem())
+ if t.Elem().Width != 0 {
+ cap := (uint64(Thearch.MAXWIDTH) - 1) / uint64(t.Elem().Width)
+ if uint64(t.NumElem()) > cap {
+ Yyerror("type %v larger than address space", Tconv(t, FmtLong))
+ }
}
+ w = t.NumElem() * t.Elem().Width
+ t.Align = t.Elem().Align
+
+ case TSLICE:
+ if t.Elem() == nil {
+ break
+ }
+ w = int64(sizeof_Array)
+ checkwidth(t.Elem())
+ t.Align = uint8(Widthptr)
case TSTRUCT:
- if t.Funarg {
+ if t.IsFuncArgStruct() {
Fatalf("dowidth fn struct %v", t)
}
w = widstruct(t, t, 0, 1)
@@ -271,18 +273,18 @@ func dowidth(t *Type) {
// make fake type to check later to
// trigger function argument computation.
case TFUNC:
- t1 := typWrapper(TFUNCARGS, t)
+ t1 := typFuncArgs(t)
checkwidth(t1)
w = int64(Widthptr) // width of func type is pointer
// function is 3 cated structures;
// compute their widths as side-effect.
case TFUNCARGS:
- t1 := t.Wrapped()
+ t1 := t.FuncArgs()
w = widstruct(t1, t1.Recvs(), 0, 0)
w = widstruct(t1, t1.Params(), w, Widthreg)
w = widstruct(t1, t1.Results(), w, Widthreg)
- t1.Argwid = w
+ t1.Extra.(*FuncType).Argwid = w
if w%int64(Widthreg) != 0 {
Warn("bad type %v %d\n", t1, w)
}
@@ -335,7 +337,7 @@ func checkwidth(t *Type) {
// function arg structs should not be checked
// outside of the enclosing function.
- if t.Funarg {
+ if t.IsFuncArgStruct() {
Fatalf("checkwidth %v", t)
}
@@ -386,7 +388,7 @@ func Argsize(t *Type) int {
}
}
- w = (w + int64(Widthptr) - 1) &^ (int64(Widthptr) - 1)
+ w = Rnd(w, int64(Widthptr))
if int64(int(w)) != w {
Fatalf("argsize too big")
}
diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go
index 8968ce8924..53662620aa 100644
--- a/src/cmd/compile/internal/gc/bexport.go
+++ b/src/cmd/compile/internal/gc/bexport.go
@@ -36,32 +36,27 @@ If the field is a pointer to another object, that object is serialized,
recursively. Otherwise the field is written. Non-pointer fields are all
encoded as integer or string values.
-Only packages and types may be referred to more than once. When getting
-to a package or type that was not serialized before, an integer _index_
+Some objects (packages, types) may be referred to more than once. When
+reaching an object that was not serialized before, an integer _index_
is assigned to it, starting at 0. In this case, the encoding starts
with an integer _tag_ < 0. The tag value indicates the kind of object
-(package or type) that follows and that this is the first time that we
-see this object. If the package or tag was already serialized, the encoding
-starts with the respective package or type index >= 0. An importer can
-trivially determine if a package or type needs to be read in for the first
-time (tag < 0) and entered into the respective package or type table, or
-if the package or type was seen already (index >= 0), in which case the
-index is used to look up the object in a table.
+that follows and that this is the first time that we see this object.
+If the object was already serialized, the encoding is simply the object
+index >= 0. An importer can trivially determine if an object needs to
+be read in for the first time (tag < 0) and entered into the respective
+object table, or if the object was seen already (index >= 0), in which
+case the index is used to look up the object in a table.
Before exporting or importing, the type tables are populated with the
predeclared types (int, string, error, unsafe.Pointer, etc.). This way
they are automatically encoded with a known and fixed type index.
-TODO(gri) We may consider using the same sharing for other items
-that are written out, such as strings, or possibly symbols (*Sym).
-
Encoding format:
The export data starts with a single byte indicating the encoding format
(compact, or with debugging information), followed by a version string
-(so we can evolve the encoding if need be), the name of the imported
-package, and a string containing platform-specific information for that
-package.
+(so we can evolve the encoding if need be), and then the package object
+for the exported package (with an empty path).
After this header, two lists of objects and the list of inlined function
bodies follows.
@@ -74,11 +69,17 @@ the previously imported type pointer so that we have exactly one version
(i.e., one pointer) for each named type (and read but discard the current
type encoding). Unnamed types simply encode their respective fields.
-In the encoding, some lists start with the list length (incl. strings).
-Some lists are terminated with an end marker (usually for lists where
-we may not know the length a priori).
+In the encoding, some lists start with the list length. Some lists are
+terminated with an end marker (usually for lists where we may not know
+the length a priori).
+
+Integers use variable-length encoding for compact representation.
-All integer values use variable-length encoding for compact representation.
+Strings are canonicalized similar to objects that may occur multiple times:
+If the string was exported already, it is represented by its index only.
+Otherwise, the export data starts with the negative string length (negative,
+so we can distinguish from string index), followed by the string bytes.
+The empty string is mapped to index 0.
The exporter and importer are completely symmetric in implementation: For
each encoding routine there is a matching and symmetric decoding routine.
@@ -90,9 +91,9 @@ importer.
package gc
import (
+ "bufio"
"bytes"
"cmd/compile/internal/big"
- "cmd/internal/obj"
"encoding/binary"
"fmt"
"sort"
@@ -109,12 +110,13 @@ import (
//
// NOTE: This flag is the first flag to enable if importing dies because of
// (suspected) format errors, and whenever a change is made to the format.
-// Having debugFormat enabled increases the export data size massively (by
-// several factors) - avoid running with the flag enabled in general.
const debugFormat = false // default: false
-// TODO(gri) remove eventually
-const forceNewExport = false // force new export format - DO NOT SUBMIT with this flag set
+// forceObjFileStability enforces additional constraints in export data
+// and other parts of the compiler to eliminate object file differences
+// only due to the choice of export format.
+// TODO(gri) disable and remove once there is only one export format again
+const forceObjFileStability = true
const exportVersion = "v0"
@@ -126,10 +128,18 @@ const exportVersion = "v0"
const exportInlined = true // default: true
type exporter struct {
- out *obj.Biobuf
+ out *bufio.Writer
+
+ // object -> index maps, indexed in order of serialization
+ strIndex map[string]int
pkgIndex map[*Pkg]int
typIndex map[*Type]int
- inlined []*Func
+ funcList []*Func
+
+ // position encoding
+ posInfoFormat bool
+ prevFile string
+ prevLine int
// debugging support
written int // bytes written
@@ -137,13 +147,18 @@ type exporter struct {
trace bool
}
-// Export writes the exportlist for localpkg to out and returns the number of bytes written.
-func Export(out *obj.Biobuf, trace bool) int {
+// export writes the exportlist for localpkg to out and returns the number of bytes written.
+func export(out *bufio.Writer, trace bool) int {
p := exporter{
out: out,
+ strIndex: map[string]int{"": 0}, // empty string is mapped to 0
pkgIndex: make(map[*Pkg]int),
typIndex: make(map[*Type]int),
- trace: trace,
+ // don't emit pos info for builtin packages
+ // (not needed and avoids path name diffs in builtin.go between
+ // Windows and non-Windows machines, exposed via builtin_test.go)
+ posInfoFormat: Debug['A'] == 0,
+ trace: trace,
}
// first byte indicates low-level encoding format
@@ -151,7 +166,10 @@ func Export(out *obj.Biobuf, trace bool) int {
if debugFormat {
format = 'd'
}
- p.byte(format)
+ p.rawByte(format)
+
+ // posInfo exported or not?
+ p.bool(p.posInfoFormat)
// --- generic export data ---
@@ -184,22 +202,12 @@ func Export(out *obj.Biobuf, trace bool) int {
Fatalf("exporter: local package path not empty: %q", localpkg.Path)
}
p.pkg(localpkg)
-
- // write compiler-specific flags
- // TODO(gri) move this into the compiler-specific export data section
- {
- var flags string
- if safemode != 0 {
- flags = "safe"
- }
- p.string(flags)
- }
if p.trace {
p.tracef("\n")
}
// export objects
-
+ //
// First, export all exported (package-level) objects; i.e., all objects
// in the current exportlist. These objects represent all information
// required to import this package and type-check against it; i.e., this
@@ -272,6 +280,12 @@ func Export(out *obj.Biobuf, trace bool) int {
}
}
+ // write compiler-specific flags
+ p.bool(safemode)
+ if p.trace {
+ p.tracef("\n")
+ }
+
// Phase 2: Export objects added to exportlist during phase 1.
// Don't use range since exportlist may grow during this phase
// and we want to export all remaining objects.
@@ -328,27 +342,39 @@ func Export(out *obj.Biobuf, trace bool) int {
// --- inlined function bodies ---
if p.trace {
- p.tracef("\n--- inlined function bodies ---\n[ ")
+ p.tracef("\n--- inlined function bodies ---\n")
if p.indent != 0 {
Fatalf("exporter: incorrect indentation")
}
}
- // write inlined function bodies
- p.int(len(p.inlined))
- if p.trace {
- p.tracef("]\n")
- }
- for _, f := range p.inlined {
- if p.trace {
- p.tracef("\n----\nfunc { %s }\n", Hconv(f.Inl, FmtSharp))
- }
- p.stmtList(f.Inl)
- if p.trace {
- p.tracef("\n")
+ // write inlineable function bodies
+ objcount = 0
+ for i, f := range p.funcList {
+ if f != nil {
+ // function has inlineable body:
+ // write index and body
+ if p.trace {
+ p.tracef("\n----\nfunc { %s }\n", Hconv(f.Inl, FmtSharp))
+ }
+ p.int(i)
+ p.stmtList(f.Inl)
+ if p.trace {
+ p.tracef("\n")
+ }
+ objcount++
}
}
+ // indicate end of list
+ if p.trace {
+ p.tracef("\n")
+ }
+ p.int(-1) // invalid index terminates list
+
+ // for self-verification only (redundant)
+ p.int(objcount)
+
if p.trace {
p.tracef("\n--- end ---\n")
}
@@ -413,6 +439,7 @@ func (p *exporter) obj(sym *Sym) {
}
p.tag(constTag)
+ p.pos(n)
// TODO(gri) In inlined functions, constants are used directly
// so they should never occur as re-exported objects. We may
// not need the qualified name here. See also comment above.
@@ -441,6 +468,7 @@ func (p *exporter) obj(sym *Sym) {
if n.Type.Etype == TFUNC && n.Class == PFUNC {
// function
p.tag(funcTag)
+ p.pos(n)
p.qualifiedName(sym)
sig := sym.Def.Type
@@ -449,10 +477,9 @@ func (p *exporter) obj(sym *Sym) {
p.paramList(sig.Params(), inlineable)
p.paramList(sig.Results(), inlineable)
- index := -1
+ var f *Func
if inlineable {
- index = len(p.inlined)
- p.inlined = append(p.inlined, sym.Def.Func)
+ f = sym.Def.Func
// TODO(gri) re-examine reexportdeplist:
// Because we can trivially export types
// in-place, we don't need to collect types
@@ -460,23 +487,48 @@ func (p *exporter) obj(sym *Sym) {
// With an adjusted reexportdeplist used only
// by the binary exporter, we can also avoid
// the global exportlist.
- reexportdeplist(sym.Def.Func.Inl)
+ reexportdeplist(f.Inl)
}
- p.int(index)
+ p.funcList = append(p.funcList, f)
} else {
// variable
p.tag(varTag)
+ p.pos(n)
p.qualifiedName(sym)
p.typ(sym.Def.Type)
}
default:
- Fatalf("exporter: unexpected export symbol: %v %v", Oconv(n.Op, 0), sym)
+ Fatalf("exporter: unexpected export symbol: %v %v", n.Op, sym)
}
}
+func (p *exporter) pos(n *Node) {
+ if !p.posInfoFormat {
+ return
+ }
+
+ var file string
+ var line int
+ if n != nil {
+ file, line = Ctxt.LineHist.AbsFileLine(int(n.Lineno))
+ }
+
+ if file == p.prevFile && line != p.prevLine {
+ // common case: write delta-encoded line number
+ p.int(line - p.prevLine) // != 0
+ } else {
+ // uncommon case: filename changed, or line didn't change
+ p.int(0)
+ p.string(file)
+ p.int(line)
+ p.prevFile = file
+ }
+ p.prevLine = line
+}
+
func isInlineable(n *Node) bool {
- if exportInlined && n != nil && n.Func != nil && len(n.Func.Inl.Slice()) != 0 {
+ if exportInlined && n != nil && n.Func != nil && n.Func.Inl.Len() != 0 {
// 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 {
@@ -515,19 +567,20 @@ func (p *exporter) typ(t *Type) {
p.typIndex[t] = len(p.typIndex)
// pick off named types
- if sym := t.Sym; sym != nil {
+ if tsym := t.Sym; tsym != nil {
// Predeclared types should have been found in the type map.
if t.Orig == t {
Fatalf("exporter: predeclared type missing from type map?")
}
- // TODO(gri) The assertion below seems incorrect (crashes during all.bash).
- // we expect the respective definition to point to us
- // if sym.Def.Type != t {
- // Fatalf("exporter: type definition doesn't point to us?")
- // }
+
+ n := typenod(t)
+ if n.Type != t {
+ Fatalf("exporter: named type definition incorrectly set up")
+ }
p.tag(namedTag)
- p.qualifiedName(sym)
+ p.pos(n)
+ p.qualifiedName(tsym)
// write underlying type
p.typ(t.Orig)
@@ -559,6 +612,7 @@ func (p *exporter) typ(t *Type) {
Fatalf("invalid symbol name: %s (%v)", m.Sym.Name, m.Sym)
}
+ p.pos(m.Sym.Def)
p.fieldSym(m.Sym, false)
sig := m.Type
@@ -569,13 +623,12 @@ func (p *exporter) typ(t *Type) {
p.paramList(sig.Params(), inlineable)
p.paramList(sig.Results(), inlineable)
- index := -1
+ var f *Func
if inlineable {
- index = len(p.inlined)
- p.inlined = append(p.inlined, mfn.Func)
+ f = mfn.Func
reexportdeplist(mfn.Func.Inl)
}
- p.int(index)
+ p.funcList = append(p.funcList, f)
}
if p.trace && len(methods) > 0 {
@@ -591,18 +644,18 @@ func (p *exporter) typ(t *Type) {
if t.isDDDArray() {
Fatalf("array bounds should be known at export time: %v", t)
}
- if t.IsArray() {
- p.tag(arrayTag)
- p.int64(t.NumElem())
- } else {
- p.tag(sliceTag)
- }
+ p.tag(arrayTag)
+ p.int64(t.NumElem())
+ p.typ(t.Elem())
+
+ case TSLICE:
+ p.tag(sliceTag)
p.typ(t.Elem())
case TDDDFIELD:
// see p.param use of TDDDFIELD
p.tag(dddTag)
- p.typ(t.Wrapped())
+ p.typ(t.DDDField())
case TSTRUCT:
p.tag(structTag)
@@ -664,17 +717,10 @@ func (p *exporter) fieldList(t *Type) {
}
func (p *exporter) field(f *Field) {
+ p.pos(f.Sym.Def)
p.fieldName(f.Sym, f)
p.typ(f.Type)
- p.note(f.Note)
-}
-
-func (p *exporter) note(n *string) {
- var s string
- if n != nil {
- s = *n
- }
- p.string(s)
+ p.string(f.Note)
}
func (p *exporter) methodList(t *Type) {
@@ -693,6 +739,7 @@ func (p *exporter) methodList(t *Type) {
}
func (p *exporter) method(m *Field) {
+ p.pos(m.Sym.Def)
p.fieldName(m.Sym, m)
p.paramList(m.Type.Params(), false)
p.paramList(m.Type.Results(), false)
@@ -742,7 +789,7 @@ func basetypeName(t *Type) string {
}
func (p *exporter) paramList(params *Type, numbered bool) {
- if !params.IsStruct() || !params.Funarg {
+ if !params.IsFuncArgStruct() {
Fatalf("exporter: parameter list expected")
}
@@ -768,7 +815,7 @@ func (p *exporter) param(q *Field, n int, numbered bool) {
t := q.Type
if q.Isddd {
// create a fake type to encode ... just for the p.typ call
- t = typWrapper(TDDDFIELD, t.Elem())
+ t = typDDDField(t.Elem())
}
p.typ(t)
if n > 0 {
@@ -779,16 +826,17 @@ func (p *exporter) param(q *Field, n int, numbered bool) {
// supply the parameter package here. We need the package
// when the function is inlined so we can properly resolve
// the name.
- // TODO(gri) should do this only once per function/method
+ // TODO(gri) This is compiler-specific. Try using importpkg
+ // here and then update the symbols if we find an inlined
+ // body only. Otherwise, the parameter name is ignored and
+ // the package doesn't matter. This would remove an int
+ // (likely 1 byte) for each named parameter.
p.pkg(q.Sym.Pkg)
}
// TODO(gri) This is compiler-specific (escape info).
// Move into compiler-specific section eventually?
// (Not having escape info causes tests to fail, e.g. runtime GCInfoTest)
- //
- // TODO(gri) The q.Note is much more verbose that necessary and
- // adds significantly to export data size. FIX THIS.
- p.note(q.Note)
+ p.string(q.Note)
}
func parName(f *Field, numbered bool) string {
@@ -827,7 +875,7 @@ func parName(f *Field, numbered bool) string {
// Functions that can be inlined use numbered parameters so we can distingish them
// from other names in their context after inlining (i.e., the parameter numbering
// is a form of parameter rewriting). See issue 4326 for an example and test case.
- if numbered {
+ if forceObjFileStability || numbered {
if !strings.Contains(name, "·") && f.Nname != nil && f.Nname.Name != nil && f.Nname.Name.Vargen > 0 {
name = fmt.Sprintf("%s·%d", name, f.Nname.Name.Vargen) // append Vargen
}
@@ -1132,12 +1180,15 @@ func (p *exporter) expr(n *Node) {
case OSLICE, OSLICESTR, OSLICEARR:
p.op(OSLICE)
p.expr(n.Left)
- p.expr(n.Right)
+ low, high, _ := n.SliceBounds()
+ p.exprsOrNil(low, high)
case OSLICE3, OSLICE3ARR:
p.op(OSLICE3)
p.expr(n.Left)
- p.expr(n.Right)
+ low, high, max := n.SliceBounds()
+ p.exprsOrNil(low, high)
+ p.expr(max)
case OCOPY, OCOMPLEX:
p.op(op)
@@ -1215,7 +1266,7 @@ func (p *exporter) expr(n *Node) {
p.op(ODCLCONST)
default:
- Fatalf("exporter: CANNOT EXPORT: %s\nPlease notify gri@\n", opnames[n.Op])
+ Fatalf("exporter: CANNOT EXPORT: %s\nPlease notify gri@\n", n.Op)
}
}
@@ -1262,12 +1313,11 @@ func (p *exporter) stmt(n *Node) {
// unimplemented - handled by default case
case OAS, OASWB:
- p.op(op)
// Don't export "v = <N>" initializing statements, hope they're always
// preceded by the DCL which will be re-parsed and typecheck to reproduce
// the "v = <N>" again.
- // TODO(gri) if n.Right == nil, don't emit anything
- if p.bool(n.Right != nil) {
+ if n.Right != nil {
+ p.op(OAS)
p.expr(n.Left)
p.expr(n.Right)
}
@@ -1280,16 +1330,14 @@ func (p *exporter) stmt(n *Node) {
p.expr(n.Right)
}
+ case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
+ fallthrough
+
case OAS2:
p.op(OAS2)
p.exprList(n.List)
p.exprList(n.Rlist)
- case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
- p.op(op)
- p.exprList(n.List)
- p.exprList(n.Rlist)
-
case ORETURN:
p.op(ORETURN)
p.exprList(n.List)
@@ -1328,11 +1376,15 @@ func (p *exporter) stmt(n *Node) {
p.stmtList(n.List)
case OCASE, OXCASE:
- p.op(op)
+ p.op(OXCASE)
p.stmtList(n.List)
p.stmtList(n.Nbody)
- case OBREAK, OCONTINUE, OGOTO, OFALL, OXFALL:
+ case OFALL:
+ op = OXFALL
+ fallthrough
+
+ case OBREAK, OCONTINUE, OGOTO, OXFALL:
p.op(op)
p.exprsOrNil(n.Left, nil)
@@ -1344,7 +1396,7 @@ func (p *exporter) stmt(n *Node) {
p.expr(n.Left)
default:
- Fatalf("exporter: CANNOT EXPORT: %s\nPlease notify gri@\n", opnames[n.Op])
+ Fatalf("exporter: CANNOT EXPORT: %s\nPlease notify gri@\n", n.Op)
}
}
@@ -1432,7 +1484,7 @@ func (p *exporter) bool(b bool) bool {
func (p *exporter) op(op Op) {
if p.trace {
p.tracef("[")
- defer p.tracef("= %s] ", opnames[op])
+ defer p.tracef("= %s] ", op)
}
p.int(int(op))
@@ -1488,9 +1540,17 @@ func (p *exporter) string(s string) {
if p.trace {
p.tracef("%q ", s)
}
- p.rawInt64(int64(len(s)))
+ // if we saw the string before, write its index (>= 0)
+ // (the empty string is mapped to 0)
+ if i, ok := p.strIndex[s]; ok {
+ p.rawInt64(int64(i))
+ return
+ }
+ // otherwise, remember string and write its negative length and bytes
+ p.strIndex[s] = len(p.strIndex)
+ p.rawInt64(-int64(len(s)))
for i := 0; i < len(s); i++ {
- p.byte(s[i])
+ p.rawByte(s[i])
}
}
@@ -1498,7 +1558,7 @@ func (p *exporter) string(s string) {
// it easy for a reader to detect if it is "out of sync". Used only
// if debugFormat is set.
func (p *exporter) marker(m byte) {
- p.byte(m)
+ p.rawByte(m)
// Uncomment this for help tracking down the location
// of an incorrect marker when running in debugFormat.
// if p.trace {
@@ -1512,12 +1572,12 @@ func (p *exporter) rawInt64(x int64) {
var tmp [binary.MaxVarintLen64]byte
n := binary.PutVarint(tmp[:], x)
for i := 0; i < n; i++ {
- p.byte(tmp[i])
+ p.rawByte(tmp[i])
}
}
-// byte is the bottleneck interface to write to p.out.
-// byte escapes b as follows (any encoding does that
+// rawByte is the bottleneck interface to write to p.out.
+// rawByte escapes b as follows (any encoding does that
// hides '$'):
//
// '$' => '|' 'S'
@@ -1525,7 +1585,8 @@ func (p *exporter) rawInt64(x int64) {
//
// Necessary so other tools can find the end of the
// export data by searching for "$$".
-func (p *exporter) byte(b byte) {
+// rawByte should only be used by low-level encoders.
+func (p *exporter) rawByte(b byte) {
switch b {
case '$':
// write '$' as '|' 'S'
@@ -1533,10 +1594,10 @@ func (p *exporter) byte(b byte) {
fallthrough
case '|':
// write '|' as '|' '|'
- obj.Bputc(p.out, '|')
+ p.out.WriteByte('|')
p.written++
}
- obj.Bputc(p.out, b)
+ p.out.WriteByte(b)
p.written++
}
diff --git a/src/cmd/compile/internal/gc/bimport.go b/src/cmd/compile/internal/gc/bimport.go
index 8c53372b80..6fe30cdba9 100644
--- a/src/cmd/compile/internal/gc/bimport.go
+++ b/src/cmd/compile/internal/gc/bimport.go
@@ -20,12 +20,19 @@ import (
// changes to bimport.go and bexport.go.
type importer struct {
- in *bufio.Reader
- buf []byte // for reading strings
- bufarray [64]byte // initial underlying array for buf, large enough to avoid allocation when compiling std lib
+ in *bufio.Reader
+ buf []byte // reused for reading strings
+
+ // object lists, in order of deserialization
+ strList []string
pkgList []*Pkg
typList []*Type
- inlined []*Node // functions with pending inlined function bodies
+ funcList []*Node // nil entry means already declared
+
+ // position encoding
+ posInfoFormat bool
+ prevFile string
+ prevLine int
// debugging support
debugFormat bool
@@ -34,11 +41,13 @@ type importer struct {
// Import populates importpkg from the serialized package data.
func Import(in *bufio.Reader) {
- p := importer{in: in}
- p.buf = p.bufarray[:]
+ p := importer{
+ in: in,
+ strList: []string{""}, // empty string is mapped to 0
+ }
// read low-level encoding format
- switch format := p.byte(); format {
+ switch format := p.rawByte(); format {
case 'c':
// compact format - nothing to do
case 'd':
@@ -47,6 +56,8 @@ func Import(in *bufio.Reader) {
Fatalf("importer: invalid encoding format in export data: got %q; want 'c' or 'd'", format)
}
+ p.posInfoFormat = p.bool()
+
// --- generic export data ---
if v := p.string(); v != exportVersion {
@@ -58,12 +69,6 @@ func Import(in *bufio.Reader) {
// read package data
p.pkg()
- if p.pkgList[0] != importpkg {
- Fatalf("importer: imported package not found in pkgList[0]")
- }
-
- // read compiler-specific flags
- importpkg.Safe = p.string() == "safe"
// defer some type-checking until all types are read in completely
// (parser.go:import_package)
@@ -73,7 +78,7 @@ func Import(in *bufio.Reader) {
// read objects
- // Phase 1
+ // phase 1
objcount := 0
for {
tag := p.tagOrIndex()
@@ -91,7 +96,10 @@ func Import(in *bufio.Reader) {
// --- compiler-specific export data ---
- // Phase 2
+ // read compiler-specific flags
+ importpkg.Safe = p.bool()
+
+ // phase 2
objcount = 0
for {
tag := p.tagOrIndex()
@@ -107,23 +115,46 @@ func Import(in *bufio.Reader) {
Fatalf("importer: got %d objects; want %d", objcount, count)
}
- // read inlined functions bodies
+ // read inlineable functions bodies
if dclcontext != PEXTERN {
Fatalf("importer: unexpected context %d", dclcontext)
}
- bcount := p.int() // consistency check only
- if bcount != len(p.inlined) {
- Fatalf("importer: expected %d inlined function bodies; got %d", bcount, len(p.inlined))
- }
- for _, f := range p.inlined {
+ objcount = 0
+ for i0 := -1; ; {
+ i := p.int() // index of function with inlineable body
+ if i < 0 {
+ break
+ }
+
+ // don't process the same function twice
+ if i <= i0 {
+ Fatalf("importer: index not increasing: %d <= %d", i, i0)
+ }
+ i0 = i
+
if Funcdepth != 0 {
Fatalf("importer: unexpected Funcdepth %d", Funcdepth)
}
- if f != nil {
- // function body not yet imported - read body and set it
+
+ // Note: In the original code, funchdr and funcbody are called for
+ // all functions (that were not yet imported). Now, we are calling
+ // them only for functions with inlineable bodies. funchdr does
+ // parameter renaming which doesn't matter if we don't have a body.
+
+ if f := p.funcList[i]; f != nil {
+ // function not yet imported - read body and set it
funchdr(f)
- f.Func.Inl.Set(p.stmtList())
+ body := p.stmtList()
+ if body == nil {
+ // Make sure empty body is not interpreted as
+ // no inlineable body (see also parser.fnbody)
+ // (not doing so can cause significant performance
+ // degradation due to unnecessary calls to empty
+ // functions).
+ body = []*Node{Nod(OEMPTY, nil, nil)}
+ }
+ f.Func.Inl.Set(body)
funcbody(f)
} else {
// function already imported - read body but discard declarations
@@ -131,6 +162,13 @@ func Import(in *bufio.Reader) {
p.stmtList()
dclcontext = PEXTERN
}
+
+ objcount++
+ }
+
+ // self-verification
+ if count := p.int(); count != objcount {
+ Fatalf("importer: got %d functions; want %d", objcount, count)
}
if dclcontext != PEXTERN {
@@ -171,7 +209,12 @@ func (p *importer) pkg() *Pkg {
Fatalf("importer: bad path in import: %q", path)
}
- // an empty path denotes the package we are currently importing
+ // an empty path denotes the package we are currently importing;
+ // it must be the first package we see
+ if (path == "") != (len(p.pkgList) == 0) {
+ panic(fmt.Sprintf("package path %q for pkg index %d", path, len(p.pkgList)))
+ }
+
pkg := importpkg
if path != "" {
pkg = mkpkg(path)
@@ -197,6 +240,7 @@ func idealType(typ *Type) *Type {
func (p *importer) obj(tag int) {
switch tag {
case constTag:
+ p.pos()
sym := p.qualifiedName()
typ := p.typ()
val := p.value(typ)
@@ -206,68 +250,66 @@ func (p *importer) obj(tag int) {
p.typ()
case varTag:
+ p.pos()
sym := p.qualifiedName()
typ := p.typ()
importvar(sym, typ)
case funcTag:
+ p.pos()
sym := p.qualifiedName()
params := p.paramList()
result := p.paramList()
- inl := p.int()
sig := functype(nil, params, result)
importsym(sym, ONAME)
if sym.Def != nil && sym.Def.Op == ONAME {
- if Eqtype(sig, sym.Def.Type) {
- // function was imported before (via another import)
- dclcontext = PDISCARD // since we skip funchdr below
- } else {
+ // function was imported before (via another import)
+ if !Eqtype(sig, sym.Def.Type) {
Fatalf("importer: inconsistent definition for func %v during import\n\t%v\n\t%v", sym, sym.Def.Type, sig)
}
- }
-
- var n *Node
- if dclcontext != PDISCARD {
- n = newfuncname(sym)
- n.Type = sig
- declare(n, PFUNC)
- if inl < 0 {
- funchdr(n)
- }
- }
-
- if inl >= 0 {
- // function has inlined body - collect for later
- if inl != len(p.inlined) {
- Fatalf("importer: inlined index = %d; want %d", inl, len(p.inlined))
- }
- p.inlined = append(p.inlined, n)
- }
-
- // parser.go:hidden_import
- if dclcontext == PDISCARD {
- dclcontext = PEXTERN // since we skip the funcbody below
+ p.funcList = append(p.funcList, nil)
break
}
- if inl < 0 {
- funcbody(n)
- }
- importlist = append(importlist, n) // TODO(gri) may only be needed for inlineable functions
+ n := newfuncname(sym)
+ n.Type = sig
+ declare(n, PFUNC)
+ p.funcList = append(p.funcList, n)
+ importlist = append(importlist, n)
if Debug['E'] > 0 {
fmt.Printf("import [%q] func %v \n", importpkg.Path, n)
- if Debug['m'] > 2 && len(n.Func.Inl.Slice()) != 0 {
+ if Debug['m'] > 2 && n.Func.Inl.Len() != 0 {
fmt.Printf("inl body: %v\n", n.Func.Inl)
}
}
default:
- Fatalf("importer: unexpected object tag")
+ Fatalf("importer: unexpected object (tag = %d)", tag)
}
}
+func (p *importer) pos() {
+ if !p.posInfoFormat {
+ return
+ }
+
+ file := p.prevFile
+ line := p.prevLine
+
+ if delta := p.int(); delta != 0 {
+ line += delta
+ } else {
+ file = p.string()
+ line = p.int()
+ p.prevFile = file
+ }
+ p.prevLine = line
+
+ // TODO(gri) register new position
+}
+
func (p *importer) newtyp(etype EType) *Type {
t := typ(etype)
p.typList = append(p.typList, t)
@@ -286,6 +328,7 @@ func (p *importer) typ() *Type {
switch i {
case namedTag:
// parser.go:hidden_importsym
+ p.pos()
tsym := p.qualifiedName()
// parser.go:hidden_pkgtype
@@ -311,28 +354,19 @@ func (p *importer) typ() *Type {
for i := p.int(); i > 0; i-- {
// parser.go:hidden_fndcl
+ p.pos()
sym := p.fieldSym()
recv := p.paramList() // TODO(gri) do we need a full param list for the receiver?
params := p.paramList()
result := p.paramList()
- inl := p.int()
n := methodname1(newname(sym), recv[0].Right)
n.Type = functype(recv[0], params, result)
checkwidth(n.Type)
addmethod(sym, n.Type, tsym.Pkg, false, false)
- if inl < 0 {
- funchdr(n)
- }
-
- if inl >= 0 {
- // method has inlined body - collect for later
- if inl != len(p.inlined) {
- Fatalf("importer: inlined index = %d; want %d", inl, len(p.inlined))
- }
- p.inlined = append(p.inlined, n)
- }
+ p.funcList = append(p.funcList, n)
+ importlist = append(importlist, n)
// (comment from parser.go)
// inl.C's inlnode in on a dotmeth node expects to find the inlineable body as
@@ -341,15 +375,9 @@ func (p *importer) typ() *Type {
// this back link here we avoid special casing there.
n.Type.SetNname(n)
- // parser.go:hidden_import
- if inl < 0 {
- funcbody(n)
- }
- importlist = append(importlist, n) // TODO(gri) may only be needed for inlineable functions
-
if Debug['E'] > 0 {
fmt.Printf("import [%q] meth %v \n", importpkg.Path, n)
- if Debug['m'] > 2 && len(n.Func.Inl.Slice()) != 0 {
+ if Debug['m'] > 2 && n.Func.Inl.Len() != 0 {
fmt.Printf("inl body: %v\n", n.Func.Inl)
}
}
@@ -357,18 +385,20 @@ func (p *importer) typ() *Type {
dclcontext = savedContext
- case arrayTag, sliceTag:
+ case arrayTag:
t = p.newtyp(TARRAY)
- if i == arrayTag {
- t.SetNumElem(p.int64())
- } else {
- t.SetNumElem(sliceBound)
- }
- t.Type = p.typ()
+ bound := p.int64()
+ elem := p.typ()
+ t.Extra = &ArrayType{Elem: elem, Bound: bound}
+
+ case sliceTag:
+ t = p.newtyp(TSLICE)
+ elem := p.typ()
+ t.Extra = SliceType{Elem: elem}
case dddTag:
t = p.newtyp(TDDDFIELD)
- t.Type = p.typ()
+ t.Extra = DDDFieldType{T: p.typ()}
case structTag:
t = p.newtyp(TSTRUCT)
@@ -376,7 +406,7 @@ func (p *importer) typ() *Type {
case pointerTag:
t = p.newtyp(Tptr)
- t.Type = p.typ()
+ t.Extra = PtrType{Elem: p.typ()}
case signatureTag:
t = p.newtyp(TFUNC)
@@ -393,13 +423,15 @@ func (p *importer) typ() *Type {
case mapTag:
t = p.newtyp(TMAP)
- t.Down = p.typ() // key
- t.Type = p.typ() // val
+ mt := t.MapType()
+ mt.Key = p.typ()
+ mt.Val = p.typ()
case chanTag:
t = p.newtyp(TCHAN)
- t.Chan = ChanDir(p.int())
- t.Type = p.typ()
+ ct := t.ChanType()
+ ct.Dir = ChanDir(p.int())
+ ct.Elem = p.typ()
default:
Fatalf("importer: unexpected type (tag = %d)", i)
@@ -419,23 +451,22 @@ func (p *importer) qualifiedName() *Sym {
}
// parser.go:hidden_structdcl_list
-func (p *importer) fieldList() []*Node {
- i := p.int()
- if i == 0 {
- return nil
- }
- n := make([]*Node, i)
- for i := range n {
- n[i] = p.field()
+func (p *importer) fieldList() (fields []*Node) {
+ if n := p.int(); n > 0 {
+ fields = make([]*Node, n)
+ for i := range fields {
+ fields[i] = p.field()
+ }
}
- return n
+ return
}
// parser.go:hidden_structdcl
func (p *importer) field() *Node {
+ p.pos()
sym := p.fieldName()
typ := p.typ()
- note := p.note()
+ note := p.string()
var n *Node
if sym.Name != "" {
@@ -444,7 +475,7 @@ func (p *importer) field() *Node {
// anonymous field - typ must be T or *T and T must be a type name
s := typ.Sym
if s == nil && typ.IsPtr() {
- s = typ.Type.Sym // deref
+ s = typ.Elem().Sym // deref
}
pkg := importpkg
if sym != nil {
@@ -453,33 +484,25 @@ func (p *importer) field() *Node {
n = embedded(s, pkg)
n.Right = typenod(typ)
}
- n.SetVal(note)
+ n.SetVal(Val{U: note})
return n
}
-func (p *importer) note() (v Val) {
- if s := p.string(); s != "" {
- v.U = s
- }
- return
-}
-
// parser.go:hidden_interfacedcl_list
-func (p *importer) methodList() []*Node {
- i := p.int()
- if i == 0 {
- return nil
- }
- n := make([]*Node, i)
- for i := range n {
- n[i] = p.method()
+func (p *importer) methodList() (methods []*Node) {
+ if n := p.int(); n > 0 {
+ methods = make([]*Node, n)
+ for i := range methods {
+ methods[i] = p.method()
+ }
}
- return n
+ return
}
// parser.go:hidden_interfacedcl
func (p *importer) method() *Node {
+ p.pos()
sym := p.fieldName()
params := p.paramList()
result := p.paramList()
@@ -531,7 +554,7 @@ func (p *importer) param(named bool) *Node {
isddd := false
if typ.Etype == TDDDFIELD {
// TDDDFIELD indicates wrapped ... slice type
- typ = typSlice(typ.Wrapped())
+ typ = typSlice(typ.DDDField())
isddd = true
}
@@ -551,7 +574,7 @@ func (p *importer) param(named bool) *Node {
// TODO(gri) This is compiler-specific (escape info).
// Move into compiler-specific section eventually?
- n.SetVal(p.note())
+ n.SetVal(Val{U: p.string()})
return n
}
@@ -636,6 +659,10 @@ func (p *importer) float(x *Mpflt) {
// re-establish the syntax tree's invariants. At some future point we might be
// able to avoid this round-about way and create the rewritten nodes directly,
// possibly avoiding a lot of duplicate work (name resolution, type checking).
+//
+// Refined nodes (e.g., ODOTPTR as a refinement of OXDOT) are exported as their
+// unrefined nodes (since this is what the importer uses). The respective case
+// entries are unreachable in the importer.
func (p *importer) stmtList() []*Node {
var list []*Node
@@ -797,9 +824,19 @@ func (p *importer) node() *Node {
// case OINDEX, OINDEXMAP, OSLICE, OSLICESTR, OSLICEARR, OSLICE3, OSLICE3ARR:
// unreachable - mapped to cases below by exporter
- case OINDEX, OSLICE, OSLICE3:
+ case OINDEX:
return Nod(op, p.expr(), p.expr())
+ case OSLICE, OSLICE3:
+ n := Nod(op, p.expr(), nil)
+ low, high := p.exprsOrNil()
+ var max *Node
+ if n.Op.IsSlice3() {
+ max = p.expr()
+ }
+ n.SetSliceBounds(low, high, max)
+ return n
+
case OCOPY, OCOMPLEX:
n := builtinCall(op)
n.List.Set([]*Node{p.expr(), p.expr()})
@@ -881,14 +918,11 @@ func (p *importer) node() *Node {
// case ODCLFIELD:
// unimplemented
- case OAS, OASWB:
- if p.bool() {
- lhs := p.expr()
- rhs := p.expr()
- return Nod(OAS, lhs, rhs)
- }
- // TODO(gri) we should not have emitted anything here
- return Nod(OEMPTY, nil, nil)
+ // case OAS, OASWB:
+ // unreachable - mapped to OAS case below by exporter
+
+ case OAS:
+ return Nod(OAS, p.expr(), p.expr())
case OASOP:
n := Nod(OASOP, nil, nil)
@@ -902,15 +936,10 @@ func (p *importer) node() *Node {
}
return n
- case OAS2:
- lhs := p.exprList()
- rhs := p.exprList()
- n := Nod(OAS2, nil, nil)
- n.List.Set(lhs)
- n.Rlist.Set(rhs)
- return n
+ // case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
+ // unreachable - mapped to OAS2 case below by exporter
- case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
+ case OAS2:
n := Nod(OAS2, nil, nil)
n.List.Set(p.exprList())
n.Rlist.Set(p.exprList())
@@ -964,7 +993,10 @@ func (p *importer) node() *Node {
popdcl()
return n
- case OCASE, OXCASE:
+ // case OCASE, OXCASE:
+ // unreachable - mapped to OXCASE case below by exporter
+
+ case OXCASE:
markdcl()
n := Nod(OXCASE, nil, nil)
n.List.Set(p.exprList())
@@ -974,10 +1006,10 @@ func (p *importer) node() *Node {
popdcl()
return n
- case OBREAK, OCONTINUE, OGOTO, OFALL, OXFALL:
- if op == OFALL {
- op = OXFALL
- }
+ // case OFALL:
+ // unreachable - mapped to OXFALL case below by exporter
+
+ case OBREAK, OCONTINUE, OGOTO, OXFALL:
left, _ := p.exprsOrNil()
return Nod(op, left, nil)
@@ -993,7 +1025,7 @@ func (p *importer) node() *Node {
return nil
default:
- Fatalf("importer: %s (%d) node not yet supported", opnames[op], op)
+ Fatalf("importer: %s (%d) node not yet supported", op, op)
panic("unreachable") // satisfy compiler
}
}
@@ -1067,29 +1099,31 @@ func (p *importer) int64() int64 {
}
func (p *importer) string() string {
- if p.debugFormat {
+ if debugFormat {
p.marker('s')
}
-
- // TODO(gri) should we intern strings here?
-
- if n := int(p.rawInt64()); n > 0 {
- if cap(p.buf) < n {
- p.buf = make([]byte, n)
- } else {
- p.buf = p.buf[:n]
- }
- for i := range p.buf {
- p.buf[i] = p.byte()
- }
- return string(p.buf)
+ // if the string was seen before, i is its index (>= 0)
+ // (the empty string is at index 0)
+ i := p.rawInt64()
+ if i >= 0 {
+ return p.strList[i]
}
-
- return ""
+ // otherwise, i is the negative string length (< 0)
+ if n := int(-i); n <= cap(p.buf) {
+ p.buf = p.buf[:n]
+ } else {
+ p.buf = make([]byte, n)
+ }
+ for i := range p.buf {
+ p.buf[i] = p.rawByte()
+ }
+ s := string(p.buf)
+ p.strList = append(p.strList, s)
+ return s
}
func (p *importer) marker(want byte) {
- if got := p.byte(); got != want {
+ if got := p.rawByte(); got != want {
Fatalf("importer: incorrect marker: got %c; want %c (pos = %d)", got, want, p.read)
}
@@ -1110,12 +1144,13 @@ func (p *importer) rawInt64() int64 {
// needed for binary.ReadVarint in rawInt64
func (p *importer) ReadByte() (byte, error) {
- return p.byte(), nil
+ return p.rawByte(), nil
}
-// byte is the bottleneck interface for reading from p.in.
+// rawByte is the bottleneck interface for reading from p.in.
// It unescapes '|' 'S' to '$' and '|' '|' to '|'.
-func (p *importer) byte() byte {
+// rawByte should only be used by low-level decoders.
+func (p *importer) rawByte() byte {
c, err := p.in.ReadByte()
p.read++
if err != nil {
diff --git a/src/cmd/compile/internal/gc/builtin.go b/src/cmd/compile/internal/gc/builtin.go
index 411c7b8605..cc64e73f25 100644
--- a/src/cmd/compile/internal/gc/builtin.go
+++ b/src/cmd/compile/internal/gc/builtin.go
@@ -3,135 +3,106 @@
package gc
const runtimeimport = "" +
- "package runtime safe\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 \"unsafe-uintptr\") (? int)\n" +
- "func @\"\".slicestringcopy (@\"\".to·2 any, @\"\".fr·3 any) (? int)\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 (@\"\".tab·2 *byte, @\"\".elem·3 *any, @\"\".buf·4 *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 @\"\".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 @\"\".writeBarrier struct { @\"\".enabled bool; @\"\".needed bool; @\"\".cgo bool }\n" +
- "func @\"\".writebarrierptr (@\"\".dst·1 *any, @\"\".src·2 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, @\"\".cap·4 int) (@\"\".ary·1 []any)\n" +
- "func @\"\".memmove (@\"\".to·1 *any, @\"\".frm·2 *any, @\"\".length·3 uintptr \"unsafe-uintptr\")\n" +
- "func @\"\".memclr (@\"\".ptr·1 *byte, @\"\".length·2 uintptr \"unsafe-uintptr\")\n" +
- "func @\"\".memequal (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr \"unsafe-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 \"unsafe-uintptr\")\n" +
- "func @\"\".racefuncexit ()\n" +
- "func @\"\".raceread (? uintptr \"unsafe-uintptr\")\n" +
- "func @\"\".racewrite (? uintptr \"unsafe-uintptr\")\n" +
- "func @\"\".racereadrange (@\"\".addr·1 uintptr \"unsafe-uintptr\", @\"\".size·2 uintptr \"unsafe-uintptr\")\n" +
- "func @\"\".racewriterange (@\"\".addr·1 uintptr \"unsafe-uintptr\", @\"\".size·2 uintptr \"unsafe-uintptr\")\n" +
- "func @\"\".msanread (@\"\".addr·1 uintptr \"unsafe-uintptr\", @\"\".size·2 uintptr \"unsafe-uintptr\")\n" +
- "func @\"\".msanwrite (@\"\".addr·1 uintptr \"unsafe-uintptr\", @\"\".size·2 uintptr \"unsafe-uintptr\")\n" +
- "\n" +
- "$$\n"
+ "c\x00\x03v0\x01\rruntime\x00\t\x11newobject\x00\x02\x17\"\vtyp·2\x00\x00\x01" +
+ "\x17:\x00\t\x13panicindex\x00\x00\x00\t\x13panicslice\x00\x00\x00\t\x15panic" +
+ "divide\x00\x00\x00\t\x15throwreturn\x00\x00\x00\t\x11throwinit\x00\x00\x00\t" +
+ "\x11panicwrap\x00\x05 \x00 \x00 \x00\x00\t\rgopanic\x00\x01\x1b\x00\x00\x00\x00\t\x11gor" +
+ "ecover\x00\x01\x17\b\x00\x01\x1b\x00\x00\x00\t\x11printbool\x00\x01\x00\x00\x00\t\x13printf" +
+ "loat\x00\x01\x1a\x00\x00\t\x0fprintint\x00\x01\n\x00\x00\t\x0fprinthex\x00\x01\x14\x00\x00\t" +
+ "\x11printuint\x00\x01\x14\x00\x00\t\x17printcomplex\x00\x01\x1e\x00\x00\t\x15prin" +
+ "tstring\x00\x01 \x00\x00\t\x17printpointer\x00\x01:\x00\x00\t\x13printif" +
+ "ace\x00\x01:\x00\x00\t\x13printeface\x00\x01:\x00\x00\t\x13printslice\x00\x01:" +
+ "\x00\x00\t\rprintnl\x00\x00\x00\t\rprintsp\x00\x00\x00\t\x11printlock\x00\x00\x00" +
+ "\t\x15printunlock\x00\x00\x00\t\x19concatstring2\x00\x05\x17\x0f@\"\x00 \x00" +
+ " \x00\x01 \x00\t\x19concatstring3\x00\a\x17\x0f@\"\x00 \x00 \x00 \x00\x01 \x00\t\x19co" +
+ "ncatstring4\x00\t\x17\x0f@\"\x00 \x00 \x00 \x00 \x00\x01 \x00\t\x19concatstr" +
+ "ing5\x00\v\x17\x0f@\"\x00 \x00 \x00 \x00 \x00 \x00\x01 \x00\t\x19concatstrings\x00" +
+ "\x03\x17\x0f@\"\x00\x11 \x00\x01 \x00\t\x11cmpstring\x00\x03 \x00 \x00\x01\x02\x00\t\x0feqstri" +
+ "ng\x00\x03 \x00 \x00\x01\x00\x00\t\x11intstring\x00\x03\x17\x0f\b\"\x00\n\x00\x01 \x00\t!slic" +
+ "ebytetostring\x00\x03\x17\x0f@\"\x00\x11\"\x00\x01 \x00\t'slicebytetos" +
+ "tringtmp\x00\x01\x11\"\x00\x01 \x00\t!slicerunetostring\x00\x03\x17\x0f@" +
+ "\"\x00\x11|S\x00\x01 \x00\t!stringtoslicebyte\x00\x03\x17\x0f@\"\x00 \x00\x01\x11\"" +
+ "\x00\t'stringtoslicebytetmp\x00\x01 \x00\x01\x11\"\x00\t!stringt" +
+ "oslicerune\x00\x03\x17\x0f@|S\x00 \x00\x01\x11|S\x00\t\x13stringiter\x00\x03 " +
+ "\x00\x02\x00\x01\x02\x00\t\x15stringiter2\x00\x03 \x00\x02\x00\x04\x02\rretk·1\x00\x00|S\r" +
+ "retv·2\x00\x00\t\x11slicecopy\x00\x06:\tto·2\x00\x00:\tfr·3\x00\x00" +
+ "\x16\vwid·4\x00\x1bunsafe-uintptr\x01\x02\x00\t\x1dslicestring" +
+ "copy\x00\x04:^\x00\x00:`\x00\x00\x01\x02\x00\t\rconvI2E\x00\x02:\relem·2\x00\x00\x02" +
+ ":\vret·1\x00\x00\t\rconvI2I\x00\x04\x17\"\b\x00\x00:\relem·3\x00\x00\x02:l" +
+ "\x00\x00\t\rconvT2E\x00\x06\x17\"\b\x00\x00>p\x00\x00>\vbuf·4\x00\x00\x02:l\x00\x00\t\rc" +
+ "onvT2I\x00\x06\x17\"\vtab·2\x00\x00>p\x00\x00>t\x00\x00\x02:l\x00\x00\t\x11assert" +
+ "E2E\x00\x06\x17\"\vtyp·1\x00\x00:\x0fiface·2\x00\x00>\vret·3\x00\x00\x00\t" +
+ "\x13assertE2E2\x00\x06\x17\"\b\x00\x00:\x0fiface·3\x00\x00>\vret·4\x00\x00" +
+ "\x01\x00\x00\t\x11assertE2I\x00\x06\x17\"||\x00\x00:~\x00\x00>\x80\x01\x00\x00\x00\t\x13assert" +
+ "E2I2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00>\x86\x01\x00\x00\x01\x00\x00\t\x11assertE2T\x00\x06\x17\"|" +
+ "|\x00\x00:~\x00\x00>\x80\x01\x00\x00\x00\t\x13assertE2T2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00>\x86\x01" +
+ "\x00\x00\x01\x00\x00\t\x11assertI2E\x00\x06\x17\"||\x00\x00:~\x00\x00>\x80\x01\x00\x00\x00\t\x13asse" +
+ "rtI2E2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00>\x86\x01\x00\x00\x01\x00\x00\t\x11assertI2I\x00\x06\x17" +
+ "\"||\x00\x00:~\x00\x00>\x80\x01\x00\x00\x00\t\x13assertI2I2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00>" +
+ "\x86\x01\x00\x00\x01\x00\x00\t\x11assertI2T\x00\x06\x17\"||\x00\x00:~\x00\x00>\x80\x01\x00\x00\x00\t\x13as" +
+ "sertI2T2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00>\x86\x01\x00\x00\x01\x00\x00\t\x17panicdotty" +
+ "pe\x00\x06\x17\"\rhave·1\x00\x00\x9a\x01\rwant·2\x00\x00\x9a\x01\x84\x01\x00\x00\x00\t\rifa" +
+ "ceeq\x00\x04:\ti1·2\x00\x00:\ti2·3\x00\x00\x02\x00l\x00\x00\t\refaceeq\x00\x04" +
+ ":\xa4\x01\x00\x00:\xa6\x01\x00\x00\x02\x00l\x00\x00\t\rmakemap\x00\b\x17\"\x13mapType·2\x00" +
+ "\x00\n\rhint·3\x00\x00>\x11mapbuf·4\x00\x00>\x17bucketbuf·5\x00" +
+ "\x00\x02\x1d::\rhmap·1\x00\x00\t\x13mapaccess1\x00\x06\x17\"\xac\x01\x00\x00\x1d::\rh" +
+ "map·3\x00\x00>\vkey·4\x00\x00\x02>\vval·1\x00\x00\t!mapaccess" +
+ "1_fast32\x00\x06\x17\"\xac\x01\x00\x00\x1d::\xb8\x01\x00\x00:\xba\x01\x00\x00\x02>\xbc\x01\x00\x00\t!mapa" +
+ "ccess1_fast64\x00\x06\x17\"\xac\x01\x00\x00\x1d::\xb8\x01\x00\x00:\xba\x01\x00\x00\x02>\xbc\x01\x00\x00\t" +
+ "#mapaccess1_faststr\x00\x06\x17\"\xac\x01\x00\x00\x1d::\xb8\x01\x00\x00:\xba\x01\x00\x00\x02" +
+ ">\xbc\x01\x00\x00\t\x1bmapaccess1_fat\x00\b\x17\"\xac\x01\x00\x00\x1d::\xb8\x01\x00\x00>\xba\x01\x00" +
+ "\x00\x17\"\rzero·5\x00\x00\x02>\xbc\x01\x00\x00\t\x13mapaccess2\x00\x06\x17\"\x13mapT" +
+ "ype·3\x00\x00\x1d::\rhmap·4\x00\x00>\vkey·5\x00\x00\x04>\xbc\x01\x00\x00\x00\rp" +
+ "res·2\x00\x00\t!mapaccess2_fast32\x00\x06\x17\"\xca\x01\x00\x00\x1d::\xcc\x01" +
+ "\x00\x00:\xce\x01\x00\x00\x04>\xbc\x01\x00\x00\x00\xd0\x01\x00\x00\t!mapaccess2_fast64\x00\x06\x17" +
+ "\"\xca\x01\x00\x00\x1d::\xcc\x01\x00\x00:\xce\x01\x00\x00\x04>\xbc\x01\x00\x00\x00\xd0\x01\x00\x00\t#mapaccess2" +
+ "_faststr\x00\x06\x17\"\xca\x01\x00\x00\x1d::\xcc\x01\x00\x00:\xce\x01\x00\x00\x04>\xbc\x01\x00\x00\x00\xd0\x01\x00\x00\t" +
+ "\x1bmapaccess2_fat\x00\b\x17\"\xca\x01\x00\x00\x1d::\xcc\x01\x00\x00>\xce\x01\x00\x00\x17\"\rze" +
+ "ro·6\x00\x00\x04>\xbc\x01\x00\x00\x00\xd0\x01\x00\x00\t\x13mapassign1\x00\b\x17\"\x13mapTy" +
+ "pe·1\x00\x00\x1d::\rhmap·2\x00\x00>\vkey·3\x00\x00>\vval·4\x00\x00" +
+ "\x00\t\x15mapiterinit\x00\x06\x17\"\xde\x01\x00\x00\x1d::\xe0\x01\x00\x00>\x0fhiter·3\x00" +
+ "\x00\x00\t\x11mapdelete\x00\x06\x17\"\xde\x01\x00\x00\x1d::\xe0\x01\x00\x00>\xe2\x01\x00\x00\x00\t\x15mapi" +
+ "ternext\x00\x02>\x0fhiter·1\x00\x00\x00\t\x0fmakechan\x00\x04\x17\"\x15cha" +
+ "nType·2\x00\x00\n\xae\x01\x00\x00\x02\x1f\x06:\x0fhchan·1\x00\x00\t\x11chanrecv" +
+ "1\x00\x06\x17\"\x15chanType·1\x00\x00\x1f\x02:\x0fhchan·2\x00\x00>p\x00\x00\x00\t\x11" +
+ "chanrecv2\x00\x06\x17\"\xf2\x01\x00\x00\x1f\x02:\x0fhchan·3\x00\x00>\relem·4" +
+ "\x00\x00\x01\x00\x00\t\x11chansend1\x00\x06\x17\"\xf8\x01\x00\x00\x1f\x04:\xfa\x01\x00\x00>p\x00\x00\x00\t\x11cl" +
+ "osechan\x00\x02:\xf4\x01\x00\x00\x00\a\x17writeBarrier\x00\x15\x06\renabled" +
+ "\x00\x00\x00\vneeded\x00\x00\x00\x05cgo\x00\x00\x00\t\x1dwritebarrierptr\x00\x04>" +
+ "\vdst·1\x00\x00:\vsrc·2\x00\x00\x00\t\x17typedmemmove\x00\x06\x17\"||" +
+ "\x00\x00>\vdst·2\x00\x00>\vsrc·3\x00\x00\x00\t\x1btypedslicecopy\x00" +
+ "\x06\x17\"\b\x00\x00:\vdst·3\x00\x00:\vsrc·4\x00\x00\x01\x02\x00\t\x17selectnbs" +
+ "end\x00\x06\x17\"\xf2\x01\x00\x00\x1f\x04:\xfe\x01\x00\x00>\x80\x02\x00\x00\x01\x00\x00\t\x17selectnbrecv" +
+ "\x00\x06\x17\"\xf2\x01\x00\x00>p\x00\x00\x1f\x02:\x0fhchan·4\x00\x00\x01\x00\x00\t\x19selectnbr" +
+ "ecv2\x00\b\x17\"\xf2\x01\x00\x00>p\x00\x00\x17\x00\x15received·4\x00\x00\x1f\x02:\x0fhcha" +
+ "n·5\x00\x00\x01\x00\x00\t\x11newselect\x00\x06\x17\"\vsel·1\x00\x00\n\x13selsi" +
+ "ze·2\x00\x00\b\rsize·3\x00\x00\x00\t\x13selectsend\x00\x06\x17\"\vsel\xc2" +
+ "\xb72\x00\x00\x1f\x04:\xfe\x01\x00\x00>\x80\x02\x00\x00\x02\x00\x15selected·1\x00\x00\t\x13select" +
+ "recv\x00\x06\x17\"\xb6\x02\x00\x00\x1f\x02:\xfe\x01\x00\x00>\x80\x02\x00\x00\x02\x00\xb8\x02\x00\x00\t\x15selectre" +
+ "cv2\x00\b\x17\"\xb6\x02\x00\x00\x1f\x02:\xfe\x01\x00\x00>\x80\x02\x00\x00\xf8\x01\x15received·5\x00\x00\x02" +
+ "\x00\xb8\x02\x00\x00\t\x19selectdefault\x00\x02\x17\"\xb6\x02\x00\x00\x02\x00\xb8\x02\x00\x00\t\x0fsele" +
+ "ctgo\x00\x02\x17\"\xae\x02\x00\x00\x00\t\tblock\x00\x00\x00\t\x11makeslice\x00\x06\x17\"\b\x00" +
+ "\x00\n\vnel·3\x00\x00\n\vcap·4\x00\x00\x02\x11:\vary·1\x00\x00\t\x11grows" +
+ "lice\x00\x06\x17\"\b\x00\x00\x11:\vold·3\x00\x00\x02\xca\x02\x00\x00\x02\x11:\xcc\x02\x00\x00\t\rmemm" +
+ "ove\x00\x06>\tto·1\x00\x00>\vfrm·2\x00\x00\x16\x11length·3\x00d\x00\t\v" +
+ "memclr\x00\x04\x17\"\vptr·1\x00\x00\x16\x11length·2\x00d\x00\t\x0fmemeq" +
+ "ual\x00\x06>\ax·2\x00\x00>\ay·3\x00\x00\x16\rsize·4\x00d\x01\x00\x00\t\x11mem" +
+ "equal8\x00\x04>\xe2\x02\x00\x00>\xe4\x02\x00\x00\x01\x00\x00\t\x13memequal16\x00\x04>\xe2\x02\x00\x00" +
+ ">\xe4\x02\x00\x00\x01\x00\x00\t\x13memequal32\x00\x04>\xe2\x02\x00\x00>\xe4\x02\x00\x00\x01\x00\x00\t\x13mem" +
+ "equal64\x00\x04>\xe2\x02\x00\x00>\xe4\x02\x00\x00\x01\x00\x00\t\x15memequal128\x00\x04>\xe2\x02" +
+ "\x00\x00>\xe4\x02\x00\x00\x01\x00\x00\t\x0fint64div\x00\x03\n\x00\n\x00\x01\n\x00\t\x11uint64div" +
+ "\x00\x03\x14\x00\x14\x00\x01\x14\x00\t\x0fint64mod\x00\x03\n\x00\n\x00\x01\n\x00\t\x11uint64mod\x00" +
+ "\x03\x14\x00\x14\x00\x01\x14\x00\t\x1bfloat64toint64\x00\x01\x1a\x00\x01\n\x00\t\x1dfloat64" +
+ "touint64\x00\x01\x1a\x00\x01\x14\x00\t\x1bint64tofloat64\x00\x01\n\x00\x01\x1a\x00\t\x1d" +
+ "uint64tofloat64\x00\x01\x14\x00\x01\x1a\x00\t\x19complex128div\x00\x04\x1e" +
+ "\vnum·2\x00\x00\x1e\vden·3\x00\x00\x02\x1e\vquo·1\x00\x00\t\x19racefunc" +
+ "enter\x00\x01\x16d\x00\t\x17racefuncexit\x00\x00\x00\t\x0fraceread\x00\x01\x16" +
+ "d\x00\t\x11racewrite\x00\x01\x16d\x00\t\x19racereadrange\x00\x04\x16\radd" +
+ "r·1\x00d\x16\rsize·2\x00d\x00\t\x1bracewriterange\x00\x04\x16\x94\x03\x00" +
+ "d\x16\x96\x03\x00d\x00\t\x0fmsanread\x00\x04\x16\x94\x03\x00d\x16\x96\x03\x00d\x00\t\x11msanwrit" +
+ "e\x00\x04\x16\x94\x03\x00d\x16\x96\x03\x00d\x00\v\xf4\x01\x02\v\x00\x01\x00\n$$\n"
const unsafeimport = "" +
- "package unsafe\n" +
- "type @\"\".Pointer uintptr\n" +
- "func @\"\".Offsetof (? any) (? uintptr)\n" +
- "func @\"\".Sizeof (? any) (? uintptr)\n" +
- "func @\"\".Alignof (? any) (? uintptr)\n" +
- "\n" +
- "$$\n"
+ "c\x00\x03v0\x01\vunsafe\x00\x05\r\rPointer\x00\x16\x00\t\x0fOffsetof\x00\x01:" +
+ "\x00\x01\x16\x00\t\vSizeof\x00\x01:\x00\x01\x16\x00\t\rAlignof\x00\x01:\x00\x01\x16\x00\v\b\x00\v\x00" +
+ "\x01\x00\n$$\n"
diff --git a/src/cmd/compile/internal/gc/builtin/runtime.go b/src/cmd/compile/internal/gc/builtin/runtime.go
index 584368a144..e9316cb313 100644
--- a/src/cmd/compile/internal/gc/builtin/runtime.go
+++ b/src/cmd/compile/internal/gc/builtin/runtime.go
@@ -89,10 +89,12 @@ 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 mapaccess1_fat(mapType *byte, hmap map[any]any, key *any, zero *byte) (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 mapaccess2_fat(mapType *byte, hmap map[any]any, key *any, zero *byte) (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)
diff --git a/src/cmd/compile/internal/gc/cgen.go b/src/cmd/compile/internal/gc/cgen.go
index c594ad4c11..fd57fbd4a7 100644
--- a/src/cmd/compile/internal/gc/cgen.go
+++ b/src/cmd/compile/internal/gc/cgen.go
@@ -7,6 +7,7 @@ package gc
import (
"cmd/internal/obj"
"cmd/internal/obj/ppc64"
+ "cmd/internal/sys"
"fmt"
)
@@ -88,7 +89,7 @@ func cgen_wb(n, res *Node, wb bool) {
if !res.Addable {
if n.Ullman > res.Ullman {
- if Ctxt.Arch.Regsize == 4 && Is64(n.Type) {
+ if Ctxt.Arch.RegSize == 4 && Is64(n.Type) {
var n1 Node
Tempname(&n1, n.Type)
Cgen(n, &n1)
@@ -127,7 +128,7 @@ func cgen_wb(n, res *Node, wb bool) {
f = false
}
- if !n.Type.IsComplex() && Ctxt.Arch.Regsize == 8 && !wb {
+ if !n.Type.IsComplex() && Ctxt.Arch.RegSize == 8 && !wb {
a := Thearch.Optoas(OAS, res.Type)
var addr obj.Addr
if Thearch.Sudoaddable(a, res, &addr) {
@@ -151,7 +152,7 @@ func cgen_wb(n, res *Node, wb bool) {
}
}
- if Ctxt.Arch.Thechar == '8' {
+ if Ctxt.Arch.Family == sys.I386 {
// no registers to speak of
var n1, n2 Node
Tempname(&n1, n.Type)
@@ -203,7 +204,7 @@ func cgen_wb(n, res *Node, wb bool) {
// Write barrier now handled. Code below this line can ignore wb.
- if Ctxt.Arch.Thechar == '5' { // TODO(rsc): Maybe more often?
+ if Ctxt.Arch.Family == sys.ARM { // 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 || n.Type.IsComplex() || res.Type.IsComplex() {
@@ -246,12 +247,12 @@ func cgen_wb(n, res *Node, wb bool) {
return
}
- if (Ctxt.Arch.Thechar == '6' || Ctxt.Arch.Thechar == '8') && n.Addable {
+ if Ctxt.Arch.InFamily(sys.AMD64, sys.I386, sys.S390X) && n.Addable {
Thearch.Gmove(n, res)
return
}
- if Ctxt.Arch.Thechar == '0' || Ctxt.Arch.Thechar == '7' || Ctxt.Arch.Thechar == '9' {
+ if Ctxt.Arch.InFamily(sys.ARM64, sys.MIPS64, sys.PPC64) {
// if both are addressable, move
if n.Addable {
if n.Op == OREGISTER || res.Op == OREGISTER {
@@ -268,7 +269,7 @@ func cgen_wb(n, res *Node, wb bool) {
}
// if n is sudoaddable generate addr and move
- if Ctxt.Arch.Thechar == '5' && !Is64(n.Type) && !Is64(res.Type) && !n.Type.IsComplex() && !res.Type.IsComplex() {
+ if Ctxt.Arch.Family == sys.ARM && !Is64(n.Type) && !Is64(res.Type) && !n.Type.IsComplex() && !res.Type.IsComplex() {
a := Thearch.Optoas(OAS, n.Type)
var addr obj.Addr
if Thearch.Sudoaddable(a, n, &addr) {
@@ -310,7 +311,7 @@ func cgen_wb(n, res *Node, wb bool) {
}
// 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)) {
+ 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,
@@ -334,7 +335,7 @@ func cgen_wb(n, res *Node, wb bool) {
return
}
- if !n.Type.IsComplex() && Ctxt.Arch.Regsize == 8 {
+ if !n.Type.IsComplex() && Ctxt.Arch.RegSize == 8 {
a := Thearch.Optoas(OAS, n.Type)
var addr obj.Addr
if Thearch.Sudoaddable(a, n, &addr) {
@@ -401,11 +402,11 @@ func cgen_wb(n, res *Node, wb bool) {
Regalloc(&n1, nl.Type, res)
Cgen(nl, &n1)
- if Ctxt.Arch.Thechar == '5' {
+ if Ctxt.Arch.Family == sys.ARM {
var n2 Node
Nodconst(&n2, nl.Type, 0)
Thearch.Gins(a, &n2, &n1)
- } else if Ctxt.Arch.Thechar == '7' {
+ } else if Ctxt.Arch.Family == sys.ARM64 {
Thearch.Gins(a, &n1, &n1)
} else {
Thearch.Gins(a, nil, &n1)
@@ -452,7 +453,7 @@ func cgen_wb(n, res *Node, wb bool) {
return
}
- if Ctxt.Arch.Thechar == '8' {
+ if Ctxt.Arch.Family == sys.I386 {
var n1 Node
var n2 Node
Tempname(&n2, n.Type)
@@ -465,7 +466,7 @@ func cgen_wb(n, res *Node, wb bool) {
var n1 Node
var n2 Node
- if Ctxt.Arch.Thechar == '5' {
+ if Ctxt.Arch.Family == sys.ARM {
if nl.Addable && !Is64(nl.Type) {
Regalloc(&n1, nl.Type, res)
Thearch.Gmove(nl, &n1)
@@ -707,7 +708,7 @@ sbop: // symmetric binary
abop: // asymmetric binary
var n1 Node
var n2 Node
- if Ctxt.Arch.Thechar == '8' {
+ if Ctxt.Arch.Family == sys.I386 {
// no registers, sigh
if Smallintconst(nr) {
var n1 Node
@@ -751,14 +752,14 @@ abop: // asymmetric binary
Regalloc(&n1, nl.Type, res)
Cgen(nl, &n1)
- if Smallintconst(nr) && Ctxt.Arch.Thechar != '0' && Ctxt.Arch.Thechar != '5' && Ctxt.Arch.Thechar != '7' && Ctxt.Arch.Thechar != '9' { // TODO(rsc): Check opcode for arm
+ if Smallintconst(nr) && Ctxt.Arch.Family != sys.MIPS64 && Ctxt.Arch.Family != sys.ARM && Ctxt.Arch.Family != sys.ARM64 && Ctxt.Arch.Family != sys.PPC64 { // TODO(rsc): Check opcode for arm
n2 = *nr
} else {
Regalloc(&n2, nr.Type, nil)
Cgen(nr, &n2)
}
} else {
- if Smallintconst(nr) && Ctxt.Arch.Thechar != '0' && Ctxt.Arch.Thechar != '5' && Ctxt.Arch.Thechar != '7' && Ctxt.Arch.Thechar != '9' { // TODO(rsc): Check opcode for arm
+ if Smallintconst(nr) && Ctxt.Arch.Family != sys.MIPS64 && Ctxt.Arch.Family != sys.ARM && Ctxt.Arch.Family != sys.ARM64 && Ctxt.Arch.Family != sys.PPC64 { // TODO(rsc): Check opcode for arm
n2 = *nr
} else {
Regalloc(&n2, nr.Type, res)
@@ -876,8 +877,8 @@ func cgen_wbfat(n, res *Node) {
// 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':
+ switch Ctxt.Arch.Family {
+ case sys.AMD64, sys.I386:
// We use sized math, so the result is already truncated.
default:
switch n.Op {
@@ -945,7 +946,7 @@ func Cgenr(n *Node, a *Node, res *Node) {
OCALLINTER:
var n1 Node
Igen(n, &n1, res)
- Regalloc(a, Types[Tptr], &n1)
+ Regalloc(a, n.Type, &n1)
Thearch.Gmove(&n1, a)
Regfree(&n1)
@@ -977,10 +978,14 @@ func Agenr(n *Node, a *Node, res *Node) {
case OIND:
Cgenr(n.Left, a, res)
- Cgen_checknil(a)
+ if !n.Left.NonNil {
+ Cgen_checknil(a)
+ } else if Debug_checknil != 0 && n.Lineno > 1 {
+ Warnl(n.Lineno, "removed nil check")
+ }
case OINDEX:
- if Ctxt.Arch.Thechar == '5' {
+ if Ctxt.Arch.Family == sys.ARM {
var p2 *obj.Prog // to be patched to panicindex.
w := uint32(n.Type.Width)
bounded := Debug['B'] != 0 || n.Bounded
@@ -1127,7 +1132,7 @@ func Agenr(n *Node, a *Node, res *Node) {
Regfree(&n2)
break
}
- if Ctxt.Arch.Thechar == '8' {
+ if Ctxt.Arch.Family == sys.I386 {
var p2 *obj.Prog // to be patched to panicindex.
w := uint32(n.Type.Width)
bounded := Debug['B'] != 0 || n.Bounded
@@ -1586,7 +1591,11 @@ func Agen(n *Node, res *Node) {
case OIND:
Cgen(nl, res)
- Cgen_checknil(res)
+ if !nl.NonNil {
+ Cgen_checknil(res)
+ } else if Debug_checknil != 0 && n.Lineno > 1 {
+ Warnl(n.Lineno, "removed nil check")
+ }
case ODOT:
Agen(nl, res)
@@ -1596,7 +1605,11 @@ func Agen(n *Node, res *Node) {
case ODOTPTR:
Cgen(nl, res)
- Cgen_checknil(res)
+ if !nl.NonNil {
+ Cgen_checknil(res)
+ } else if Debug_checknil != 0 && n.Lineno > 1 {
+ Warnl(n.Lineno, "removed nil check")
+ }
if n.Xoffset != 0 {
addOffset(res, n.Xoffset)
}
@@ -1604,7 +1617,7 @@ func Agen(n *Node, res *Node) {
}
func addOffset(res *Node, offset int64) {
- if Ctxt.Arch.Thechar == '6' || Ctxt.Arch.Thechar == '8' {
+ if Ctxt.Arch.InFamily(sys.AMD64, sys.I386) {
Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), Nodintconst(offset), res)
return
}
@@ -1657,7 +1670,11 @@ func Igen(n *Node, a *Node, res *Node) {
case ODOTPTR:
Cgenr(n.Left, a, res)
- Cgen_checknil(a)
+ if !n.Left.NonNil {
+ Cgen_checknil(a)
+ } else if Debug_checknil != 0 && n.Lineno > 1 {
+ Warnl(n.Lineno, "removed nil check")
+ }
a.Op = OINDREG
a.Xoffset += n.Xoffset
a.Type = n.Type
@@ -1790,7 +1807,7 @@ func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) {
}
if !n.Type.IsBoolean() {
- Fatalf("bgen: bad type %v for %v", n.Type, Oconv(n.Op, 0))
+ Fatalf("bgen: bad type %v for %v", n.Type, n.Op)
}
for n.Op == OCONVNOP {
@@ -1825,13 +1842,14 @@ func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) {
return
case ONAME:
+ // Some architectures 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.
+ mayNeedTemp := Ctxt.Arch.InFamily(sys.ARM, sys.ARM64, sys.MIPS64, sys.PPC64, sys.S390X)
+
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 '0', '5', '7', '9':
- Fatalf("genval 0g, 5g, 7g, 9g ONAMES not fully implemented")
+ if mayNeedTemp {
+ Fatalf("genval ONAMES not fully implemented")
}
Cgen(n, res)
if !wantTrue {
@@ -1840,7 +1858,7 @@ func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) {
return
}
- if n.Addable && Ctxt.Arch.Thechar != '0' && Ctxt.Arch.Thechar != '5' && Ctxt.Arch.Thechar != '7' && Ctxt.Arch.Thechar != '9' {
+ if n.Addable && !mayNeedTemp {
// no need for a temporary
bgenNonZero(n, nil, wantTrue, likely, to)
return
@@ -1977,7 +1995,7 @@ func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) {
return
}
- if Ctxt.Arch.Regsize == 4 && Is64(nr.Type) {
+ 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)
@@ -2015,7 +2033,7 @@ func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) {
Regfree(&n2)
} else {
var n1 Node
- if !nl.Addable && Ctxt.Arch.Thechar == '8' {
+ if !nl.Addable && Ctxt.Arch.Family == sys.I386 {
Tempname(&n1, nl.Type)
} else {
Regalloc(&n1, nl.Type, nil)
@@ -2024,13 +2042,13 @@ func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) {
Cgen(nl, &n1)
nl = &n1
- if Smallintconst(nr) && Ctxt.Arch.Thechar != '0' && Ctxt.Arch.Thechar != '9' {
+ if Smallintconst(nr) && Ctxt.Arch.Family != sys.MIPS64 && Ctxt.Arch.Family != sys.PPC64 {
Thearch.Gins(Thearch.Optoas(OCMP, nr.Type), nl, nr)
bins(nr.Type, res, op, likely, to)
return
}
- if !nr.Addable && Ctxt.Arch.Thechar == '8' {
+ if !nr.Addable && Ctxt.Arch.Family == sys.I386 {
nr = CgenTemp(nr)
}
@@ -2044,13 +2062,13 @@ func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) {
l, r := nl, nr
// On x86, only < and <= work right with NaN; reverse if needed
- if Ctxt.Arch.Thechar == '6' && nl.Type.IsFloat() && (op == OGT || op == OGE) {
+ if Ctxt.Arch.Family == sys.AMD64 && nl.Type.IsFloat() && (op == OGT || op == OGE) {
l, r = r, l
op = Brrev(op)
}
// MIPS does not have CMP instruction
- if Ctxt.Arch.Thechar == '0' {
+ if Ctxt.Arch.Family == sys.MIPS64 {
p := Thearch.Ginscmp(op, nr.Type, l, r, likely)
Patch(p, to)
return
@@ -2062,8 +2080,8 @@ func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) {
// Handle floating point special cases.
// Note that 8g has Bgen_float and is handled above.
if nl.Type.IsFloat() {
- switch Ctxt.Arch.Thechar {
- case '5':
+ switch Ctxt.Arch.Family {
+ case sys.ARM:
if genval {
Fatalf("genval 5g Isfloat special cases not implemented")
}
@@ -2077,7 +2095,7 @@ func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) {
Patch(p, Pc)
}
return
- case '6':
+ case sys.AMD64:
switch n.Op {
case OEQ:
// neither NE nor P
@@ -2111,7 +2129,7 @@ func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) {
}
return
}
- case '7', '9':
+ case sys.ARM64, sys.PPC64:
if genval {
Fatalf("genval 7g, 9g Isfloat special cases not implemented")
}
@@ -2143,7 +2161,7 @@ func bgenNonZero(n, res *Node, wantTrue bool, likely int, to *obj.Prog) {
}
// MIPS does not have CMP instruction
- if Thearch.Thechar == '0' {
+ if Thearch.LinkArch.Family == sys.MIPS64 {
p := Gbranch(Thearch.Optoas(op, n.Type), n.Type, likely)
Naddr(&p.From, n)
Patch(p, to)
@@ -2352,7 +2370,7 @@ func Ginscall(f *Node, proc int) {
// into the instruction stream.
Thearch.Ginsnop()
- if Thearch.Thechar == '9' {
+ if Thearch.LinkArch.Family == sys.PPC64 {
// On ppc64, when compiling Go into position
// independent code on ppc64le we insert an
// instruction to reload the TOC pointer from the
@@ -2361,7 +2379,7 @@ func Ginscall(f *Node, proc int) {
// If the MOVD is not needed, insert a hardware NOP
// so that the same number of instructions are used
// on ppc64 in both shared and non-shared modes.
- if Ctxt.Flag_shared != 0 {
+ if Ctxt.Flag_shared {
p := Thearch.Gins(ppc64.AMOVD, nil, nil)
p.From.Type = obj.TYPE_MEM
p.From.Offset = 24
@@ -2436,7 +2454,7 @@ func Ginscall(f *Node, proc int) {
func cgen_callinter(n *Node, res *Node, proc int) {
i := n.Left
if i.Op != ODOTINTER {
- Fatalf("cgen_callinter: not ODOTINTER %v", Oconv(i.Op, 0))
+ Fatalf("cgen_callinter: not ODOTINTER %v", i.Op)
}
i = i.Left // interface
@@ -2620,24 +2638,70 @@ func cgen_ret(n *Node) {
}
}
+// hasHMUL64 reports whether the architecture supports 64-bit
+// signed and unsigned high multiplication (OHMUL).
+func hasHMUL64() bool {
+ switch Ctxt.Arch.Family {
+ case sys.AMD64, sys.S390X, sys.ARM64:
+ return true
+ case sys.ARM, sys.I386, sys.MIPS64, sys.PPC64:
+ return false
+ }
+ Fatalf("unknown architecture")
+ return false
+}
+
+// hasRROTC64 reports whether the architecture supports 64-bit
+// rotate through carry instructions (ORROTC).
+func hasRROTC64() bool {
+ switch Ctxt.Arch.Family {
+ case sys.AMD64:
+ return true
+ case sys.ARM, sys.ARM64, sys.I386, sys.MIPS64, sys.PPC64, sys.S390X:
+ return false
+ }
+ Fatalf("unknown architecture")
+ return false
+}
+
+func hasRightShiftWithCarry() bool {
+ switch Ctxt.Arch.Family {
+ case sys.ARM64:
+ return true
+ case sys.AMD64, sys.ARM, sys.I386, sys.MIPS64, sys.PPC64, sys.S390X:
+ return false
+ }
+ Fatalf("unknown architecture")
+ return false
+}
+
+func hasAddSetCarry() bool {
+ switch Ctxt.Arch.Family {
+ case sys.ARM64:
+ return true
+ case sys.AMD64, sys.ARM, sys.I386, sys.MIPS64, sys.PPC64, sys.S390X:
+ return false
+ }
+ Fatalf("unknown architecture")
+ return false
+}
+
// generate division according to op, one of:
// res = nl / nr
// res = nl % nr
func cgen_div(op Op, 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 == '0' || Ctxt.Arch.Thechar == '7' || Ctxt.Arch.Thechar == '9' {
+ // Architectures need to support 64-bit high multiplications
+ // (OHMUL) in order to perform divide by constant optimizations.
+ if nr.Op != OLITERAL || !hasHMUL64() {
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
+ // Try to do division using multiplication: (2^w)/d.
+ // See Hacker's Delight, chapter 10.
switch Simtype[nl.Type.Etype] {
default:
goto longdiv
@@ -2650,6 +2714,18 @@ func cgen_div(op Op, nl *Node, nr *Node, res *Node) {
if m.Bad != 0 {
break
}
+
+ // In order to add the numerator we need to be able to
+ // avoid overflow. This is done by shifting the result of the
+ // addition right by 1 and inserting the carry bit into
+ // the MSB. For now this needs the RROTC instruction.
+ // TODO(mundaym): Hacker's Delight 2nd ed. chapter 10 proposes
+ // an alternative sequence of instructions for architectures
+ // (TODO: MIPS64, PPC64, S390X) that do not have a shift
+ // right with carry instruction.
+ if m.Ua != 0 && !hasRROTC64() && !hasRightShiftWithCarry() {
+ goto longdiv
+ }
if op == OMOD {
goto longmod
}
@@ -2663,13 +2739,21 @@ func cgen_div(op Op, nl *Node, nr *Node, res *Node) {
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)
+ // Need to add numerator accounting for overflow.
+ if hasAddSetCarry() {
+ Thearch.AddSetCarry(&n1, &n3, &n3)
+ } else {
+ 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)
+ if !hasRROTC64() {
+ Thearch.RightShiftWithCarry(&n3, uint(m.S), &n3)
+ } else {
+ 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
@@ -2701,7 +2785,7 @@ func cgen_div(op Op, nl *Node, nr *Node, res *Node) {
Thearch.Cgen_hmul(&n1, &n2, &n3)
if m.Sm < 0 {
- // need to add numerator
+ // Need to add numerator (cannot overflow).
Thearch.Gins(Thearch.Optoas(OADD, nl.Type), &n1, &n3)
}
@@ -2714,8 +2798,8 @@ func cgen_div(op Op, nl *Node, nr *Node, res *Node) {
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
+ // This could probably be removed by factoring it into
+ // the multiplier.
Thearch.Gins(Thearch.Optoas(OMINUS, nl.Type), nil, &n3)
}
@@ -2727,14 +2811,14 @@ func cgen_div(op Op, nl *Node, nr *Node, res *Node) {
goto longdiv
- // division and mod using (slow) hardware instruction
+ // 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
+ // 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)
@@ -2744,11 +2828,6 @@ longmod:
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
@@ -2844,7 +2923,7 @@ func cgen_append(n, res *Node) {
arg.Addable = true
arg.Xoffset = Ctxt.FixedFrameSize()
arg.Type = Ptrto(Types[TUINT8])
- Cgen(typename(res.Type), &arg)
+ Cgen(typename(res.Type.Elem()), &arg)
arg.Xoffset += int64(Widthptr)
arg.Type = Types[Tptr]
@@ -2995,7 +3074,7 @@ func cgen_slice(n, res *Node, wb bool) {
regalloc := Regalloc
ginscon := Thearch.Ginscon
gins := Thearch.Gins
- if Thearch.Thechar == '8' {
+ if Thearch.LinkArch.Family == sys.I386 {
regalloc = func(n *Node, t *Type, reuse *Node) {
Tempname(n, t)
}
@@ -3058,15 +3137,7 @@ func cgen_slice(n, res *Node, wb bool) {
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
- }
+ x1, x2, x3 := n.SliceBounds() // unevaluated index arguments
// 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.
@@ -3238,7 +3309,7 @@ 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 Ctxt.Arch.RegSize == 4 && Is64(n1.Type) {
if n1.Val().U.(*Mpint).CmpInt64(1<<31) >= 0 {
Fatalf("missed slice out of bounds check")
}
diff --git a/src/cmd/compile/internal/gc/closure.go b/src/cmd/compile/internal/gc/closure.go
index 80c8d309af..d2cb9ebf1e 100644
--- a/src/cmd/compile/internal/gc/closure.go
+++ b/src/cmd/compile/internal/gc/closure.go
@@ -194,7 +194,7 @@ func makeclosure(func_ *Node) *Node {
xfunc.Nbody.Set(func_.Nbody.Slice())
xfunc.Func.Dcl = append(func_.Func.Dcl, xfunc.Func.Dcl...)
func_.Func.Dcl = nil
- if len(xfunc.Nbody.Slice()) == 0 {
+ if xfunc.Nbody.Len() == 0 {
Fatalf("empty body - won't generate any code")
}
xfunc = typecheck(xfunc, Etop)
@@ -419,7 +419,7 @@ func closuredebugruntimecheck(r *Node) {
Warnl(r.Lineno, "stack closure, captured vars = %v", r.Func.Cvars)
}
}
- if compiling_runtime > 0 && r.Esc == EscHeap {
+ if compiling_runtime && r.Esc == EscHeap {
yyerrorl(r.Lineno, "heap-allocated closure, not allowed in runtime.")
}
}
diff --git a/src/cmd/compile/internal/gc/const.go b/src/cmd/compile/internal/gc/const.go
index 5c9a67c8b5..e0f5e977fe 100644
--- a/src/cmd/compile/internal/gc/const.go
+++ b/src/cmd/compile/internal/gc/const.go
@@ -10,6 +10,59 @@ import (
"strings"
)
+// Ctype describes the constant kind of an "ideal" (untyped) constant.
+type Ctype int8
+
+const (
+ CTxxx Ctype = iota
+
+ CTINT
+ CTRUNE
+ CTFLT
+ CTCPLX
+ CTSTR
+ CTBOOL
+ CTNIL
+)
+
+type Val struct {
+ // U contains one of:
+ // bool bool when n.ValCtype() == CTBOOL
+ // *Mpint int when n.ValCtype() == CTINT, rune when n.ValCtype() == CTRUNE
+ // *Mpflt float when n.ValCtype() == CTFLT
+ // *Mpcplx pair of floats when n.ValCtype() == CTCPLX
+ // string string when n.ValCtype() == CTSTR
+ // *Nilval when n.ValCtype() == CTNIL
+ U interface{}
+}
+
+func (v Val) Ctype() Ctype {
+ switch x := v.U.(type) {
+ default:
+ Fatalf("unexpected Ctype for %T", v.U)
+ panic("not reached")
+ case nil:
+ return 0
+ case *NilVal:
+ return CTNIL
+ case bool:
+ return CTBOOL
+ case *Mpint:
+ if x.Rune {
+ return CTRUNE
+ }
+ return CTINT
+ case *Mpflt:
+ return CTFLT
+ case *Mpcplx:
+ return CTCPLX
+ case string:
+ return CTSTR
+ }
+}
+
+type NilVal struct{}
+
// IntLiteral returns the Node's literal value as an integer.
func (n *Node) IntLiteral() (x int64, ok bool) {
switch {
@@ -173,16 +226,13 @@ func convlit1(n *Node, t *Type, explicit bool, reuse canReuseNode) *Node {
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:
+ // If trying to convert to non-complex type,
+ // leave as complex128 and let typechecker complain.
t = Types[TCOMPLEX128]
fallthrough
-
- //fallthrough
case TCOMPLEX128:
n.Type = t
-
n.Left = convlit(n.Left, Types[TFLOAT64])
n.Right = convlit(n.Right, Types[TFLOAT64])
@@ -231,9 +281,7 @@ func convlit1(n *Node, t *Type, explicit bool, reuse canReuseNode) *Node {
return n
case TARRAY:
- if !t.IsSlice() {
- goto bad
- }
+ goto bad
case TPTR32,
TPTR64,
@@ -241,6 +289,7 @@ func convlit1(n *Node, t *Type, explicit bool, reuse canReuseNode) *Node {
TMAP,
TCHAN,
TFUNC,
+ TSLICE,
TUNSAFEPTR:
break
@@ -326,22 +375,22 @@ bad:
}
func copyval(v Val) Val {
- switch v.Ctype() {
- case CTINT, CTRUNE:
+ switch u := v.U.(type) {
+ case *Mpint:
i := new(Mpint)
- i.Set(v.U.(*Mpint))
- i.Rune = v.U.(*Mpint).Rune
+ i.Set(u)
+ i.Rune = u.Rune
v.U = i
- case CTFLT:
+ case *Mpflt:
f := newMpflt()
- f.Set(v.U.(*Mpflt))
+ f.Set(u)
v.U = f
- case CTCPLX:
+ case *Mpcplx:
c := new(Mpcplx)
- c.Real.Set(&v.U.(*Mpcplx).Real)
- c.Imag.Set(&v.U.(*Mpcplx).Imag)
+ c.Real.Set(&u.Real)
+ c.Imag.Set(&u.Imag)
v.U = c
}
@@ -349,16 +398,16 @@ func copyval(v Val) Val {
}
func tocplx(v Val) Val {
- switch v.Ctype() {
- case CTINT, CTRUNE:
+ switch u := v.U.(type) {
+ case *Mpint:
c := new(Mpcplx)
- c.Real.SetInt(v.U.(*Mpint))
+ c.Real.SetInt(u)
c.Imag.SetFloat64(0.0)
v.U = c
- case CTFLT:
+ case *Mpflt:
c := new(Mpcplx)
- c.Real.Set(v.U.(*Mpflt))
+ c.Real.Set(u)
c.Imag.SetFloat64(0.0)
v.U = c
}
@@ -367,17 +416,17 @@ func tocplx(v Val) Val {
}
func toflt(v Val) Val {
- switch v.Ctype() {
- case CTINT, CTRUNE:
+ switch u := v.U.(type) {
+ case *Mpint:
f := newMpflt()
- f.SetInt(v.U.(*Mpint))
+ f.SetInt(u)
v.U = f
- case CTCPLX:
+ case *Mpcplx:
f := newMpflt()
- f.Set(&v.U.(*Mpcplx).Real)
- if v.U.(*Mpcplx).Imag.CmpFloat64(0) != 0 {
- Yyerror("constant %v%vi truncated to real", Fconv(&v.U.(*Mpcplx).Real, FmtSharp), Fconv(&v.U.(*Mpcplx).Imag, FmtSharp|FmtSign))
+ f.Set(&u.Real)
+ if u.Imag.CmpFloat64(0) != 0 {
+ Yyerror("constant %v%vi truncated to real", Fconv(&u.Real, FmtSharp), Fconv(&u.Imag, FmtSharp|FmtSign))
}
v.U = f
}
@@ -386,31 +435,33 @@ func toflt(v Val) Val {
}
func toint(v Val) Val {
- switch v.Ctype() {
- case CTRUNE:
- i := new(Mpint)
- i.Set(v.U.(*Mpint))
- v.U = i
+ switch u := v.U.(type) {
+ case *Mpint:
+ if u.Rune {
+ i := new(Mpint)
+ i.Set(u)
+ v.U = i
+ }
- case CTFLT:
+ case *Mpflt:
i := new(Mpint)
- if f := v.U.(*Mpflt); i.SetFloat(f) < 0 {
+ if i.SetFloat(u) < 0 {
msg := "constant %v truncated to integer"
// provide better error message if SetFloat failed because f was too large
- if f.Val.IsInt() {
+ if u.Val.IsInt() {
msg = "constant %v overflows integer"
}
- Yyerror(msg, Fconv(f, FmtSharp))
+ Yyerror(msg, Fconv(u, FmtSharp))
}
v.U = i
- case CTCPLX:
+ case *Mpcplx:
i := new(Mpint)
- if i.SetFloat(&v.U.(*Mpcplx).Real) < 0 {
- Yyerror("constant %v%vi truncated to integer", Fconv(&v.U.(*Mpcplx).Real, FmtSharp), Fconv(&v.U.(*Mpcplx).Imag, FmtSharp|FmtSign))
+ if i.SetFloat(&u.Real) < 0 {
+ Yyerror("constant %v%vi truncated to integer", Fconv(&u.Real, FmtSharp), Fconv(&u.Imag, FmtSharp|FmtSign))
}
- if v.U.(*Mpcplx).Imag.CmpFloat64(0) != 0 {
- Yyerror("constant %v%vi truncated to real", Fconv(&v.U.(*Mpcplx).Real, FmtSharp), Fconv(&v.U.(*Mpcplx).Imag, FmtSharp|FmtSign))
+ if u.Imag.CmpFloat64(0) != 0 {
+ Yyerror("constant %v%vi truncated to real", Fconv(&u.Real, FmtSharp), Fconv(&u.Imag, FmtSharp|FmtSign))
}
v.U = i
}
@@ -419,30 +470,25 @@ func toint(v Val) Val {
}
func doesoverflow(v Val, t *Type) bool {
- switch v.Ctype() {
- case CTINT, CTRUNE:
+ switch u := v.U.(type) {
+ case *Mpint:
if !t.IsInteger() {
Fatalf("overflow: %v integer constant", t)
}
- if v.U.(*Mpint).Cmp(Minintval[t.Etype]) < 0 || v.U.(*Mpint).Cmp(Maxintval[t.Etype]) > 0 {
- return true
- }
+ return u.Cmp(Minintval[t.Etype]) < 0 || u.Cmp(Maxintval[t.Etype]) > 0
- case CTFLT:
+ case *Mpflt:
if !t.IsFloat() {
Fatalf("overflow: %v floating-point constant", t)
}
- if v.U.(*Mpflt).Cmp(minfltval[t.Etype]) <= 0 || v.U.(*Mpflt).Cmp(maxfltval[t.Etype]) >= 0 {
- return true
- }
+ return u.Cmp(minfltval[t.Etype]) <= 0 || u.Cmp(maxfltval[t.Etype]) >= 0
- case CTCPLX:
+ case *Mpcplx:
if !t.IsComplex() {
Fatalf("overflow: %v complex constant", t)
}
- if v.U.(*Mpcplx).Real.Cmp(minfltval[t.Etype]) <= 0 || v.U.(*Mpcplx).Real.Cmp(maxfltval[t.Etype]) >= 0 || v.U.(*Mpcplx).Imag.Cmp(minfltval[t.Etype]) <= 0 || v.U.(*Mpcplx).Imag.Cmp(maxfltval[t.Etype]) >= 0 {
- return true
- }
+ return u.Real.Cmp(minfltval[t.Etype]) <= 0 || u.Real.Cmp(maxfltval[t.Etype]) >= 0 ||
+ u.Imag.Cmp(minfltval[t.Etype]) <= 0 || u.Imag.Cmp(maxfltval[t.Etype]) >= 0
}
return false
@@ -466,21 +512,16 @@ func overflow(v Val, t *Type) {
}
func tostr(v Val) Val {
- switch v.Ctype() {
- case CTINT, CTRUNE:
+ switch u := v.U.(type) {
+ case *Mpint:
var i int64 = 0xFFFD
- if u := v.U.(*Mpint); u.Cmp(Minintval[TUINT32]) >= 0 && u.Cmp(Maxintval[TUINT32]) <= 0 {
+ if u.Cmp(Minintval[TUINT32]) >= 0 && u.Cmp(Maxintval[TUINT32]) <= 0 {
i = u.Int64()
}
- v = Val{}
v.U = string(i)
- case CTFLT:
- Yyerror("no float -> string")
- fallthrough
-
- case CTNIL:
- v = Val{}
+ case *NilVal:
+ // Can happen because of string([]byte(nil)).
v.U = ""
}
@@ -654,7 +695,7 @@ func evconst(n *Node) {
switch uint32(n.Op)<<16 | uint32(v.Ctype()) {
default:
if n.Diag == 0 {
- Yyerror("illegal constant expression %v %v", Oconv(n.Op, 0), nl.Type)
+ Yyerror("illegal constant expression %v %v", n.Op, nl.Type)
n.Diag = 1
}
return
@@ -667,8 +708,6 @@ func evconst(n *Node) {
break
}
fallthrough
-
- // fall through
case OCONV_ | CTINT_,
OCONV_ | CTRUNE_,
OCONV_ | CTFLT_,
@@ -1140,7 +1179,7 @@ setfalse:
illegal:
if n.Diag == 0 {
- Yyerror("illegal constant expression: %v %v %v", nl.Type, Oconv(n.Op, 0), nr.Type)
+ Yyerror("illegal constant expression: %v %v %v", nl.Type, n.Op, nr.Type)
n.Diag = 1
}
}
diff --git a/src/cmd/compile/internal/gc/cplx.go b/src/cmd/compile/internal/gc/cplx.go
index b0fa70b0ad..9bb2027520 100644
--- a/src/cmd/compile/internal/gc/cplx.go
+++ b/src/cmd/compile/internal/gc/cplx.go
@@ -89,8 +89,9 @@ func subnode(nr *Node, ni *Node, nc *Node) {
t := Types[tc]
if nc.Op == OLITERAL {
- nodfconst(nr, t, &nc.Val().U.(*Mpcplx).Real)
- nodfconst(ni, t, &nc.Val().U.(*Mpcplx).Imag)
+ u := nc.Val().U.(*Mpcplx)
+ nodfconst(nr, t, &u.Real)
+ nodfconst(ni, t, &u.Imag)
return
}
@@ -398,7 +399,7 @@ func Complexgen(n *Node, res *Node) {
switch n.Op {
default:
Dump("complexgen: unknown op", n)
- Fatalf("complexgen: unknown op %v", Oconv(n.Op, 0))
+ Fatalf("complexgen: unknown op %v", n.Op)
case ODOT,
ODOTPTR,
@@ -457,7 +458,7 @@ func Complexgen(n *Node, res *Node) {
switch n.Op {
default:
- Fatalf("complexgen: unknown op %v", Oconv(n.Op, 0))
+ Fatalf("complexgen: unknown op %v", n.Op)
case OCONV:
Complexmove(nl, res)
diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go
index bd5a1f6f07..6d46d9a73c 100644
--- a/src/cmd/compile/internal/gc/dcl.go
+++ b/src/cmd/compile/internal/gc/dcl.go
@@ -11,21 +11,29 @@ import (
"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
+
+var externdcl []*Node
-// declaration stack & operations
-func dcopy(a *Sym, b *Sym) {
+var blockgen int32 // max block number
+
+var block int32 // current block number
+
+// dclstack maintains a stack of shadowed symbol declarations so that
+// popdcl can restore their declarations when a block scope ends.
+// The stack is maintained as a linked list, using Sym's Link field.
+//
+// In practice, the "stack" actually ends up forming a tree: goto and label
+// statements record the current state of dclstack so that checkgoto can
+// validate that a goto statement does not jump over any declarations or
+// into a new block scope.
+//
+// Finally, the Syms in this list are not "real" Syms as they don't actually
+// represent object names. Sym is just a convenient type for saving shadowed
+// Sym definitions, and only a subset of its fields are actually used.
+var dclstack *Sym
+
+func dcopy(a, b *Sym) {
a.Pkg = b.Pkg
a.Name = b.Name
a.Def = b.Def
@@ -41,15 +49,16 @@ func push() *Sym {
return d
}
+// pushdcl pushes the current declaration for symbol s (if any) so that
+// it can be shadowed by a new declaration within a nested block scope.
func pushdcl(s *Sym) *Sym {
d := push()
dcopy(d, s)
- if dflag() {
- fmt.Printf("\t%v push %v %p\n", linestr(lineno), s, s.Def)
- }
return d
}
+// popdcl pops the innermost block scope and restores all symbol declarations
+// to their previous state.
func popdcl() {
d := dclstack
for ; d != nil && d.Name != ""; d = d.Link {
@@ -57,9 +66,6 @@ func popdcl() {
lno := s.Lastlineno
dcopy(s, d)
d.Lastlineno = lno
- if dflag() {
- fmt.Printf("\t%v pop %v %p\n", linestr(lineno), s, s.Def)
- }
}
if d == nil {
@@ -70,6 +76,7 @@ func popdcl() {
block = d.Block
}
+// markdcl records the start of a new block scope for declarations.
func markdcl() {
d := push()
d.Name = "" // used as a mark in fifo
@@ -104,6 +111,7 @@ func testdclstack() {
}
}
+// redeclare emits a diagnostic about symbol s being redeclared somewhere.
func redeclare(s *Sym, where string) {
if s.Lastlineno == 0 {
var tmp string
@@ -137,6 +145,8 @@ var vargen int
var declare_typegen int
+// declare records that Node n declares symbol n.Sym in the specified
+// declaration context.
func declare(n *Node, ctxt Class) {
if ctxt == PDISCARD {
return
@@ -165,9 +175,6 @@ func declare(n *Node, ctxt Class) {
gen := 0
if ctxt == PEXTERN {
externdcl = append(externdcl, n)
- if dflag() {
- fmt.Printf("\t%v global decl %v %p\n", linestr(lineno), s, n)
- }
} else {
if Curfn == nil && ctxt == PAUTO {
Fatalf("automatic outside function")
@@ -318,8 +325,7 @@ func constiter(vl []*Node, t *Node, cl []*Node) []*Node {
return vv
}
-// this generates a new name node,
-// typically for labels or other one-off names.
+// newname returns a new ONAME Node associated with symbol s.
func newname(s *Sym) *Node {
if s == nil {
Fatalf("newname nil")
@@ -364,17 +370,14 @@ func typenod(t *Type) *Node {
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.
+// oldname returns the Node that declares symbol s in the current scope.
+// If no such Node currently exists, an ONONAME Node is returned instead.
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.
+ // Maybe a top-level declaration will come along later to
+ // define s. resolve will check s.Def again once all input
+ // source has been processed.
n = newname(s)
n.Op = ONONAME
n.Name.Iota = iota_ // save current iota value in const declarations
@@ -548,7 +551,7 @@ func funchdr(n *Node) {
func funcargs(nt *Node) {
if nt.Op != OTFUNC {
- Fatalf("funcargs %v", Oconv(nt.Op, 0))
+ Fatalf("funcargs %v", nt.Op)
}
// re-start the variable generation number
@@ -562,7 +565,7 @@ func funcargs(nt *Node) {
if nt.Left != nil {
n := nt.Left
if n.Op != ODCLFIELD {
- Fatalf("funcargs receiver %v", Oconv(n.Op, 0))
+ Fatalf("funcargs receiver %v", n.Op)
}
if n.Left != nil {
n.Left.Op = ONAME
@@ -577,7 +580,7 @@ func funcargs(nt *Node) {
for _, n := range nt.List.Slice() {
if n.Op != ODCLFIELD {
- Fatalf("funcargs in %v", Oconv(n.Op, 0))
+ Fatalf("funcargs in %v", n.Op)
}
if n.Left != nil {
n.Left.Op = ONAME
@@ -595,7 +598,7 @@ func funcargs(nt *Node) {
var i int = 0
for _, n := range nt.Rlist.Slice() {
if n.Op != ODCLFIELD {
- Fatalf("funcargs out %v", Oconv(n.Op, 0))
+ Fatalf("funcargs out %v", n.Op)
}
if n.Left == nil {
@@ -716,10 +719,10 @@ func checkembeddedtype(t *Type) {
}
}
- if t.IsPtr() {
+ if t.IsPtr() || t.IsUnsafePtr() {
Yyerror("embedded type cannot be a pointer")
- } else if t.Etype == TFORW && t.Embedlineno == 0 {
- t.Embedlineno = lineno
+ } else if t.Etype == TFORW && t.ForwardType().Embedlineno == 0 {
+ t.ForwardType().Embedlineno = lineno
}
}
@@ -752,17 +755,13 @@ func structfield(n *Node) *Field {
f.Broke = true
}
- switch n.Val().Ctype() {
- case CTSTR:
- f.Note = new(string)
- *f.Note = n.Val().U.(string)
-
+ switch u := n.Val().U.(type) {
+ case string:
+ f.Note = u
default:
Yyerror("field annotation must be string")
- fallthrough
-
- case CTxxx:
- f.Note = nil
+ case nil:
+ // noop
}
if n.Left != nil && n.Left.Op == ONAME {
@@ -830,7 +829,7 @@ func tostruct0(t *Type, l []*Node) {
func tofunargs(l []*Node) *Type {
t := typ(TSTRUCT)
- t.Funarg = true
+ t.StructType().Funarg = true
fields := make([]*Field, len(l))
for i, n := range l {
@@ -1036,11 +1035,11 @@ func functype0(t *Type, this *Node, in, out []*Node) {
t.Broke = true
}
- t.Outnamed = false
+ t.FuncType().Outnamed = false
if len(out) > 0 && out[0].Left != nil && out[0].Left.Orig != nil {
s := out[0].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 = true
+ t.FuncType().Outnamed = true
}
}
}
@@ -1305,7 +1304,7 @@ func makefuncsym(s *Sym) {
if isblanksym(s) {
return
}
- if compiling_runtime != 0 && s.Name == "getg" {
+ if compiling_runtime && s.Name == "getg" {
// runtime.getg() is not a real function and so does
// not get a funcsym.
return
@@ -1415,7 +1414,7 @@ func (c *nowritebarrierrecChecker) visitcall(n *Node) {
if fn == nil || fn.Op != ONAME || fn.Class != PFUNC || fn.Name.Defn == nil {
return
}
- if (compiling_runtime != 0 || fn.Sym.Pkg == Runtimepkg) && fn.Sym.Name == "allocm" {
+ if (compiling_runtime || fn.Sym.Pkg == Runtimepkg) && fn.Sym.Name == "allocm" {
return
}
defn := fn.Name.Defn
diff --git a/src/cmd/compile/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go
index 9b8f134178..52c09e47f9 100644
--- a/src/cmd/compile/internal/gc/esc.go
+++ b/src/cmd/compile/internal/gc/esc.go
@@ -522,7 +522,7 @@ func escfunc(e *EscState, func_ *Node) {
if ln.Type != nil && !haspointers(ln.Type) {
break
}
- if len(Curfn.Nbody.Slice()) == 0 && !Curfn.Noescape {
+ if Curfn.Nbody.Len() == 0 && !Curfn.Noescape {
ln.Esc = EscHeap
} else {
ln.Esc = EscNone // prime for escflood later
@@ -998,8 +998,8 @@ func escassign(e *EscState, dst, src *Node, step *EscStep) {
if Debug['m'] > 2 {
fmt.Printf("%v:[%d] %v escassign: %v(%v)[%v] = %v(%v)[%v]\n",
linestr(lineno), e.loopdepth, funcSym(Curfn),
- Nconv(dst, FmtShort), Jconv(dst, FmtShort), Oconv(dst.Op, 0),
- Nconv(src, FmtShort), Jconv(src, FmtShort), Oconv(src.Op, 0))
+ Nconv(dst, FmtShort), Jconv(dst, FmtShort), dst.Op,
+ Nconv(src, FmtShort), Jconv(src, FmtShort), src.Op)
}
setlineno(dst)
@@ -1181,7 +1181,7 @@ func escassign(e *EscState, dst, src *Node, step *EscStep) {
var tags [1 << (bitsPerOutputInTag + EscReturnBits)]string
// mktag returns the string representation for an escape analysis tag.
-func mktag(mask int) *string {
+func mktag(mask int) string {
switch mask & EscMask {
case EscNone, EscReturn:
break
@@ -1191,22 +1191,22 @@ func mktag(mask int) *string {
}
if mask < len(tags) && tags[mask] != "" {
- return &tags[mask]
+ return tags[mask]
}
s := fmt.Sprintf("esc:0x%x", mask)
if mask < len(tags) {
tags[mask] = s
}
- return &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:") {
+func parsetag(note string) uint16 {
+ if !strings.HasPrefix(note, "esc:") {
return EscUnknown
}
- n, _ := strconv.ParseInt((*note)[4:], 0, 0)
+ n, _ := strconv.ParseInt(note[4:], 0, 0)
em := uint16(n)
if em == 0 {
return EscNone
@@ -1268,7 +1268,7 @@ func describeEscape(em uint16) string {
// 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 Nodes, src *Node) uint16 {
+func escassignfromtag(e *EscState, note string, dsts Nodes, src *Node) uint16 {
em := parsetag(note)
if src.Op == OLITERAL {
return em
@@ -1435,7 +1435,7 @@ func esccall(e *EscState, n *Node, up *Node) {
ll := n.List
if n.List.Len() == 1 {
a := n.List.First()
- if a.Type.IsStruct() && a.Type.Funarg { // f(g()).
+ if a.Type.IsFuncArgStruct() { // f(g())
ll = e.nodeEscState(a).Escretval
}
}
@@ -1469,7 +1469,7 @@ func esccall(e *EscState, n *Node, up *Node) {
nE := e.nodeEscState(n)
if fn != nil && fn.Op == ONAME && fn.Class == PFUNC &&
- fn.Name.Defn != nil && len(fn.Name.Defn.Nbody.Slice()) != 0 && fn.Name.Param.Ntype != nil && fn.Name.Defn.Esc < EscFuncTagged {
+ fn.Name.Defn != nil && fn.Name.Defn.Nbody.Len() != 0 && fn.Name.Param.Ntype != nil && fn.Name.Defn.Esc < EscFuncTagged {
if Debug['m'] > 3 {
fmt.Printf("%v::esccall:: %v in recursive group\n", linestr(lineno), Nconv(n, FmtShort))
}
@@ -1741,7 +1741,7 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep,
if Debug['m'] > 2 {
fmt.Printf("escwalk: level:%d depth:%d %.*s op=%v %v(%v) scope:%v[%d] extraloopdepth=%v\n",
- level, e.pdepth, e.pdepth, "\t\t\t\t\t\t\t\t\t\t", Oconv(src.Op, 0), Nconv(src, FmtShort), Jconv(src, FmtShort), e.curfnSym(src), srcE.Escloopdepth, extraloopdepth)
+ level, e.pdepth, e.pdepth, "\t\t\t\t\t\t\t\t\t\t", src.Op, Nconv(src, FmtShort), Jconv(src, FmtShort), e.curfnSym(src), srcE.Escloopdepth, extraloopdepth)
}
e.pdepth++
@@ -1969,7 +1969,7 @@ func esctag(e *EscState, func_ *Node) {
// External functions are assumed unsafe,
// unless //go:noescape is given before the declaration.
- if len(func_.Nbody.Slice()) == 0 {
+ if func_.Nbody.Len() == 0 {
if func_.Noescape {
for _, t := range func_.Type.Params().Fields().Slice() {
if haspointers(t.Type) {
@@ -1997,7 +1997,7 @@ func esctag(e *EscState, func_ *Node) {
}
Warnl(func_.Lineno, "%v assuming %v is unsafe uintptr", funcSym(func_), name)
}
- t.Note = &unsafeUintptrTag
+ t.Note = unsafeUintptrTag
}
}
diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go
index 9fc6e56275..4b48c53b91 100644
--- a/src/cmd/compile/internal/gc/export.go
+++ b/src/cmd/compile/internal/gc/export.go
@@ -7,7 +7,7 @@ package gc
import (
"bufio"
"bytes"
- "cmd/internal/obj"
+ "cmd/internal/bio"
"fmt"
"sort"
"unicode"
@@ -15,8 +15,8 @@ import (
)
var (
- newexport int // if set, use new export format
- Debug_export int // if set, print debugging information about export data
+ newexport bool // if set, use new export format
+ Debug_export int // if set, print debugging information about export data
exportsize int
)
@@ -203,7 +203,7 @@ func reexportdep(n *Node) {
t := n.Type
switch t.Etype {
- case TARRAY, TCHAN, TPTR32, TPTR64:
+ case TARRAY, TCHAN, TPTR32, TPTR64, TSLICE:
if t.Sym == nil {
t = t.Elem()
}
@@ -252,7 +252,7 @@ func dumpexportvar(s *Sym) {
dumpexporttype(t)
if t.Etype == TFUNC && n.Class == PFUNC {
- if n.Func != nil && len(n.Func.Inl.Slice()) != 0 {
+ if n.Func != nil && n.Func.Inl.Len() != 0 {
// 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 {
@@ -303,7 +303,7 @@ func dumpexporttype(t *Type) {
case TMAP:
dumpexporttype(t.Val())
dumpexporttype(t.Key())
- case TARRAY, TCHAN, TPTR32, TPTR64:
+ case TARRAY, TCHAN, TPTR32, TPTR64, TSLICE:
dumpexporttype(t.Elem())
}
@@ -323,7 +323,7 @@ func dumpexporttype(t *Type) {
if f.Nointerface {
exportf("\t//go:nointerface\n")
}
- if f.Type.Nname() != nil && len(f.Type.Nname().Func.Inl.Slice()) != 0 { // nname was set by caninl
+ if f.Type.Nname() != nil && f.Type.Nname().Func.Inl.Len() != 0 { // 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
@@ -354,7 +354,7 @@ func dumpsym(s *Sym) {
switch s.Def.Op {
default:
- Yyerror("unexpected export symbol: %v %v", Oconv(s.Def.Op, 0), s)
+ Yyerror("unexpected export symbol: %v %v", s.Def.Op, s)
case OLITERAL:
dumpexportconst(s)
@@ -377,15 +377,15 @@ func dumpexport() {
}
size := 0 // size of export section without enclosing markers
- if forceNewExport || newexport != 0 {
+ if newexport {
// binary export
// The linker also looks for the $$ marker - use char after $$ to distinguish format.
exportf("\n$$B\n") // indicate binary format
if debugFormat {
// save a copy of the export data
var copy bytes.Buffer
- bcopy := obj.Binitw(&copy)
- size = Export(bcopy, Debug_export != 0)
+ bcopy := bufio.NewWriter(&copy)
+ size = export(bcopy, Debug_export != 0)
bcopy.Flush() // flushing to bytes.Buffer cannot fail
if n, err := bout.Write(copy.Bytes()); n != size || err != nil {
Fatalf("error writing export data: got %d bytes, want %d bytes, err = %v", n, size, err)
@@ -407,7 +407,7 @@ func dumpexport() {
pkgs = savedPkgs
pkgMap = savedPkgMap
} else {
- size = Export(bout, Debug_export != 0)
+ size = export(bout.Writer, Debug_export != 0)
}
exportf("\n$$\n")
} else {
@@ -417,7 +417,7 @@ func dumpexport() {
exportf("\n$$\n") // indicate textual format
exportsize = 0
exportf("package %s", localpkg.Name)
- if safemode != 0 {
+ if safemode {
exportf(" safe")
}
exportf("\n")
@@ -577,7 +577,7 @@ func importtype(pt *Type, t *Type) {
}
func dumpasmhdr() {
- b, err := obj.Bopenw(asmhdr)
+ b, err := bio.Create(asmhdr)
if err != nil {
Fatalf("%v", err)
}
@@ -592,7 +592,7 @@ func dumpasmhdr() {
case OTYPE:
t := n.Type
- if !t.IsStruct() || t.Map != nil || t.Funarg {
+ if !t.IsStruct() || t.StructType().Map != nil || t.IsFuncArgStruct() {
break
}
fmt.Fprintf(b, "#define %s__size %d\n", t.Sym.Name, int(t.Width))
@@ -604,5 +604,5 @@ func dumpasmhdr() {
}
}
- obj.Bterm(b)
+ b.Close()
}
diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go
index ab9bad3c2a..2c3afb0ecc 100644
--- a/src/cmd/compile/internal/gc/fmt.go
+++ b/src/cmd/compile/internal/gc/fmt.go
@@ -193,7 +193,7 @@ var goopnames = []string{
}
// Fmt "%O": Node opcodes
-func Oconv(o Op, flag FmtFlag) string {
+func oconv(o Op, flag FmtFlag) string {
if (flag&FmtSharp != 0) || fmtmode != FDbg {
if o >= 0 && int(o) < len(goopnames) && goopnames[o] != "" {
return goopnames[o]
@@ -319,6 +319,12 @@ func Jconv(n *Node, flag FmtFlag) string {
if n.Assigned {
buf.WriteString(" assigned")
}
+ if n.Bounded {
+ buf.WriteString(" bounded")
+ }
+ if n.NonNil {
+ buf.WriteString(" nonnil")
+ }
if c == 0 && n.Used {
fmt.Fprintf(&buf, " used(%v)", n.Used)
@@ -328,16 +334,17 @@ func Jconv(n *Node, flag FmtFlag) string {
// Fmt "%V": Values
func Vconv(v Val, flag FmtFlag) string {
- switch v.Ctype() {
- case CTINT:
- if (flag&FmtSharp != 0) || fmtmode == FExp {
- return Bconv(v.U.(*Mpint), FmtSharp)
+ switch u := v.U.(type) {
+ case *Mpint:
+ if !u.Rune {
+ if (flag&FmtSharp != 0) || fmtmode == FExp {
+ return Bconv(u, FmtSharp)
+ }
+ return Bconv(u, 0)
}
- return Bconv(v.U.(*Mpint), 0)
- case CTRUNE:
- x := v.U.(*Mpint).Int64()
- if ' ' <= x && x < 0x80 && x != '\\' && x != '\'' {
+ x := u.Int64()
+ if ' ' <= x && x < utf8.RuneSelf && x != '\\' && x != '\'' {
return fmt.Sprintf("'%c'", int(x))
}
if 0 <= x && x < 1<<16 {
@@ -346,39 +353,39 @@ func Vconv(v Val, flag FmtFlag) string {
if 0 <= x && x <= utf8.MaxRune {
return fmt.Sprintf("'\\U%08x'", uint64(x))
}
- return fmt.Sprintf("('\\x00' + %v)", v.U.(*Mpint))
+ return fmt.Sprintf("('\\x00' + %v)", u)
- case CTFLT:
+ case *Mpflt:
if (flag&FmtSharp != 0) || fmtmode == FExp {
- return Fconv(v.U.(*Mpflt), 0)
+ return Fconv(u, 0)
}
- return Fconv(v.U.(*Mpflt), FmtSharp)
+ return Fconv(u, FmtSharp)
- case CTCPLX:
+ case *Mpcplx:
if (flag&FmtSharp != 0) || fmtmode == FExp {
- return fmt.Sprintf("(%v+%vi)", &v.U.(*Mpcplx).Real, &v.U.(*Mpcplx).Imag)
+ return fmt.Sprintf("(%v+%vi)", &u.Real, &u.Imag)
}
if v.U.(*Mpcplx).Real.CmpFloat64(0) == 0 {
- return fmt.Sprintf("%vi", Fconv(&v.U.(*Mpcplx).Imag, FmtSharp))
+ return fmt.Sprintf("%vi", Fconv(&u.Imag, FmtSharp))
}
if v.U.(*Mpcplx).Imag.CmpFloat64(0) == 0 {
- return Fconv(&v.U.(*Mpcplx).Real, FmtSharp)
+ return Fconv(&u.Real, FmtSharp)
}
if v.U.(*Mpcplx).Imag.CmpFloat64(0) < 0 {
- return fmt.Sprintf("(%v%vi)", Fconv(&v.U.(*Mpcplx).Real, FmtSharp), Fconv(&v.U.(*Mpcplx).Imag, FmtSharp))
+ return fmt.Sprintf("(%v%vi)", Fconv(&u.Real, FmtSharp), Fconv(&u.Imag, FmtSharp))
}
- return fmt.Sprintf("(%v+%vi)", Fconv(&v.U.(*Mpcplx).Real, FmtSharp), Fconv(&v.U.(*Mpcplx).Imag, FmtSharp))
+ return fmt.Sprintf("(%v+%vi)", Fconv(&u.Real, FmtSharp), Fconv(&u.Imag, FmtSharp))
- case CTSTR:
- return strconv.Quote(v.U.(string))
+ case string:
+ return strconv.Quote(u)
- case CTBOOL:
- if v.U.(bool) {
+ case bool:
+ if u {
return "true"
}
return "false"
- case CTNIL:
+ case *NilVal:
return "nil"
}
@@ -416,6 +423,7 @@ var etnames = []string{
TPTR64: "PTR64",
TFUNC: "FUNC",
TARRAY: "ARRAY",
+ TSLICE: "SLICE",
TSTRUCT: "STRUCT",
TCHAN: "CHAN",
TMAP: "MAP",
@@ -445,6 +453,10 @@ func (e EType) String() string {
return Econv(e)
}
+func (o Op) String() string {
+ return oconv(o, 0)
+}
+
// Fmt "%S": syms
func symfmt(s *Sym, flag FmtFlag) string {
if s.Pkg != nil && flag&FmtShort == 0 {
@@ -574,7 +586,7 @@ func typefmt(t *Type, flag FmtFlag) string {
if fmtmode == FDbg {
fmtmode = 0
- str := Econv(t.Etype) + "-" + typefmt(t, flag)
+ str := t.Etype.String() + "-" + typefmt(t, flag)
fmtmode = FDbg
return str
}
@@ -587,12 +599,12 @@ func typefmt(t *Type, flag FmtFlag) string {
return "*" + t.Elem().String()
case TARRAY:
- if t.IsArray() {
- return fmt.Sprintf("[%d]%v", t.NumElem(), t.Elem())
- }
if t.isDDDArray() {
return "[...]" + t.Elem().String()
}
+ return fmt.Sprintf("[%d]%v", t.NumElem(), t.Elem())
+
+ case TSLICE:
return "[]" + t.Elem().String()
case TCHAN:
@@ -671,26 +683,27 @@ func typefmt(t *Type, flag FmtFlag) string {
return buf.String()
case TSTRUCT:
- if t.Map != nil {
+ if m := t.StructType().Map; m != nil {
+ mt := m.MapType()
// 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 "map.bucket[" + t.Map.Key().String() + "]" + t.Map.Val().String()
+ if mt.Bucket == t {
+ return "map.bucket[" + m.Key().String() + "]" + m.Val().String()
}
- if t.Map.Hmap == t {
- return "map.hdr[" + t.Map.Key().String() + "]" + t.Map.Val().String()
+ if mt.Hmap == t {
+ return "map.hdr[" + m.Key().String() + "]" + m.Val().String()
}
- if t.Map.Hiter == t {
- return "map.iter[" + t.Map.Key().String() + "]" + t.Map.Val().String()
+ if mt.Hiter == t {
+ return "map.iter[" + m.Key().String() + "]" + m.Val().String()
}
Yyerror("unknown internal map type")
}
var buf bytes.Buffer
- if t.Funarg {
+ if t.IsFuncArgStruct() {
buf.WriteString("(")
var flag1 FmtFlag
if fmtmode == FTypeId || fmtmode == FErr { // no argument names on function signature, and no "noescape"/"nosplit" tags
@@ -735,15 +748,18 @@ func typefmt(t *Type, flag FmtFlag) string {
if fmtmode == FExp {
Fatalf("cannot use TDDDFIELD with old exporter")
}
- return fmt.Sprintf("%v <%v> %v", Econv(t.Etype), t.Sym, t.Wrapped())
+ return fmt.Sprintf("%v <%v> %v", t.Etype, t.Sym, t.DDDField())
+
+ case Txxx:
+ return "Txxx"
}
if fmtmode == FExp {
- Fatalf("missing %v case during export", Econv(t.Etype))
+ Fatalf("missing %v case during export", t.Etype)
}
// Don't know how to handle - fall back to detailed prints.
- return fmt.Sprintf("%v <%v> %v", Econv(t.Etype), t.Sym, t.Elem())
+ return fmt.Sprintf("%v <%v> %v", t.Etype, t.Sym, t.Elem())
}
// Statements which may be rendered with a simplestmt as init.
@@ -824,7 +840,7 @@ func stmtfmt(n *Node) string {
break
}
- f += fmt.Sprintf("%v %v= %v", n.Left, Oconv(Op(n.Etype), FmtSharp), n.Right)
+ f += fmt.Sprintf("%v %v= %v", n.Left, oconv(Op(n.Etype), FmtSharp), n.Right)
case OAS2:
if n.Colas && !complexinit {
@@ -898,11 +914,11 @@ func stmtfmt(n *Node) string {
case OSELECT, OSWITCH:
if fmtmode == FErr {
- f += fmt.Sprintf("%v statement", Oconv(n.Op, 0))
+ f += fmt.Sprintf("%v statement", n.Op)
break
}
- f += Oconv(n.Op, FmtSharp)
+ f += oconv(n.Op, FmtSharp)
if simpleinit {
f += fmt.Sprintf(" %v;", n.Ninit.First())
}
@@ -925,9 +941,9 @@ func stmtfmt(n *Node) string {
OFALL,
OXFALL:
if n.Left != nil {
- f += fmt.Sprintf("%v %v", Oconv(n.Op, FmtSharp), n.Left)
+ f += fmt.Sprintf("%v %v", oconv(n.Op, FmtSharp), n.Left)
} else {
- f += Oconv(n.Op, FmtSharp)
+ f += oconv(n.Op, FmtSharp)
}
case OEMPTY:
@@ -1180,7 +1196,7 @@ func exprfmt(n *Node, prec int) string {
if fmtmode == FErr {
return "func literal"
}
- if len(n.Nbody.Slice()) != 0 {
+ if n.Nbody.Len() != 0 {
return fmt.Sprintf("%v { %v }", n.Type, n.Nbody)
}
return fmt.Sprintf("%v { %v }", n.Type, n.Name.Param.Closure.Nbody)
@@ -1296,20 +1312,32 @@ func exprfmt(n *Node, prec int) string {
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 OINDEX, OINDEXMAP:
+ return fmt.Sprintf("%s[%v]", exprfmt(n.Left, nprec), n.Right)
+
+ case OSLICE, OSLICESTR, OSLICEARR, OSLICE3, OSLICE3ARR:
+ var buf bytes.Buffer
+ buf.WriteString(exprfmt(n.Left, nprec))
+ buf.WriteString("[")
+ low, high, max := n.SliceBounds()
+ if low != nil {
+ buf.WriteString(low.String())
+ }
+ buf.WriteString(":")
+ if high != nil {
+ buf.WriteString(high.String())
+ }
+ if n.Op.IsSlice3() {
+ buf.WriteString(":")
+ if max != nil {
+ buf.WriteString(max.String())
+ }
+ }
+ buf.WriteString("]")
+ return buf.String()
case OCOPY, OCOMPLEX:
- return fmt.Sprintf("%v(%v, %v)", Oconv(n.Op, FmtSharp), n.Left, n.Right)
+ return fmt.Sprintf("%v(%v, %v)", oconv(n.Op, FmtSharp), n.Left, n.Right)
case OCONV,
OCONVIFACE,
@@ -1341,12 +1369,12 @@ func exprfmt(n *Node, prec int) string {
OPRINT,
OPRINTN:
if n.Left != nil {
- return fmt.Sprintf("%v(%v)", Oconv(n.Op, FmtSharp), n.Left)
+ return fmt.Sprintf("%v(%v)", oconv(n.Op, FmtSharp), n.Left)
}
if n.Isddd {
- return fmt.Sprintf("%v(%v...)", Oconv(n.Op, FmtSharp), Hconv(n.List, FmtComma))
+ return fmt.Sprintf("%v(%v...)", oconv(n.Op, FmtSharp), Hconv(n.List, FmtComma))
}
- return fmt.Sprintf("%v(%v)", Oconv(n.Op, FmtSharp), Hconv(n.List, FmtComma))
+ return fmt.Sprintf("%v(%v)", oconv(n.Op, FmtSharp), Hconv(n.List, FmtComma))
case OCALL, OCALLFUNC, OCALLINTER, OCALLMETH, OGETG:
var f string
@@ -1380,9 +1408,9 @@ func exprfmt(n *Node, prec int) string {
ORECV:
var f string
if n.Left.Op == n.Op {
- f += fmt.Sprintf("%v ", Oconv(n.Op, FmtSharp))
+ f += fmt.Sprintf("%v ", oconv(n.Op, FmtSharp))
} else {
- f += Oconv(n.Op, FmtSharp)
+ f += oconv(n.Op, FmtSharp)
}
f += exprfmt(n.Left, nprec+1)
return f
@@ -1411,7 +1439,7 @@ func exprfmt(n *Node, prec int) string {
var f string
f += exprfmt(n.Left, nprec)
- f += fmt.Sprintf(" %v ", Oconv(n.Op, FmtSharp))
+ f += fmt.Sprintf(" %v ", oconv(n.Op, FmtSharp))
f += exprfmt(n.Right, nprec+1)
return f
@@ -1432,7 +1460,7 @@ func exprfmt(n *Node, prec int) string {
var f string
f += exprfmt(n.Left, nprec)
// TODO(marvin): Fix Node.EType type union.
- f += fmt.Sprintf(" %v ", Oconv(Op(n.Etype), FmtSharp))
+ f += fmt.Sprintf(" %v ", oconv(Op(n.Etype), FmtSharp))
f += exprfmt(n.Right, nprec+1)
return f
@@ -1444,7 +1472,7 @@ func exprfmt(n *Node, prec int) string {
}
}
- return fmt.Sprintf("<node %v>", Oconv(n.Op, 0))
+ return fmt.Sprintf("<node %v>", n.Op)
}
func nodefmt(n *Node, flag FmtFlag) string {
@@ -1499,40 +1527,40 @@ func nodedump(n *Node, flag FmtFlag) string {
}
if n.Ninit.Len() != 0 {
- fmt.Fprintf(&buf, "%v-init%v", Oconv(n.Op, 0), n.Ninit)
+ fmt.Fprintf(&buf, "%v-init%v", n.Op, n.Ninit)
indent(&buf)
}
}
switch n.Op {
default:
- fmt.Fprintf(&buf, "%v%v", Oconv(n.Op, 0), Jconv(n, 0))
+ fmt.Fprintf(&buf, "%v%v", n.Op, Jconv(n, 0))
case OREGISTER, OINDREG:
- fmt.Fprintf(&buf, "%v-%v%v", Oconv(n.Op, 0), obj.Rconv(int(n.Reg)), Jconv(n, 0))
+ fmt.Fprintf(&buf, "%v-%v%v", n.Op, obj.Rconv(int(n.Reg)), Jconv(n, 0))
case OLITERAL:
- fmt.Fprintf(&buf, "%v-%v%v", Oconv(n.Op, 0), Vconv(n.Val(), 0), Jconv(n, 0))
+ fmt.Fprintf(&buf, "%v-%v%v", n.Op, Vconv(n.Val(), 0), Jconv(n, 0))
case ONAME, ONONAME:
if n.Sym != nil {
- fmt.Fprintf(&buf, "%v-%v%v", Oconv(n.Op, 0), n.Sym, Jconv(n, 0))
+ fmt.Fprintf(&buf, "%v-%v%v", n.Op, n.Sym, Jconv(n, 0))
} else {
- fmt.Fprintf(&buf, "%v%v", Oconv(n.Op, 0), Jconv(n, 0))
+ fmt.Fprintf(&buf, "%v%v", n.Op, Jconv(n, 0))
}
if recur && n.Type == nil && n.Name != nil && n.Name.Param != nil && n.Name.Param.Ntype != nil {
indent(&buf)
- fmt.Fprintf(&buf, "%v-ntype%v", Oconv(n.Op, 0), n.Name.Param.Ntype)
+ fmt.Fprintf(&buf, "%v-ntype%v", n.Op, n.Name.Param.Ntype)
}
case OASOP:
- fmt.Fprintf(&buf, "%v-%v%v", Oconv(n.Op, 0), Oconv(Op(n.Etype), 0), Jconv(n, 0))
+ fmt.Fprintf(&buf, "%v-%v%v", n.Op, Op(n.Etype), Jconv(n, 0))
case OTYPE:
- fmt.Fprintf(&buf, "%v %v%v type=%v", Oconv(n.Op, 0), n.Sym, Jconv(n, 0), n.Type)
+ fmt.Fprintf(&buf, "%v %v%v type=%v", n.Op, n.Sym, Jconv(n, 0), n.Type)
if recur && n.Type == nil && n.Name.Param.Ntype != nil {
indent(&buf)
- fmt.Fprintf(&buf, "%v-ntype%v", Oconv(n.Op, 0), n.Name.Param.Ntype)
+ fmt.Fprintf(&buf, "%v-ntype%v", n.Op, n.Name.Param.Ntype)
}
}
@@ -1553,17 +1581,17 @@ func nodedump(n *Node, flag FmtFlag) string {
}
if n.List.Len() != 0 {
indent(&buf)
- fmt.Fprintf(&buf, "%v-list%v", Oconv(n.Op, 0), n.List)
+ fmt.Fprintf(&buf, "%v-list%v", n.Op, n.List)
}
if n.Rlist.Len() != 0 {
indent(&buf)
- fmt.Fprintf(&buf, "%v-rlist%v", Oconv(n.Op, 0), n.Rlist)
+ fmt.Fprintf(&buf, "%v-rlist%v", n.Op, n.Rlist)
}
- if len(n.Nbody.Slice()) != 0 {
+ if n.Nbody.Len() != 0 {
indent(&buf)
- fmt.Fprintf(&buf, "%v-body%v", Oconv(n.Op, 0), n.Nbody)
+ fmt.Fprintf(&buf, "%v-body%v", n.Op, n.Nbody)
}
}
@@ -1672,8 +1700,8 @@ func Fldconv(f *Field, flag FmtFlag) string {
// (The escape analysis tags do not apply to func vars.)
// But it must not suppress struct field tags.
// See golang.org/issue/13777 and golang.org/issue/14331.
- if flag&FmtShort == 0 && (!fmtbody || !f.Funarg) && f.Note != nil {
- str += " " + strconv.Quote(*f.Note)
+ if flag&FmtShort == 0 && (!fmtbody || !f.Funarg) && f.Note != "" {
+ str += " " + strconv.Quote(f.Note)
}
if fmtmode == FTypeId && (sf&FmtUnsigned != 0) {
diff --git a/src/cmd/compile/internal/gc/gen.go b/src/cmd/compile/internal/gc/gen.go
index 4a98f41bcb..275e6a7507 100644
--- a/src/cmd/compile/internal/gc/gen.go
+++ b/src/cmd/compile/internal/gc/gen.go
@@ -8,6 +8,7 @@ package gc
import (
"cmd/internal/obj"
+ "cmd/internal/sys"
"fmt"
)
@@ -217,7 +218,7 @@ func Genlist(l Nodes) {
func cgen_proc(n *Node, proc int) {
switch n.Left.Op {
default:
- Fatalf("cgen_proc: unknown call %v", Oconv(n.Left.Op, 0))
+ Fatalf("cgen_proc: unknown call %v", n.Left.Op)
case OCALLMETH:
cgen_callmeth(n.Left, proc)
@@ -245,7 +246,7 @@ func cgen_dcl(n *Node) {
if n.Class&PHEAP == 0 {
return
}
- if compiling_runtime != 0 {
+ if compiling_runtime {
Yyerror("%v escapes to heap, not allowed in runtime.", n)
}
if prealloc[n] == nil {
@@ -1030,7 +1031,7 @@ func componentgen_wb(nr, nl *Node, wb bool) bool {
// Emit vardef if needed.
if nl.Op == ONAME {
switch nl.Type.Etype {
- case TARRAY, TSTRING, TINTER, TSTRUCT:
+ case TARRAY, TSLICE, TSTRING, TINTER, TSTRUCT:
Gvardef(nl)
}
}
@@ -1174,7 +1175,7 @@ func visitComponents(t *Type, startOffset int64, f func(elem *Type, elemOffset i
}
// 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' {
+ if !Thearch.LinkArch.InFamily(sys.ARM, sys.I386) {
Fatalf("unknown 32-bit architecture")
}
return f(Types[TUINT32], startOffset) &&
@@ -1203,13 +1204,12 @@ func visitComponents(t *Type, startOffset int64, f func(elem *Type, elemOffset i
return f(Ptrto(Types[TUINT8]), startOffset) &&
f(Types[Simtype[TUINT]], startOffset+int64(Widthptr))
- case TARRAY:
- if t.IsSlice() {
- return f(Ptrto(t.Elem()), startOffset+int64(Array_array)) &&
- f(Types[Simtype[TUINT]], startOffset+int64(Array_nel)) &&
- f(Types[Simtype[TUINT]], startOffset+int64(Array_cap))
- }
+ case TSLICE:
+ return f(Ptrto(t.Elem()), startOffset+int64(Array_array)) &&
+ f(Types[Simtype[TUINT]], startOffset+int64(Array_nel)) &&
+ f(Types[Simtype[TUINT]], startOffset+int64(Array_cap))
+ case TARRAY:
// Short-circuit [1e6]struct{}.
if t.Elem().Width == 0 {
return true
diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go
index fdea1f2fba..f9a372dcce 100644
--- a/src/cmd/compile/internal/gc/go.go
+++ b/src/cmd/compile/internal/gc/go.go
@@ -5,8 +5,9 @@
package gc
import (
- "bytes"
+ "bufio"
"cmd/compile/internal/ssa"
+ "cmd/internal/bio"
"cmd/internal/obj"
)
@@ -16,48 +17,10 @@ const (
MaxStackVarSize = 10 * 1024 * 1024
)
-type Val struct {
- // U contains one of:
- // bool bool when n.ValCtype() == CTBOOL
- // *Mpint int when n.ValCtype() == CTINT, rune when n.ValCtype() == CTRUNE
- // *Mpflt float when n.ValCtype() == CTFLT
- // *Mpcplx pair of floats when n.ValCtype() == CTCPLX
- // string string when n.ValCtype() == CTSTR
- // *Nilval when n.ValCtype() == CTNIL
- U interface{}
-}
-
-type NilVal struct{}
-
-func (v Val) Ctype() Ctype {
- switch x := v.U.(type) {
- default:
- Fatalf("unexpected Ctype for %T", v.U)
- panic("not reached")
- case nil:
- return 0
- case *NilVal:
- return CTNIL
- case bool:
- return CTBOOL
- case *Mpint:
- if x.Rune {
- return CTRUNE
- }
- return CTINT
- case *Mpflt:
- return CTFLT
- case *Mpcplx:
- return CTCPLX
- case string:
- return CTSTR
- }
-}
-
type Pkg struct {
Name string // package name, e.g. "sys"
Path string // string literal used in import statement, e.g. "runtime/internal/sys"
- Pathsym *Sym
+ Pathsym *obj.LSym
Prefix string // escaped path for use in symbol table
Imported bool // export data of this package was parsed
Exported bool // import line written in export data
@@ -66,6 +29,14 @@ type Pkg struct {
Syms map[string]*Sym
}
+// Sym represents an object name. Most commonly, this is a Go identifier naming
+// an object declared within a package, but Syms are also used to name internal
+// synthesized objects.
+//
+// As a special exception, field and method names that are exported use the Sym
+// associated with localpkg instead of the package that declared them. This
+// allows using Sym pointer equality to test for Go identifier uniqueness when
+// handling selector expressions.
type Sym struct {
Flags SymFlags
Link *Sym
@@ -111,37 +82,6 @@ const (
SymAlgGen
)
-var dclstack *Sym
-
-// Ctype describes the constant kind of an "ideal" (untyped) constant.
-type Ctype int8
-
-const (
- CTxxx Ctype = iota
-
- CTINT
- CTRUNE
- CTFLT
- CTCPLX
- CTSTR
- CTBOOL
- CTNIL
-)
-
-// ChanDir is whether a channel can send, receive, or both.
-type ChanDir uint8
-
-func (c ChanDir) CanRecv() bool { return c&Crecv != 0 }
-func (c ChanDir) CanSend() bool { return c&Csend != 0 }
-
-const (
- // types of channel
- // must match ../../../../reflect/type.go:/ChanDir
- Crecv ChanDir = 1 << 0
- Csend ChanDir = 1 << 1
- Cboth ChanDir = Crecv | Csend
-)
-
// The Class of a variable/function describes the "storage class"
// of a variable or function. During parsing, storage classes are
// called declaration contexts.
@@ -161,30 +101,6 @@ const (
PHEAP = 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 Sig struct {
- name string
- pkg *Pkg
- isym *Sym
- tsym *Sym
- type_ *Type
- mtype *Type
- offset int32
-}
-
// note this is the runtime representation
// of the compilers arrays.
//
@@ -212,20 +128,13 @@ var sizeof_Array int // runtime sizeof(Array)
// } String;
var sizeof_String int // runtime sizeof(String)
-// lexlineno is the line number _after_ the most recently read rune.
-// In particular, it's advanced (or rewound) as newlines are read (or unread).
-var lexlineno int32
-
-// lineno is the line number at the start of the most recently lexed token.
-var lineno int32
-
var pragcgobuf string
var infile string
var outfile string
-var bout *obj.Biobuf
+var bout *bio.Writer
var nerrors int
@@ -235,13 +144,9 @@ var nsyntaxerrors int
var decldepth int32
-var safemode int
-
-var nolocalimports int
+var safemode bool
-var lexbuf bytes.Buffer
-var strbuf bytes.Buffer
-var litbuf string // LLITERAL value for use in syntax error messages
+var nolocalimports bool
var Debug [256]int
@@ -266,12 +171,13 @@ var msanpkg *Pkg // package runtime/msan
var typepkg *Pkg // fake package for runtime type info (headers)
-var typelinkpkg *Pkg // fake package for runtime type info (data)
-
var unsafepkg *Pkg // package unsafe
var trackpkg *Pkg // fake package for field tracking
+var mappkg *Pkg // fake package for map zero value
+var zerosize int64
+
var Tptr EType // either TPTR32 or TPTR64
var myimportpath string
@@ -318,8 +224,6 @@ var maxfltval [NTYPE]*Mpflt
var xtop []*Node
-var externdcl []*Node
-
var exportlist []*Node
var importlist []*Node // imported functions and methods with inlinable bodies
@@ -344,10 +248,6 @@ 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 bool // flag that curfn has defer statement
var Curfn *Node
@@ -364,21 +264,21 @@ var Funcdepth int32
var typecheckok bool
-var compiling_runtime int
+var compiling_runtime bool
var compiling_wrappers int
-var use_writebarrier int
+var use_writebarrier bool
-var pure_go int
+var pure_go bool
var flag_installsuffix string
-var flag_race int
+var flag_race bool
-var flag_msan int
+var flag_msan bool
-var flag_largemodel int
+var flag_largemodel bool
// Whether we are adding any sort of code instrumentation, such as
// when the race detector is enabled.
@@ -388,9 +288,9 @@ var debuglive int
var Ctxt *obj.Link
-var writearchive int
+var writearchive bool
-var bstdout obj.Biobuf
+var bstdout *bufio.Writer
var Nacl bool
@@ -404,34 +304,6 @@ var nodfp *Node
var Disable_checknil int
-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 bool // 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 (
@@ -491,9 +363,8 @@ const (
)
type Arch struct {
- Thechar int
- Thestring string
- Thelinkarch *obj.LinkArch
+ LinkArch *obj.LinkArch
+
REGSP int
REGCTXT int
REGCALLX int // BX
@@ -507,23 +378,25 @@ type Arch struct {
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(Op, *Node, *Node, *Node) bool
- Cgen_float func(*Node, *Node) // optional
- Cgen_hmul func(*Node, *Node, *Node)
- Cgen_shift func(Op, bool, *Node, *Node, *Node)
- Clearfat func(*Node)
- Cmp64 func(*Node, *Node, Op, int, *obj.Prog) // only on 32-bit systems
- Defframe func(*obj.Prog)
- Dodiv func(Op, *Node, *Node, *Node)
- Excise func(*Flow)
- Expandchecks func(*obj.Prog)
- Getg func(*Node)
- Gins func(obj.As, *Node, *Node) *obj.Prog
+ 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(Op, *Node, *Node, *Node) bool
+ Cgen_float func(*Node, *Node) // optional
+ Cgen_hmul func(*Node, *Node, *Node)
+ RightShiftWithCarry func(*Node, uint, *Node) // only on systems without RROTC instruction
+ AddSetCarry func(*Node, *Node, *Node) // only on systems when ADD does not update carry flag
+ Cgen_shift func(Op, bool, *Node, *Node, *Node)
+ Clearfat func(*Node)
+ Cmp64 func(*Node, *Node, Op, int, *obj.Prog) // only on 32-bit systems
+ Defframe func(*obj.Prog)
+ Dodiv func(Op, *Node, *Node, *Node)
+ Excise func(*Flow)
+ Expandchecks func(*obj.Prog)
+ Getg func(*Node)
+ Gins func(obj.As, *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.
diff --git a/src/cmd/compile/internal/gc/gsubr.go b/src/cmd/compile/internal/gc/gsubr.go
index 353d90f593..7e64194957 100644
--- a/src/cmd/compile/internal/gc/gsubr.go
+++ b/src/cmd/compile/internal/gc/gsubr.go
@@ -32,6 +32,7 @@ package gc
import (
"cmd/internal/obj"
+ "cmd/internal/sys"
"fmt"
"runtime"
"strings"
@@ -57,7 +58,9 @@ func Ismem(n *Node) bool {
return true
case OADDR:
- return Thearch.Thechar == '6' || Thearch.Thechar == '9' // because 6g uses PC-relative addressing; TODO(rsc): not sure why 9g too
+ // amd64 and s390x use PC relative addressing.
+ // TODO(rsc): not sure why ppc64 needs this too.
+ return Thearch.LinkArch.InFamily(sys.AMD64, sys.PPC64, sys.S390X)
}
return false
@@ -83,7 +86,7 @@ func Gbranch(as obj.As, 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' && Thearch.Thechar != '0' {
+ if as != obj.AJMP && likely != 0 && !Thearch.LinkArch.InFamily(sys.PPC64, sys.ARM64, sys.MIPS64, sys.S390X) {
p.From.Type = obj.TYPE_CONST
if likely > 0 {
p.From.Offset = 1
@@ -274,7 +277,7 @@ func gused(n *Node) {
func Isfat(t *Type) bool {
if t != nil {
switch t.Etype {
- case TSTRUCT, TARRAY, TSTRING,
+ case TSTRUCT, TARRAY, TSLICE, TSTRING,
TINTER: // maybe remove later
return true
}
@@ -324,13 +327,13 @@ func Naddr(a *obj.Addr, n *Node) {
a := a // copy to let escape into Ctxt.Dconv
Debug['h'] = 1
Dump("naddr", n)
- Fatalf("naddr: bad %v %v", Oconv(n.Op, 0), Ctxt.Dconv(a))
+ Fatalf("naddr: bad %v %v", n.Op, 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.
+ if Thearch.LinkArch.Family == sys.I386 { // TODO(rsc): Never clear a->width.
a.Width = 0
}
@@ -342,7 +345,7 @@ func Naddr(a *obj.Addr, n *Node) {
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.
+ if Thearch.LinkArch.Family == sys.I386 { // TODO(rsc): Never clear a->width.
a.Width = 0
}
@@ -419,36 +422,36 @@ func Naddr(a *obj.Addr, n *Node) {
if !n.Left.Type.IsStruct() || n.Left.Type.Field(0).Sym != n.Sym {
Debug['h'] = 1
Dump("naddr", n)
- Fatalf("naddr: bad %v %v", Oconv(n.Op, 0), Ctxt.Dconv(a))
+ Fatalf("naddr: bad %v %v", n.Op, Ctxt.Dconv(a))
}
Naddr(a, n.Left)
case OLITERAL:
- if Thearch.Thechar == '8' {
+ if Thearch.LinkArch.Family == sys.I386 {
a.Width = 0
}
- switch n.Val().Ctype() {
+ switch u := n.Val().U.(type) {
default:
Fatalf("naddr: const %v", Tconv(n.Type, FmtLong))
- case CTFLT:
+ case *Mpflt:
a.Type = obj.TYPE_FCONST
- a.Val = n.Val().U.(*Mpflt).Float64()
+ a.Val = u.Float64()
- case CTINT, CTRUNE:
+ case *Mpint:
a.Sym = nil
a.Type = obj.TYPE_CONST
- a.Offset = n.Int64()
+ a.Offset = u.Int64()
- case CTSTR:
- datagostring(n.Val().U.(string), a)
+ case string:
+ datagostring(u, a)
- case CTBOOL:
+ case bool:
a.Sym = nil
a.Type = obj.TYPE_CONST
- a.Offset = int64(obj.Bool2int(n.Val().U.(bool)))
+ a.Offset = int64(obj.Bool2int(u))
- case CTNIL:
+ case *NilVal:
a.Sym = nil
a.Type = obj.TYPE_CONST
a.Offset = 0
@@ -457,12 +460,12 @@ func Naddr(a *obj.Addr, n *Node) {
case OADDR:
Naddr(a, n.Left)
a.Etype = uint8(Tptr)
- if Thearch.Thechar != '0' && Thearch.Thechar != '5' && Thearch.Thechar != '7' && Thearch.Thechar != '9' { // TODO(rsc): Do this even for arm, ppc64.
+ if !Thearch.LinkArch.InFamily(sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64, sys.S390X) { // TODO(rsc): Do this even for these architectures.
a.Width = int64(Widthptr)
}
if a.Type != obj.TYPE_MEM {
a := a // copy to let escape into Ctxt.Dconv
- Fatalf("naddr: OADDR %v (from %v)", Ctxt.Dconv(a), Oconv(n.Left.Op, 0))
+ Fatalf("naddr: OADDR %v (from %v)", Ctxt.Dconv(a), n.Left.Op)
}
a.Type = obj.TYPE_ADDR
@@ -496,7 +499,7 @@ func Naddr(a *obj.Addr, n *Node) {
}
a.Etype = uint8(Simtype[TUINT])
a.Offset += int64(Array_nel)
- if Thearch.Thechar != '5' { // TODO(rsc): Do this even on arm.
+ if Thearch.LinkArch.Family != sys.ARM { // TODO(rsc): Do this even on arm.
a.Width = int64(Widthint)
}
@@ -509,7 +512,7 @@ func Naddr(a *obj.Addr, n *Node) {
}
a.Etype = uint8(Simtype[TUINT])
a.Offset += int64(Array_cap)
- if Thearch.Thechar != '5' { // TODO(rsc): Do this even on arm.
+ if Thearch.LinkArch.Family != sys.ARM { // TODO(rsc): Do this even on arm.
a.Width = int64(Widthint)
}
}
@@ -541,7 +544,7 @@ func nodarg(t interface{}, fp int) *Node {
switch t := t.(type) {
case *Type:
// entire argument struct, not just one arg
- if !t.IsStruct() || !t.Funarg {
+ if !t.IsFuncArgStruct() {
Fatalf("nodarg: bad type %v", t)
}
n = Nod(ONAME, nil, nil)
@@ -695,7 +698,7 @@ func Regalloc(n *Node, t *Type, o *Node) {
Fatalf("regalloc: t nil")
}
et := Simtype[t.Etype]
- if Ctxt.Arch.Regsize == 4 && (et == TINT64 || et == TUINT64) {
+ if Ctxt.Arch.RegSize == 4 && (et == TINT64 || et == TUINT64) {
Fatalf("regalloc 64bit")
}
diff --git a/src/cmd/compile/internal/gc/init.go b/src/cmd/compile/internal/gc/init.go
index d355a46557..6c9223b57a 100644
--- a/src/cmd/compile/internal/gc/init.go
+++ b/src/cmd/compile/internal/gc/init.go
@@ -58,8 +58,6 @@ func anyinit(n []*Node) bool {
break
}
fallthrough
-
- // fall through
default:
return true
}
diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go
index ea2394e7f9..10b61377ca 100644
--- a/src/cmd/compile/internal/gc/inl.go
+++ b/src/cmd/compile/internal/gc/inl.go
@@ -71,7 +71,7 @@ func typecheckinl(fn *Node) {
}
save_safemode := safemode
- safemode = 0
+ safemode = false
savefn := Curfn
Curfn = fn
@@ -100,7 +100,7 @@ func caninl(fn *Node) {
}
// If fn has no body (is defined outside of Go), cannot inline it.
- if len(fn.Nbody.Slice()) == 0 {
+ if fn.Nbody.Len() == 0 {
return
}
@@ -110,8 +110,9 @@ func caninl(fn *Node) {
// can't handle ... args yet
if Debug['l'] < 3 {
- for _, t := range fn.Type.Params().Fields().Slice() {
- if t.Isddd {
+ f := fn.Type.Params().Fields()
+ if len := f.Len(); len > 0 {
+ if t := f.Index(len - 1); t.Isddd {
return
}
}
@@ -128,7 +129,7 @@ func caninl(fn *Node) {
}
const maxBudget = 80
- budget := maxBudget // allowed hairyness
+ budget := int32(maxBudget) // allowed hairyness
if ishairylist(fn.Nbody, &budget) || budget < 0 {
return
}
@@ -136,27 +137,29 @@ func caninl(fn *Node) {
savefn := Curfn
Curfn = fn
- fn.Func.Nname.Func.Inl.Set(fn.Nbody.Slice())
- fn.Nbody.Set(inlcopylist(fn.Func.Nname.Func.Inl.Slice()))
- inldcl := inlcopylist(fn.Func.Nname.Name.Defn.Func.Dcl)
- fn.Func.Nname.Func.Inldcl.Set(inldcl)
- fn.Func.Nname.Func.InlCost = int32(maxBudget - budget)
+ n := fn.Func.Nname
+
+ n.Func.Inl.Set(fn.Nbody.Slice())
+ fn.Nbody.Set(inlcopylist(n.Func.Inl.Slice()))
+ inldcl := inlcopylist(n.Name.Defn.Func.Dcl)
+ n.Func.Inldcl.Set(inldcl)
+ n.Func.InlCost = 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.SetNname(fn.Func.Nname)
+ fn.Type.SetNname(n)
if Debug['m'] > 1 {
- fmt.Printf("%v: can inline %v as: %v { %v }\n", fn.Line(), Nconv(fn.Func.Nname, FmtSharp), Tconv(fn.Type, FmtSharp), Hconv(fn.Func.Nname.Func.Inl, FmtSharp))
+ fmt.Printf("%v: can inline %v as: %v { %v }\n", fn.Line(), Nconv(n, FmtSharp), Tconv(fn.Type, FmtSharp), Hconv(n.Func.Inl, FmtSharp))
} else if Debug['m'] != 0 {
- fmt.Printf("%v: can inline %v\n", fn.Line(), fn.Func.Nname)
+ fmt.Printf("%v: can inline %v\n", fn.Line(), n)
}
Curfn = savefn
}
// Look for anything we want to punt on.
-func ishairylist(ll Nodes, budget *int) bool {
+func ishairylist(ll Nodes, budget *int32) bool {
for _, n := range ll.Slice() {
if ishairy(n, budget) {
return true
@@ -165,7 +168,7 @@ func ishairylist(ll Nodes, budget *int) bool {
return false
}
-func ishairy(n *Node, budget *int) bool {
+func ishairy(n *Node, budget *int32) bool {
if n == nil {
return false
}
@@ -173,13 +176,13 @@ func ishairy(n *Node, budget *int) bool {
switch n.Op {
// Call is okay if inlinable and we have the budget for the body.
case OCALLFUNC:
- if n.Left.Func != nil && len(n.Left.Func.Inl.Slice()) != 0 {
- *budget -= int(n.Left.Func.InlCost)
+ if fn := n.Left.Func; fn != nil && fn.Inl.Len() != 0 {
+ *budget -= fn.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 && len(n.Left.Sym.Def.Func.Inl.Slice()) != 0 {
- *budget -= int(n.Left.Sym.Def.Func.InlCost)
+ if d := n.Left.Sym.Def; d != nil && d.Func.Inl.Len() != 0 {
+ *budget -= d.Func.InlCost
break
}
}
@@ -189,14 +192,15 @@ func ishairy(n *Node, budget *int) bool {
// Call is okay if inlinable and we have the budget for the body.
case OCALLMETH:
- if n.Left.Type == nil {
+ t := n.Left.Type
+ if t == nil {
Fatalf("no function type for [%p] %v\n", n.Left, Nconv(n.Left, FmtSign))
}
- if n.Left.Type.Nname() == nil {
- Fatalf("no function definition for [%p] %v\n", n.Left.Type, Tconv(n.Left.Type, FmtSign))
+ if t.Nname() == nil {
+ Fatalf("no function definition for [%p] %v\n", t, Tconv(t, FmtSign))
}
- if len(n.Left.Type.Nname().Func.Inl.Slice()) != 0 {
- *budget -= int(n.Left.Type.Nname().Func.InlCost)
+ if inlfn := t.Nname().Func; inlfn.Inl.Len() != 0 {
+ *budget -= inlfn.InlCost
break
}
if Debug['l'] < 4 {
@@ -453,7 +457,7 @@ func inlnode(n *Node) *Node {
if Debug['m'] > 3 {
fmt.Printf("%v:call to func %v\n", n.Line(), Nconv(n.Left, FmtSign))
}
- if n.Left.Func != nil && len(n.Left.Func.Inl.Slice()) != 0 && !isIntrinsicCall1(n) { // normal case
+ if n.Left.Func != nil && n.Left.Func.Inl.Len() != 0 && !isIntrinsicCall1(n) { // normal case
n = mkinlcall(n, 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 {
@@ -492,7 +496,7 @@ func mkinlcall(n *Node, fn *Node, isddd bool) *Node {
pkg := fnpkg(fn)
if pkg != localpkg && pkg != nil {
- safemode = 0
+ safemode = false
}
n = mkinlcall1(n, fn, isddd)
safemode = save_safemode
@@ -520,7 +524,7 @@ var inlgen int
// n.Left = mkinlcall1(n.Left, fn, isddd)
func mkinlcall1(n *Node, fn *Node, isddd bool) *Node {
// For variadic fn.
- if len(fn.Func.Inl.Slice()) == 0 {
+ if fn.Func.Inl.Len() == 0 {
return n
}
@@ -750,7 +754,7 @@ func mkinlcall1(n *Node, fn *Node, isddd bool) *Node {
vararrtype := typArray(varargtype.Elem(), int64(varargcount))
as.Right = Nod(OCOMPLIT, nil, typenod(vararrtype))
as.Right.List.Set(varargs)
- as.Right = Nod(OSLICE, as.Right, Nod(OKEY, nil, nil))
+ as.Right = Nod(OSLICE, as.Right, nil)
}
as = typecheck(as, Etop)
diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go
index 6f1331ca89..8608a6229c 100644
--- a/src/cmd/compile/internal/gc/lex.go
+++ b/src/cmd/compile/internal/gc/lex.go
@@ -6,6 +6,7 @@ package gc
import (
"bufio"
+ "bytes"
"cmd/internal/obj"
"fmt"
"io"
@@ -20,6 +21,17 @@ const (
BOM = 0xFEFF
)
+// lexlineno is the line number _after_ the most recently read rune.
+// In particular, it's advanced (or rewound) as newlines are read (or unread).
+var lexlineno int32
+
+// lineno is the line number at the start of the most recently lexed token.
+var lineno int32
+
+var lexbuf bytes.Buffer
+var strbuf bytes.Buffer
+var litbuf string // LLITERAL value for use in syntax error messages
+
func isSpace(c rune) bool {
return c == ' ' || c == '\t' || c == '\n' || c == '\r'
}
@@ -32,6 +44,10 @@ func isDigit(c rune) bool {
return '0' <= c && c <= '9'
}
+func isQuoted(s string) bool {
+ return len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"'
+}
+
func plan9quote(s string) string {
if s == "" {
return "''"
@@ -739,7 +755,7 @@ func (l *lexer) number(c rune) {
}
done:
- litbuf = "literal " + str
+ litbuf = "" // lazily initialized in (*parser).syntax_error
l.nlsemi = true
l.tok = LLITERAL
}
@@ -841,15 +857,6 @@ func internString(b []byte) string {
return s
}
-func more(pp *string) bool {
- p := *pp
- for p != "" && isSpace(rune(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.
@@ -875,7 +882,7 @@ func (l *lexer) getlinepragma() rune {
text := strings.TrimSuffix(lexbuf.String(), "\r")
if strings.HasPrefix(text, "go:cgo_") {
- pragcgo(text)
+ pragcgobuf += pragcgo(text)
}
verb := text
@@ -907,17 +914,17 @@ func (l *lexer) getlinepragma() rune {
case "go:noinline":
l.pragma |= Noinline
case "go:systemstack":
- if compiling_runtime == 0 {
+ if !compiling_runtime {
Yyerror("//go:systemstack only allowed in runtime")
}
l.pragma |= Systemstack
case "go:nowritebarrier":
- if compiling_runtime == 0 {
+ if !compiling_runtime {
Yyerror("//go:nowritebarrier only allowed in runtime")
}
l.pragma |= Nowritebarrier
case "go:nowritebarrierrec":
- if compiling_runtime == 0 {
+ if !compiling_runtime {
Yyerror("//go:nowritebarrierrec only allowed in runtime")
}
l.pragma |= Nowritebarrierrec | Nowritebarrier // implies Nowritebarrier
@@ -979,139 +986,114 @@ func (l *lexer) getlinepragma() rune {
return c
}
-func getimpsym(pp *string) string {
- more(pp) // skip spaces
- p := *pp
- if p == "" || p[0] == '"' {
- return ""
- }
- i := 0
- for i < len(p) && !isSpace(rune(p[i])) && p[i] != '"' {
- i++
- }
- sym := p[:i]
- *pp = p[i:]
- return sym
-}
+func pragcgo(text string) string {
+ f := pragmaFields(text)
-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
-}
+ verb := f[0][3:] // skip "go:"
+ switch verb {
+ case "cgo_export_static", "cgo_export_dynamic":
+ switch {
+ case len(f) == 2 && !isQuoted(f[1]):
+ local := plan9quote(f[1])
+ return fmt.Sprintln(verb, local)
-// 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
+ case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]):
+ local := plan9quote(f[1])
+ remote := plan9quote(f[2])
+ return fmt.Sprintln(verb, local, remote)
- 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" {
- p, ok := getquoted(&q)
- if !ok {
- Yyerror("usage: //go:cgo_dynamic_linker \"path\"")
- return
+ default:
+ Yyerror(`usage: //go:%s local [remote]`, verb)
}
- pragcgobuf += fmt.Sprintf("cgo_dynamic_linker %v\n", plan9quote(p))
- return
+ case "cgo_import_dynamic":
+ switch {
+ case len(f) == 2 && !isQuoted(f[1]):
+ local := plan9quote(f[1])
+ return fmt.Sprintln(verb, local)
- }
+ case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]):
+ local := plan9quote(f[1])
+ remote := plan9quote(f[2])
+ return fmt.Sprintln(verb, local, remote)
- 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
- }
+ case len(f) == 4 && !isQuoted(f[1]) && !isQuoted(f[2]) && isQuoted(f[3]):
+ local := plan9quote(f[1])
+ remote := plan9quote(f[2])
+ library := plan9quote(strings.Trim(f[3], `"`))
+ return fmt.Sprintln(verb, local, remote, library)
- remote = getimpsym(&q)
- if remote == "" {
- goto err2
+ default:
+ Yyerror(`usage: //go:cgo_import_dynamic local [remote ["library"]]`)
}
- pragcgobuf += fmt.Sprintf("%s %v %v\n", verb, plan9quote(local), plan9quote(remote))
- return
-
- err2:
- Yyerror("usage: //go:%s local [remote]", verb)
- return
- }
+ case "cgo_import_static":
+ switch {
+ case len(f) == 2 && !isQuoted(f[1]):
+ local := plan9quote(f[1])
+ return fmt.Sprintln(verb, local)
- 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
+ default:
+ Yyerror(`usage: //go:cgo_import_static local`)
}
+ case "cgo_dynamic_linker":
+ switch {
+ case len(f) == 2 && isQuoted(f[1]):
+ path := plan9quote(strings.Trim(f[1], `"`))
+ return fmt.Sprintln(verb, path)
- remote = getimpsym(&q)
- if remote == "" {
- goto err3
- }
- if !more(&q) {
- pragcgobuf += fmt.Sprintf("cgo_import_dynamic %v %v\n", plan9quote(local), plan9quote(remote))
- return
+ default:
+ Yyerror(`usage: //go:cgo_dynamic_linker "path"`)
}
+ case "cgo_ldflag":
+ switch {
+ case len(f) == 2 && isQuoted(f[1]):
+ arg := plan9quote(strings.Trim(f[1], `"`))
+ return fmt.Sprintln(verb, arg)
- p, ok = getquoted(&q)
- if !ok {
- goto err3
+ default:
+ Yyerror(`usage: //go:cgo_ldflag "arg"`)
}
- 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
}
+ return ""
+}
- if verb == "cgo_import_static" {
- local := getimpsym(&q)
- if local == "" || more(&q) {
- Yyerror("usage: //go:cgo_import_static local")
- return
+// pragmaFields is similar to strings.FieldsFunc(s, isSpace)
+// but does not split when inside double quoted regions and always
+// splits before the start and after the end of a double quoted region.
+// pragmaFields does not recognize escaped quotes. If a quote in s is not
+// closed the part after the opening quote will not be returned as a field.
+func pragmaFields(s string) []string {
+ var a []string
+ inQuote := false
+ fieldStart := -1 // Set to -1 when looking for start of field.
+ for i, c := range s {
+ switch {
+ case c == '"':
+ if inQuote {
+ inQuote = false
+ a = append(a, s[fieldStart:i+1])
+ fieldStart = -1
+ } else {
+ inQuote = true
+ if fieldStart >= 0 {
+ a = append(a, s[fieldStart:i])
+ }
+ fieldStart = i
+ }
+ case !inQuote && isSpace(c):
+ if fieldStart >= 0 {
+ a = append(a, s[fieldStart:i])
+ fieldStart = -1
+ }
+ default:
+ if fieldStart == -1 {
+ fieldStart = i
+ }
}
- pragcgobuf += fmt.Sprintf("cgo_import_static %v\n", plan9quote(local))
- return
-
}
-
- if verb == "cgo_ldflag" {
- p, ok := getquoted(&q)
- if !ok {
- Yyerror("usage: //go:cgo_ldflag \"arg\"")
- return
- }
- pragcgobuf += fmt.Sprintf("cgo_ldflag %v\n", plan9quote(p))
- return
-
+ if !inQuote && fieldStart >= 0 { // Last field might end at the end of the string.
+ a = append(a, s[fieldStart:])
}
+ return a
}
func (l *lexer) getr() rune {
diff --git a/src/cmd/compile/internal/gc/lex_test.go b/src/cmd/compile/internal/gc/lex_test.go
new file mode 100644
index 0000000000..9230b30dad
--- /dev/null
+++ b/src/cmd/compile/internal/gc/lex_test.go
@@ -0,0 +1,79 @@
+// Copyright 2016 The Go 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 "testing"
+
+func eq(a, b []string) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ for i := 0; i < len(a); i++ {
+ if a[i] != b[i] {
+ return false
+ }
+ }
+ return true
+}
+
+func TestPragmaFields(t *testing.T) {
+
+ var tests = []struct {
+ in string
+ want []string
+ }{
+ {"", []string{}},
+ {" \t ", []string{}},
+ {`""""`, []string{`""`, `""`}},
+ {" a'b'c ", []string{"a'b'c"}},
+ {"1 2 3 4", []string{"1", "2", "3", "4"}},
+ {"\n☺\t☹\n", []string{"☺", "☹"}},
+ {`"1 2 " 3 " 4 5"`, []string{`"1 2 "`, `3`, `" 4 5"`}},
+ {`"1""2 3""4"`, []string{`"1"`, `"2 3"`, `"4"`}},
+ {`12"34"`, []string{`12`, `"34"`}},
+ {`12"34 `, []string{`12`}},
+ }
+
+ for _, tt := range tests {
+ got := pragmaFields(tt.in)
+ if !eq(got, tt.want) {
+ t.Errorf("pragmaFields(%q) = %v; want %v", tt.in, got, tt.want)
+ continue
+ }
+ }
+}
+
+func TestPragcgo(t *testing.T) {
+
+ var tests = []struct {
+ in string
+ want string
+ }{
+ {`go:cgo_export_dynamic local`, "cgo_export_dynamic local\n"},
+ {`go:cgo_export_dynamic local remote`, "cgo_export_dynamic local remote\n"},
+ {`go:cgo_export_dynamic local' remote'`, "cgo_export_dynamic 'local''' 'remote'''\n"},
+ {`go:cgo_export_static local`, "cgo_export_static local\n"},
+ {`go:cgo_export_static local remote`, "cgo_export_static local remote\n"},
+ {`go:cgo_export_static local' remote'`, "cgo_export_static 'local''' 'remote'''\n"},
+ {`go:cgo_import_dynamic local`, "cgo_import_dynamic local\n"},
+ {`go:cgo_import_dynamic local remote`, "cgo_import_dynamic local remote\n"},
+ {`go:cgo_import_dynamic local remote "library"`, "cgo_import_dynamic local remote library\n"},
+ {`go:cgo_import_dynamic local' remote' "lib rary"`, "cgo_import_dynamic 'local''' 'remote''' 'lib rary'\n"},
+ {`go:cgo_import_static local`, "cgo_import_static local\n"},
+ {`go:cgo_import_static local'`, "cgo_import_static 'local'''\n"},
+ {`go:cgo_dynamic_linker "/path/"`, "cgo_dynamic_linker /path/\n"},
+ {`go:cgo_dynamic_linker "/p ath/"`, "cgo_dynamic_linker '/p ath/'\n"},
+ {`go:cgo_ldflag "arg"`, "cgo_ldflag arg\n"},
+ {`go:cgo_ldflag "a rg"`, "cgo_ldflag 'a rg'\n"},
+ }
+
+ for _, tt := range tests {
+ got := pragcgo(tt.in)
+ if got != tt.want {
+ t.Errorf("pragcgo(%q) = %q; want %q", tt.in, got, tt.want)
+ continue
+ }
+ }
+}
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index 73ecb09fa5..54211e4892 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -10,6 +10,7 @@ import (
"bufio"
"cmd/compile/internal/ssa"
"cmd/internal/obj"
+ "cmd/internal/sys"
"flag"
"fmt"
"io"
@@ -89,22 +90,21 @@ func doversion() {
os.Exit(0)
}
+// supportsDynlink reports whether or not the code generator for the given
+// architecture supports the -shared and -dynlink flags.
+func supportsDynlink(arch *sys.Arch) bool {
+ return arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.S390X)
+}
+
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
+ goarch = obj.Getgoarch()
- Ctxt = obj.Linknew(Thearch.Thelinkarch)
+ Ctxt = obj.Linknew(Thearch.LinkArch)
Ctxt.DiagFunc = Yyerror
- Ctxt.Bso = &bstdout
- bstdout = *obj.Binitw(os.Stdout)
+ bstdout = bufio.NewWriter(os.Stdout)
+ Ctxt.Bso = bstdout
localpkg = mkpkg("")
localpkg.Prefix = "\"\""
@@ -126,10 +126,6 @@ func Main() {
itabpkg.Name = "go.itab"
itabpkg.Prefix = "go.itab" // not go%2eitab
- typelinkpkg = mkpkg("go.typelink")
- typelinkpkg.Name = "go.typelink"
- typelinkpkg.Prefix = "go.typelink" // not go%2etypelink
-
itablinkpkg = mkpkg("go.itablink")
itablinkpkg.Name = "go.itablink"
itablinkpkg.Prefix = "go.itablink" // not go%2eitablink
@@ -141,24 +137,27 @@ func Main() {
typepkg = mkpkg("type")
typepkg.Name = "type"
+ // pseudo-package used for map zero values
+ mappkg = mkpkg("go.map")
+ mappkg.Name = "go.map"
+ mappkg.Prefix = "go.map"
+
goroot = obj.Getgoroot()
goos = obj.Getgoos()
Nacl = goos == "nacl"
if Nacl {
- flag_largemodel = 1
+ flag_largemodel = true
}
- outfile = ""
- obj.Flagcount("+", "compiling runtime", &compiling_runtime)
+ flag.BoolVar(&compiling_runtime, "+", false, "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)
+ flag.StringVar(&localimport, "D", "", "set relative `path` for local imports")
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'])
@@ -166,61 +165,52 @@ func Main() {
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.Flagstr("buildid", "record `id` as the build id in the export metadata", &buildid)
- obj.Flagcount("complete", "compiling complete package (no C or assembly)", &pure_go)
- obj.Flagstr("d", "print debug information about items in `list`", &debugstr)
+ flag.StringVar(&asmhdr, "asmhdr", "", "write assembly header to `file`")
+ flag.StringVar(&buildid, "buildid", "", "record `id` as the build id in the export metadata")
+ flag.BoolVar(&pure_go, "complete", false, "compiling complete package (no C or assembly)")
+ flag.StringVar(&debugstr, "d", "", "print debug information about items in `list`")
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.Flagfn1("importmap", "add `definition` of the form source=actual to import map", addImportMap)
- obj.Flagstr("installsuffix", "set pkg directory `suffix`", &flag_installsuffix)
+ flag.StringVar(&flag_installsuffix, "installsuffix", "", "set pkg directory `suffix`")
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("msan", "build code compatible with C/C++ memory sanitizer", &flag_msan)
- obj.Flagcount("newexport", "use new export format", &newexport) // TODO(gri) remove eventually (issue 13241)
- 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)
+ flag.BoolVar(&flag_msan, "msan", false, "build code compatible with C/C++ memory sanitizer")
+ flag.BoolVar(&newexport, "newexport", true, "use new export format") // TODO(gri) remove eventually (issue 15323)
+ flag.BoolVar(&nolocalimports, "nolocalimports", false, "reject local (relative) imports")
+ flag.StringVar(&outfile, "o", "", "write output to `file`")
+ flag.StringVar(&myimportpath, "p", "", "set expected package import `path`")
+ flag.BoolVar(&writearchive, "pack", false, "write package file instead of object file")
obj.Flagcount("r", "debug generated wrappers", &Debug['r'])
- obj.Flagcount("race", "enable race detector", &flag_race)
+ flag.BoolVar(&flag_race, "race", false, "enable race detector")
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)
+ flag.StringVar(&Ctxt.LineHist.TrimPathPrefix, "trimpath", "", "remove `prefix` from recorded source file paths")
+ flag.BoolVar(&safemode, "u", false, "reject unsafe code")
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)
+ flag.BoolVar(&use_writebarrier, "wb", true, "enable write barrier")
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_shared bool
var flag_dynlink bool
- switch Thearch.Thechar {
- case '5', '6', '7', '8', '9':
- obj.Flagcount("shared", "generate code that can be linked into a shared library", &flag_shared)
- }
- if Thearch.Thechar == '6' {
- obj.Flagcount("largemodel", "generate code that assumes a large memory model", &flag_largemodel)
- }
- switch Thearch.Thechar {
- case '5', '6', '7', '8', '9':
+ if supportsDynlink(Thearch.LinkArch.Arch) {
+ flag.BoolVar(&flag_shared, "shared", false, "generate code that can be linked into a shared library")
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)
+ if Thearch.LinkArch.Family == sys.AMD64 {
+ flag.BoolVar(&flag_largemodel, "largemodel", false, "generate code that assumes a large memory model")
+ }
+ flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to `file`")
+ flag.StringVar(&memprofile, "memprofile", "", "write memory profile to `file`")
+ flag.Int64Var(&memprofilerate, "memprofilerate", 0, "set runtime.MemProfileRate to `rate`")
flag.BoolVar(&ssaEnabled, "ssa", true, "use SSA backend to generate code")
obj.Flagparse(usage)
- if flag_dynlink {
- flag_shared = 1
- }
- Ctxt.Flag_shared = int32(flag_shared)
+ Ctxt.Flag_shared = flag_dynlink || flag_shared
Ctxt.Flag_dynlink = flag_dynlink
Ctxt.Flag_optimize = Debug['N'] == 0
@@ -233,17 +223,17 @@ func Main() {
startProfile()
- if flag_race != 0 {
+ if flag_race {
racepkg = mkpkg("runtime/race")
racepkg.Name = "race"
}
- if flag_msan != 0 {
+ if flag_msan {
msanpkg = mkpkg("runtime/msan")
msanpkg.Name = "msan"
}
- if flag_race != 0 && flag_msan != 0 {
+ if flag_race && flag_msan {
log.Fatal("cannot use both -race and -msan")
- } else if flag_race != 0 || flag_msan != 0 {
+ } else if flag_race || flag_msan {
instrumenting = true
}
@@ -301,9 +291,9 @@ func Main() {
}
Thearch.Betypeinit()
- if Widthptr == 0 {
- Fatalf("betypeinit failed")
- }
+ Widthint = Thearch.LinkArch.IntSize
+ Widthptr = Thearch.LinkArch.PtrSize
+ Widthreg = Thearch.LinkArch.RegSize
initUniverse()
@@ -422,7 +412,7 @@ func Main() {
// Typecheck imported function bodies if debug['l'] > 1,
// otherwise lazily when used or re-exported.
for _, n := range importlist {
- if len(n.Func.Inl.Slice()) != 0 {
+ if n.Func.Inl.Len() != 0 {
saveerrors()
typecheckinl(n)
}
@@ -479,7 +469,7 @@ func Main() {
fninit(xtop)
}
- if compiling_runtime != 0 {
+ if compiling_runtime {
checknowritebarrierrec()
}
@@ -577,7 +567,7 @@ func islocalname(name string) bool {
func findpkg(name string) (file string, ok bool) {
if islocalname(name) {
- if safemode != 0 || nolocalimports != 0 {
+ if safemode || nolocalimports {
return "", false
}
@@ -620,10 +610,10 @@ func findpkg(name string) (file string, ok bool) {
if flag_installsuffix != "" {
suffixsep = "_"
suffix = flag_installsuffix
- } else if flag_race != 0 {
+ } else if flag_race {
suffixsep = "_"
suffix = "race"
- } else if flag_msan != 0 {
+ } else if flag_msan {
suffixsep = "_"
suffix = "msan"
}
@@ -653,11 +643,24 @@ func loadsys() {
iota_ = -1000000
incannedimport = 1
- importpkg = Runtimepkg
- parse_import(bufio.NewReader(strings.NewReader(runtimeimport)), nil)
-
- importpkg = unsafepkg
- parse_import(bufio.NewReader(strings.NewReader(unsafeimport)), nil)
+ // The first byte in the binary export format is a 'c' or 'd'
+ // specifying the encoding format. We could just check that
+ // byte, but this is a perhaps more robust. Also, it is not
+ // speed-critical.
+ // TODO(gri) simplify once textual export format has gone
+ if strings.HasPrefix(runtimeimport, "package") {
+ // textual export format
+ importpkg = Runtimepkg
+ parse_import(bufio.NewReader(strings.NewReader(runtimeimport)), nil)
+ importpkg = unsafepkg
+ parse_import(bufio.NewReader(strings.NewReader(unsafeimport)), nil)
+ } else {
+ // binary export format
+ importpkg = Runtimepkg
+ Import(bufio.NewReader(strings.NewReader(runtimeimport)))
+ importpkg = unsafepkg
+ Import(bufio.NewReader(strings.NewReader(unsafeimport)))
+ }
importpkg = nil
incannedimport = 0
@@ -702,7 +705,7 @@ func importfile(f *Val, indent []byte) {
}
if path_ == "unsafe" {
- if safemode != 0 {
+ if safemode {
Yyerror("cannot import package unsafe")
errorexit()
}
@@ -826,7 +829,7 @@ func importfile(f *Val, indent []byte) {
errorexit()
}
- if safemode != 0 && !importpkg.Safe {
+ if safemode && !importpkg.Safe {
Yyerror("cannot import unsafe package %q", importpkg.Path)
}
}
@@ -904,7 +907,7 @@ func mkpackage(pkgname string) {
p = p[:i]
}
suffix := ".o"
- if writearchive > 0 {
+ if writearchive {
suffix = ".a"
}
outfile = p + suffix
diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go
index 99eb73bd94..c1132b6aac 100644
--- a/src/cmd/compile/internal/gc/obj.go
+++ b/src/cmd/compile/internal/gc/obj.go
@@ -5,6 +5,7 @@
package gc
import (
+ "cmd/internal/bio"
"cmd/internal/obj"
"crypto/sha256"
"fmt"
@@ -23,7 +24,7 @@ func formathdr(arhdr []byte, name string, size int64) {
func dumpobj() {
var err error
- bout, err = obj.Bopenw(outfile)
+ bout, err = bio.Create(outfile)
if err != nil {
Flusherrors()
fmt.Printf("can't create %s: %v\n", outfile, err)
@@ -32,36 +33,36 @@ func dumpobj() {
startobj := int64(0)
var arhdr [ArhdrSize]byte
- if writearchive != 0 {
- obj.Bwritestring(bout, "!<arch>\n")
+ if writearchive {
+ bout.WriteString("!<arch>\n")
arhdr = [ArhdrSize]byte{}
bout.Write(arhdr[:])
- startobj = obj.Boffset(bout)
+ startobj = bout.Offset()
}
fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring())
dumpexport()
- if writearchive != 0 {
+ if writearchive {
bout.Flush()
- size := obj.Boffset(bout) - startobj
+ size := bout.Offset() - startobj
if size&1 != 0 {
- obj.Bputc(bout, 0)
+ bout.WriteByte(0)
}
- obj.Bseek(bout, startobj-ArhdrSize, 0)
+ bout.Seek(startobj-ArhdrSize, 0)
formathdr(arhdr[:], "__.PKGDEF", size)
bout.Write(arhdr[:])
bout.Flush()
- obj.Bseek(bout, startobj+size+(size&1), 0)
+ bout.Seek(startobj+size+(size&1), 0)
arhdr = [ArhdrSize]byte{}
bout.Write(arhdr[:])
- startobj = obj.Boffset(bout)
+ startobj = bout.Offset()
fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring())
}
if pragcgobuf != "" {
- if writearchive != 0 {
+ if writearchive {
// write empty export section; must be before cgo section
fmt.Fprintf(bout, "\n$$\n\n$$\n\n")
}
@@ -86,21 +87,26 @@ func dumpobj() {
dumpglobls()
externdcl = tmp
+ if zerosize > 0 {
+ zero := Pkglookup("zero", mappkg)
+ ggloblsym(zero, int32(zerosize), obj.DUPOK|obj.RODATA)
+ }
+
dumpdata()
- obj.Writeobjdirect(Ctxt, bout)
+ obj.Writeobjdirect(Ctxt, bout.Writer)
- if writearchive != 0 {
+ if writearchive {
bout.Flush()
- size := obj.Boffset(bout) - startobj
+ size := bout.Offset() - startobj
if size&1 != 0 {
- obj.Bputc(bout, 0)
+ bout.WriteByte(0)
}
- obj.Bseek(bout, startobj-ArhdrSize, 0)
+ bout.Seek(startobj-ArhdrSize, 0)
formathdr(arhdr[:], "_go_.o", size)
bout.Write(arhdr[:])
}
- obj.Bterm(bout)
+ bout.Close()
}
func dumpglobls() {
@@ -132,9 +138,9 @@ func dumpglobls() {
funcsyms = nil
}
-func Bputname(b *obj.Biobuf, s *obj.LSym) {
- obj.Bwritestring(b, s.Name)
- obj.Bputc(b, 0)
+func Bputname(b *bio.Writer, s *obj.LSym) {
+ b.WriteString(s.Name)
+ b.WriteByte(0)
}
func Linksym(s *Sym) *obj.LSym {
@@ -320,9 +326,15 @@ func dsymptrLSym(s *obj.LSym, off int, x *obj.LSym, xoff int) int {
return off
}
+func dsymptrOffLSym(s *obj.LSym, off int, x *obj.LSym, xoff int) int {
+ s.WriteOff(Ctxt, int64(off), x, int64(xoff))
+ off += 4
+ return off
+}
+
func gdata(nam *Node, nr *Node, wid int) {
if nam.Op != ONAME {
- Fatalf("gdata nam op %v", opnames[nam.Op])
+ Fatalf("gdata nam op %v", nam.Op)
}
if nam.Sym == nil {
Fatalf("gdata nil nam sym")
@@ -330,20 +342,23 @@ func gdata(nam *Node, nr *Node, wid int) {
switch nr.Op {
case OLITERAL:
- switch nr.Val().Ctype() {
- case CTCPLX:
- gdatacomplex(nam, nr.Val().U.(*Mpcplx))
+ switch u := nr.Val().U.(type) {
+ case *Mpcplx:
+ gdatacomplex(nam, u)
- case CTSTR:
- gdatastring(nam, nr.Val().U.(string))
+ case string:
+ gdatastring(nam, u)
- case CTINT, CTRUNE, CTBOOL:
- i, _ := nr.IntLiteral()
+ case bool:
+ i := int64(obj.Bool2int(u))
Linksym(nam.Sym).WriteInt(Ctxt, nam.Xoffset, wid, i)
- case CTFLT:
+ case *Mpint:
+ Linksym(nam.Sym).WriteInt(Ctxt, nam.Xoffset, wid, u.Int64())
+
+ case *Mpflt:
s := Linksym(nam.Sym)
- f := nr.Val().U.(*Mpflt).Float64()
+ f := u.Float64()
switch nam.Type.Etype {
case TFLOAT32:
s.WriteFloat32(Ctxt, nam.Xoffset, float32(f))
@@ -357,7 +372,7 @@ func gdata(nam *Node, nr *Node, wid int) {
case OADDR:
if nr.Left.Op != ONAME {
- Fatalf("gdata ADDR left op %s", opnames[nr.Left.Op])
+ Fatalf("gdata ADDR left op %s", nr.Left.Op)
}
to := nr.Left
Linksym(nam.Sym).WriteAddr(Ctxt, nam.Xoffset, wid, Linksym(to.Sym), to.Xoffset)
@@ -369,7 +384,7 @@ func gdata(nam *Node, nr *Node, wid int) {
Linksym(nam.Sym).WriteAddr(Ctxt, nam.Xoffset, wid, Linksym(funcsym(nr.Sym)), nr.Xoffset)
default:
- Fatalf("gdata unhandled op %v %v\n", nr, opnames[nr.Op])
+ Fatalf("gdata unhandled op %v %v\n", nr, nr.Op)
}
}
diff --git a/src/cmd/compile/internal/gc/opnames.go b/src/cmd/compile/internal/gc/opnames.go
index df0d8cb7fb..015baa2376 100644
--- a/src/cmd/compile/internal/gc/opnames.go
+++ b/src/cmd/compile/internal/gc/opnames.go
@@ -160,9 +160,9 @@ var opnames = []string{
OLROT: "LROT",
ORROTC: "RROTC",
ORETJMP: "RETJMP",
- OPS: "OPS",
- OPC: "OPC",
- OSQRT: "OSQRT",
- OGETG: "OGETG",
+ OPS: "PS",
+ OPC: "PC",
+ OSQRT: "SQRT",
+ OGETG: "GETG",
OEND: "END",
}
diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go
index 8410a236cd..7026ad79ef 100644
--- a/src/cmd/compile/internal/gc/order.go
+++ b/src/cmd/compile/internal/gc/order.go
@@ -165,7 +165,7 @@ func ordersafeexpr(n *Node, order *Order) *Node {
a.Right = r
return typecheck(&a, Erv)
default:
- Fatalf("ordersafeexpr %v", Oconv(n.Op, 0))
+ Fatalf("ordersafeexpr %v", n.Op)
return nil // not reached
}
}
@@ -324,7 +324,7 @@ func ismulticall(l Nodes) bool {
// Copyret emits t1, t2, ... = n, where n is a function call,
// and then returns the list t1, t2, ....
func copyret(n *Node, order *Order) []*Node {
- if !n.Type.IsStruct() || !n.Type.Funarg {
+ if !n.Type.IsFuncArgStruct() {
Fatalf("copyret %v %d", n.Type, n.Left.Type.Results().NumFields())
}
@@ -373,7 +373,7 @@ func ordercall(n *Node, order *Order) {
if t == nil {
break
}
- if t.Note != nil && *t.Note == unsafeUintptrTag {
+ if t.Note == unsafeUintptrTag {
xp := n.List.Addr(i)
for (*xp).Op == OCONVNOP && !(*xp).Type.IsPtr() {
xp = &(*xp).Left
@@ -416,7 +416,7 @@ func ordercall(n *Node, order *Order) {
func ordermapassign(n *Node, order *Order) {
switch n.Op {
default:
- Fatalf("ordermapassign %v", Oconv(n.Op, 0))
+ Fatalf("ordermapassign %v", n.Op)
case OAS:
order.out = append(order.out, n)
@@ -478,7 +478,7 @@ func orderstmt(n *Node, order *Order) {
switch n.Op {
default:
- Fatalf("orderstmt %v", Oconv(n.Op, 0))
+ Fatalf("orderstmt %v", n.Op)
case OVARKILL, OVARLIVE:
order.out = append(order.out, n)
@@ -731,7 +731,7 @@ func orderstmt(n *Node, order *Order) {
default:
Fatalf("orderstmt range %v", n.Type)
- case TARRAY:
+ case TARRAY, TSLICE:
if n.List.Len() < 2 || isblank(n.List.Second()) {
// for i := range x will only use x once, to compute len(x).
// No need to copy it.
@@ -790,7 +790,7 @@ func orderstmt(n *Node, order *Order) {
var r *Node
for _, n2 := range n.List.Slice() {
if n2.Op != OXCASE {
- Fatalf("order select case %v", Oconv(n2.Op, 0))
+ Fatalf("order select case %v", n2.Op)
}
r = n2.Left
setlineno(n2)
@@ -803,7 +803,7 @@ func orderstmt(n *Node, order *Order) {
if r != nil {
switch r.Op {
default:
- Yyerror("unknown op in select %v", Oconv(r.Op, 0))
+ Yyerror("unknown op in select %v", r.Op)
Dump("select case", r)
// If this is case x := <-ch or case x, y := <-ch, the case has
@@ -943,7 +943,7 @@ func orderstmt(n *Node, order *Order) {
n.Left = orderexpr(n.Left, order, nil)
for _, n4 := range n.List.Slice() {
if n4.Op != OXCASE {
- Fatalf("order switch case %v", Oconv(n4.Op, 0))
+ Fatalf("order switch case %v", n4.Op)
}
orderexprlistinplace(n4.List, order)
orderblockNodes(&n4.Nbody)
@@ -1123,30 +1123,22 @@ func orderexpr(n *Node, order *Order, lhs *Node) *Node {
n = ordercopyexpr(n, n.Type, order, 0)
}
- case OSLICE, OSLICEARR, OSLICESTR:
+ case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR:
n.Left = orderexpr(n.Left, order, nil)
- n.Right.Left = orderexpr(n.Right.Left, order, nil)
- n.Right.Left = ordercheapexpr(n.Right.Left, order)
- n.Right.Right = orderexpr(n.Right.Right, order, nil)
- n.Right.Right = ordercheapexpr(n.Right.Right, order)
- if lhs == nil || lhs.Op != ONAME && !samesafeexpr(lhs, n.Left) {
- n = ordercopyexpr(n, n.Type, order, 0)
- }
-
- case OSLICE3, OSLICE3ARR:
- n.Left = orderexpr(n.Left, order, nil)
- n.Right.Left = orderexpr(n.Right.Left, order, nil)
- n.Right.Left = ordercheapexpr(n.Right.Left, order)
- n.Right.Right.Left = orderexpr(n.Right.Right.Left, order, nil)
- n.Right.Right.Left = ordercheapexpr(n.Right.Right.Left, order)
- n.Right.Right.Right = orderexpr(n.Right.Right.Right, order, nil)
- n.Right.Right.Right = ordercheapexpr(n.Right.Right.Right, order)
+ low, high, max := n.SliceBounds()
+ low = orderexpr(low, order, nil)
+ low = ordercheapexpr(low, order)
+ high = orderexpr(high, order, nil)
+ high = ordercheapexpr(high, order)
+ max = orderexpr(max, order, nil)
+ max = ordercheapexpr(max, order)
+ n.SetSliceBounds(low, high, max)
if lhs == nil || lhs.Op != ONAME && !samesafeexpr(lhs, n.Left) {
n = ordercopyexpr(n, n.Type, order, 0)
}
case OCLOSURE:
- if n.Noescape && len(n.Func.Cvars.Slice()) > 0 {
+ if n.Noescape && n.Func.Cvars.Len() > 0 {
prealloc[n] = ordertemp(Types[TUINT8], order, false) // walk will fill in correct type
}
diff --git a/src/cmd/compile/internal/gc/parser.go b/src/cmd/compile/internal/gc/parser.go
index 6538877e68..55f352590b 100644
--- a/src/cmd/compile/internal/gc/parser.go
+++ b/src/cmd/compile/internal/gc/parser.go
@@ -102,6 +102,9 @@ func (p *parser) syntax_error(msg string) {
tok = "name"
}
case LLITERAL:
+ if litbuf == "" {
+ litbuf = "literal " + lexbuf.String()
+ }
tok = litbuf
case LOPER:
tok = goopnames[p.op]
@@ -1408,20 +1411,17 @@ loop:
}
x = Nod(OINDEX, x, i)
case 1:
- i := index[0]
- j := index[1]
- x = Nod(OSLICE, x, Nod(OKEY, i, j))
+ x = Nod(OSLICE, x, nil)
+ x.SetSliceBounds(index[0], index[1], nil)
case 2:
- i := index[0]
- j := index[1]
- k := index[2]
- if j == nil {
+ if index[1] == nil {
Yyerror("middle index required in 3-index slice")
}
- if k == nil {
+ if index[2] == nil {
Yyerror("final index required in 3-index slice")
}
- x = Nod(OSLICE3, x, Nod(OKEY, i, Nod(OKEY, j, k)))
+ x = Nod(OSLICE3, x, nil)
+ x.SetSliceBounds(index[0], index[1], index[2])
default:
panic("unreachable")
@@ -2906,7 +2906,7 @@ func (p *parser) hidden_import() {
if Debug['E'] > 0 {
fmt.Printf("import [%q] func %v \n", importpkg.Path, s2)
- if Debug['m'] > 2 && len(s2.Func.Inl.Slice()) != 0 {
+ if Debug['m'] > 2 && s2.Func.Inl.Len() != 0 {
fmt.Printf("inl body:%v\n", s2.Func.Inl)
}
}
@@ -3246,17 +3246,14 @@ func (p *parser) hidden_literal() *Node {
if p.tok == LLITERAL {
ss := nodlit(p.val)
p.next()
- switch ss.Val().Ctype() {
- case CTINT, CTRUNE:
- ss.Val().U.(*Mpint).Neg()
- break
- case CTFLT:
- ss.Val().U.(*Mpflt).Neg()
- break
- case CTCPLX:
- ss.Val().U.(*Mpcplx).Real.Neg()
- ss.Val().U.(*Mpcplx).Imag.Neg()
- break
+ switch u := ss.Val().U.(type) {
+ case *Mpint:
+ u.Neg()
+ case *Mpflt:
+ u.Neg()
+ case *Mpcplx:
+ u.Real.Neg()
+ u.Imag.Neg()
default:
Yyerror("bad negated constant")
}
diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go
index efe10a419c..984d468bc6 100644
--- a/src/cmd/compile/internal/gc/pgen.go
+++ b/src/cmd/compile/internal/gc/pgen.go
@@ -7,6 +7,7 @@ package gc
import (
"cmd/compile/internal/ssa"
"cmd/internal/obj"
+ "cmd/internal/sys"
"crypto/md5"
"fmt"
"sort"
@@ -90,7 +91,7 @@ func gvardefx(n *Node, as obj.As) {
Fatalf("gvardef nil")
}
if n.Op != ONAME {
- Yyerror("gvardef %v; %v", Oconv(n.Op, FmtSharp), n)
+ Yyerror("gvardef %v; %v", oconv(n.Op, FmtSharp), n)
return
}
@@ -286,7 +287,7 @@ func allocauto(ptxt *obj.Prog) {
if haspointers(n.Type) {
stkptrsize = Stksize
}
- if Thearch.Thechar == '0' || Thearch.Thechar == '5' || Thearch.Thechar == '7' || Thearch.Thechar == '9' {
+ if Thearch.LinkArch.InFamily(sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64, sys.S390X) {
Stksize = Rnd(Stksize, int64(Widthptr))
}
if Stksize >= 1<<31 {
@@ -323,7 +324,12 @@ func Cgen_checknil(n *Node) {
Fatalf("bad checknil")
}
- if ((Thearch.Thechar == '0' || Thearch.Thechar == '5' || Thearch.Thechar == '7' || Thearch.Thechar == '9') && n.Op != OREGISTER) || !n.Addable || n.Op == OLITERAL {
+ // Most architectures require that the address to be checked is
+ // in a register (it could be in memory).
+ needsReg := !Thearch.LinkArch.InFamily(sys.AMD64, sys.I386)
+
+ // Move the address to be checked into a register if necessary.
+ if (needsReg && n.Op != OREGISTER) || !n.Addable || n.Op == OLITERAL {
var reg Node
Regalloc(&reg, Types[Tptr], n)
Cgen(n, &reg)
@@ -357,8 +363,8 @@ func compile(fn *Node) {
Curfn = fn
dowidth(Curfn.Type)
- if len(fn.Nbody.Slice()) == 0 {
- if pure_go != 0 || strings.HasPrefix(fn.Func.Nname.Sym.Name, "init.") {
+ if fn.Nbody.Len() == 0 {
+ if pure_go || strings.HasPrefix(fn.Func.Nname.Sym.Name, "init.") {
Yyerror("missing function body for %q", fn.Func.Nname.Sym.Name)
return
}
@@ -375,7 +381,7 @@ func compile(fn *Node) {
// set up domain for labels
clearlabels()
- if Curfn.Type.Outnamed {
+ if Curfn.Type.FuncType().Outnamed {
// add clearing of the output parameters
for _, t := range Curfn.Type.Results().Fields().Slice() {
if t.Nname != nil {
diff --git a/src/cmd/compile/internal/gc/pgen_test.go b/src/cmd/compile/internal/gc/pgen_test.go
index fcb8bfa0c2..44dc1db12e 100644
--- a/src/cmd/compile/internal/gc/pgen_test.go
+++ b/src/cmd/compile/internal/gc/pgen_test.go
@@ -10,6 +10,14 @@ import (
"testing"
)
+func typeWithoutPointers() *Type {
+ return &Type{Etype: TSTRUCT, Extra: &StructType{Haspointers: 1}} // haspointers -> false
+}
+
+func typeWithPointers() *Type {
+ return &Type{Etype: TSTRUCT, Extra: &StructType{Haspointers: 2}} // haspointers -> true
+}
+
// Test all code paths for cmpstackvarlt.
func TestCmpstackvar(t *testing.T) {
testdata := []struct {
@@ -62,13 +70,13 @@ func TestCmpstackvar(t *testing.T) {
false,
},
{
- Node{Class: PAUTO, Type: &Type{Haspointers: 1}}, // haspointers -> false
- Node{Class: PAUTO, Type: &Type{Haspointers: 2}}, // haspointers -> true
+ Node{Class: PAUTO, Type: typeWithoutPointers()},
+ Node{Class: PAUTO, Type: typeWithPointers()},
false,
},
{
- Node{Class: PAUTO, Type: &Type{Haspointers: 2}}, // haspointers -> true
- Node{Class: PAUTO, Type: &Type{Haspointers: 1}}, // haspointers -> false
+ Node{Class: PAUTO, Type: typeWithPointers()},
+ Node{Class: PAUTO, Type: typeWithoutPointers()},
true,
},
{
@@ -127,7 +135,7 @@ func TestStackvarSort(t *testing.T) {
{Class: PFUNC, Xoffset: 10, Type: &Type{}, Name: &Name{}, Sym: &Sym{}},
{Class: PFUNC, Xoffset: 20, Type: &Type{}, Name: &Name{}, Sym: &Sym{}},
{Class: PAUTO, Used: true, Type: &Type{}, Name: &Name{}, Sym: &Sym{}},
- {Class: PAUTO, Type: &Type{Haspointers: 1}, Name: &Name{}, Sym: &Sym{}}, // haspointers -> false
+ {Class: PAUTO, Type: typeWithoutPointers(), Name: &Name{}, Sym: &Sym{}},
{Class: PAUTO, Type: &Type{}, Name: &Name{}, Sym: &Sym{}},
{Class: PAUTO, Type: &Type{}, Name: &Name{Needzero: true}, Sym: &Sym{}},
{Class: PAUTO, Type: &Type{Width: 1}, Name: &Name{}, Sym: &Sym{}},
@@ -148,7 +156,7 @@ func TestStackvarSort(t *testing.T) {
{Class: PAUTO, Type: &Type{}, Name: &Name{}, Sym: &Sym{}},
{Class: PAUTO, Type: &Type{}, Name: &Name{}, Sym: &Sym{Name: "abc"}},
{Class: PAUTO, Type: &Type{}, Name: &Name{}, Sym: &Sym{Name: "xyz"}},
- {Class: PAUTO, Type: &Type{Haspointers: 1}, Name: &Name{}, Sym: &Sym{}}, // haspointers -> false
+ {Class: PAUTO, Type: typeWithoutPointers(), Name: &Name{}, Sym: &Sym{}},
}
// haspointers updates Type.Haspointers as a side effect, so
// exercise this function on all inputs so that reflect.DeepEqual
diff --git a/src/cmd/compile/internal/gc/plive.go b/src/cmd/compile/internal/gc/plive.go
index 43f594e2ea..e04c8563b1 100644
--- a/src/cmd/compile/internal/gc/plive.go
+++ b/src/cmd/compile/internal/gc/plive.go
@@ -17,6 +17,7 @@ package gc
import (
"cmd/internal/obj"
+ "cmd/internal/sys"
"fmt"
"sort"
"strings"
@@ -917,18 +918,17 @@ func onebitwalktype1(t *Type, xoffset *int64, bv Bvec) {
bvset(bv, int32(*xoffset/int64(Widthptr)+1)) // pointer in second slot
*xoffset += t.Width
+ case TSLICE:
+ // struct { byte *array; uintgo len; uintgo cap; }
+ if *xoffset&int64(Widthptr-1) != 0 {
+ Fatalf("onebitwalktype1: invalid TARRAY alignment, %v", t)
+ }
+ bvset(bv, int32(*xoffset/int64(Widthptr))) // pointer in first slot (BitsPointer)
+ *xoffset += t.Width
+
case TARRAY:
- if t.IsSlice() {
- // struct { byte *array; uintgo len; uintgo cap; }
- if *xoffset&int64(Widthptr-1) != 0 {
- Fatalf("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.NumElem(); i++ {
- onebitwalktype1(t.Elem(), xoffset, bv)
- }
+ for i := int64(0); i < t.NumElem(); i++ {
+ onebitwalktype1(t.Elem(), xoffset, bv)
}
case TSTRUCT:
@@ -1396,7 +1396,7 @@ func livenessepilogue(lv *Liveness) {
// The instruction before a call to deferreturn is always a
// no-op, to keep PC-specific data unambiguous.
prev := p.Opt.(*obj.Prog)
- if Ctxt.Arch.Thechar == '9' {
+ if Ctxt.Arch.Family == sys.PPC64 {
// On ppc64 there is an additional instruction
// (another no-op or reload of toc pointer) before
// the call.
diff --git a/src/cmd/compile/internal/gc/popt.go b/src/cmd/compile/internal/gc/popt.go
index 41f8ba9fcc..001a715f7b 100644
--- a/src/cmd/compile/internal/gc/popt.go
+++ b/src/cmd/compile/internal/gc/popt.go
@@ -235,6 +235,34 @@ func fixjmp(firstp *obj.Prog) {
// for every f.Data field, for use by the client.
// If newData is nil, f.Data will be nil.
+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
+}
+
+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 bool // diagnostic generated
+
+ Data interface{} // for use by client
+}
+
var flowmark int
// MaxFlowProg is the maximum size program (counted in instructions)
diff --git a/src/cmd/compile/internal/gc/racewalk.go b/src/cmd/compile/internal/gc/racewalk.go
index 09889a40f3..5bcaf89d50 100644
--- a/src/cmd/compile/internal/gc/racewalk.go
+++ b/src/cmd/compile/internal/gc/racewalk.go
@@ -54,14 +54,14 @@ func instrument(fn *Node) {
return
}
- if flag_race == 0 || !ispkgin(norace_inst_pkgs) {
+ if !flag_race || !ispkgin(norace_inst_pkgs) {
instrumentlist(fn.Nbody, nil)
// nothing interesting for race detector in fn->enter
instrumentlist(fn.Func.Exit, nil)
}
- if flag_race != 0 {
+ if flag_race {
// nodpc is the PC of the caller as extracted by
// getcallerpc. We use -widthptr(FP) for x86.
// BUG: this will not work on arm.
@@ -132,7 +132,7 @@ func instrumentnode(np **Node, init *Nodes, wr int, skip int) {
switch n.Op {
default:
- Fatalf("instrument: unknown node type %v", Oconv(n.Op, 0))
+ Fatalf("instrument: unknown node type %v", n.Op)
case OAS, OASWB, OAS2FUNC:
instrumentnode(&n.Left, init, 1, 0)
@@ -164,7 +164,13 @@ func instrumentnode(np **Node, init *Nodes, wr int, skip int) {
var outn Nodes
outn.Set(out)
instrumentnode(&ls[i], &outn, 0, 0)
- out = append(outn.Slice(), ls[i])
+ if ls[i].Op != OAS || ls[i].Ninit.Len() == 0 {
+ out = append(outn.Slice(), ls[i])
+ } else {
+ // Splice outn onto end of ls[i].Ninit
+ ls[i].Ninit.AppendNodes(&outn)
+ out = append(out, ls[i])
+ }
}
}
n.List.Set(out)
@@ -301,7 +307,11 @@ func instrumentnode(np **Node, init *Nodes, wr int, skip int) {
case OSLICE, OSLICEARR, OSLICE3, OSLICE3ARR, OSLICESTR:
instrumentnode(&n.Left, init, 0, 0)
- instrumentnode(&n.Right, init, 0, 0)
+ low, high, max := n.SliceBounds()
+ instrumentnode(&low, init, 0, 0)
+ instrumentnode(&high, init, 0, 0)
+ instrumentnode(&max, init, 0, 0)
+ n.SetSliceBounds(low, high, max)
goto ret
case OKEY:
@@ -364,13 +374,13 @@ func instrumentnode(np **Node, init *Nodes, wr int, skip int) {
OAS2RECV,
OAS2MAPR,
OASOP:
- Yyerror("instrument: %v must be lowered by now", Oconv(n.Op, 0))
+ Yyerror("instrument: %v must be lowered by now", n.Op)
goto ret
// impossible nodes: only appear in backend.
case ORROTC, OEXTEND:
- Yyerror("instrument: %v cannot exist now", Oconv(n.Op, 0))
+ Yyerror("instrument: %v cannot exist now", n.Op)
goto ret
case OGETG:
@@ -497,7 +507,7 @@ func callinstr(np **Node, init *Nodes, wr int, skip int) bool {
n = treecopy(n, 0)
makeaddable(n)
var f *Node
- if flag_msan != 0 {
+ if flag_msan {
name := "msanread"
if wr != 0 {
name = "msanwrite"
@@ -509,7 +519,7 @@ func callinstr(np **Node, init *Nodes, wr int, skip int) bool {
Fatalf("instrument: %v badwidth", t)
}
f = mkcall(name, nil, init, uintptraddr(n), Nodintconst(w))
- } else if flag_race != 0 && (t.IsStruct() || t.IsArray()) {
+ } else if flag_race && (t.IsStruct() || t.IsArray()) {
name := "racereadrange"
if wr != 0 {
name = "racewriterange"
@@ -521,7 +531,7 @@ func callinstr(np **Node, init *Nodes, wr int, skip int) bool {
Fatalf("instrument: %v badwidth", t)
}
f = mkcall(name, nil, init, uintptraddr(n), Nodintconst(w))
- } else if flag_race != 0 {
+ } else if flag_race {
name := "raceread"
if wr != 0 {
name = "racewrite"
diff --git a/src/cmd/compile/internal/gc/range.go b/src/cmd/compile/internal/gc/range.go
index 6adf8e0d6d..9d3f79cdce 100644
--- a/src/cmd/compile/internal/gc/range.go
+++ b/src/cmd/compile/internal/gc/range.go
@@ -49,7 +49,7 @@ func typecheckrange(n *Node) {
Yyerror("cannot range over %v", Nconv(n.Right, FmtLong))
goto out
- case TARRAY:
+ case TARRAY, TSLICE:
t1 = Types[TINT]
t2 = t.Elem()
@@ -154,7 +154,7 @@ func walkrange(n *Node) {
v2 = n.List.Second()
}
- // n->list has no meaning anymore, clear it
+ // n.List has no meaning anymore, clear it
// to avoid erroneous processing by racewalk.
n.List.Set(nil)
@@ -164,7 +164,7 @@ func walkrange(n *Node) {
default:
Fatalf("walkrange")
- case TARRAY:
+ case TARRAY, TSLICE:
if memclrrange(n, v1, v2, a) {
lineno = lno
return
@@ -217,9 +217,9 @@ func walkrange(n *Node) {
n.Right.Ninit.Set1(a)
}
- // orderstmt allocated the iterator for us.
- // we only use a once, so no copy needed.
case TMAP:
+ // orderstmt allocated the iterator for us.
+ // we only use a once, so no copy needed.
ha := a
th := hiter(t)
@@ -254,8 +254,8 @@ func walkrange(n *Node) {
body = []*Node{a}
}
- // orderstmt arranged for a copy of the channel variable.
case TCHAN:
+ // orderstmt arranged for a copy of the channel variable.
ha := a
n.Left = nil
@@ -278,9 +278,13 @@ func walkrange(n *Node) {
} else {
body = []*Node{Nod(OAS, v1, hv1)}
}
+ // Zero hv1. This prevents hv1 from being the sole, inaccessible
+ // reference to an otherwise GC-able value during the next channel receive.
+ // See issue 15281.
+ body = append(body, Nod(OAS, hv1, nil))
- // orderstmt arranged for a copy of the string variable.
case TSTRING:
+ // orderstmt arranged for a copy of the string variable.
ha := a
ohv1 := temp(Types[TINT])
diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go
index 11bcd4cdc6..ceed55a2a5 100644
--- a/src/cmd/compile/internal/gc/reflect.go
+++ b/src/cmd/compile/internal/gc/reflect.go
@@ -22,6 +22,16 @@ type itabEntry struct {
var signatlist []*Node
var itabs []itabEntry
+type Sig struct {
+ name string
+ pkg *Pkg
+ isym *Sym
+ tsym *Sym
+ type_ *Type
+ mtype *Type
+ offset int32
+}
+
// byMethodNameAndPackagePath sorts method signatures by name, then package path.
type byMethodNameAndPackagePath []*Sig
@@ -60,12 +70,12 @@ const (
)
func structfieldSize() int { return 3 * Widthptr } // Sizeof(runtime.structfield{})
-func imethodSize() int { return 2 * Widthptr } // Sizeof(runtime.imethod{})
+func imethodSize() int { return 4 + 4 } // Sizeof(runtime.imethod{})
func uncommonSize(t *Type) int { // Sizeof(runtime.uncommontype{})
if t.Sym == nil && len(methods(t)) == 0 {
return 0
}
- return 2*Widthptr + 2*Widthint
+ return 4 + 2 + 2
}
func makefield(name string, t *Type) *Field {
@@ -76,8 +86,8 @@ func makefield(name string, t *Type) *Field {
}
func mapbucket(t *Type) *Type {
- if t.Bucket != nil {
- return t.Bucket
+ if t.MapType().Bucket != nil {
+ return t.MapType().Bucket
}
bucket := typ(TSTRUCT)
@@ -92,13 +102,18 @@ func mapbucket(t *Type) *Type {
valtype = Ptrto(valtype)
}
+ field := make([]*Field, 0, 5)
+
// The first field is: uint8 topbits[BUCKETSIZE].
arr := typArray(Types[TUINT8], BUCKETSIZE)
- field := make([]*Field, 0, 5)
field = append(field, makefield("topbits", arr))
+
arr = typArray(keytype, BUCKETSIZE)
+ arr.Noalg = true
field = append(field, makefield("keys", arr))
+
arr = typArray(valtype, BUCKETSIZE)
+ arr.Noalg = true
field = append(field, makefield("values", arr))
// Make sure the overflow pointer is the last memory in the struct,
@@ -147,17 +162,17 @@ func mapbucket(t *Type) *Type {
Yyerror("bad math in mapbucket for %v", t)
}
- t.Bucket = bucket
+ t.MapType().Bucket = bucket
- bucket.Map = t
+ bucket.StructType().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
+ if t.MapType().Hmap != nil {
+ return t.MapType().Hmap
}
bucket := mapbucket(t)
@@ -176,14 +191,14 @@ func hmap(t *Type) *Type {
h.Local = t.Local
h.SetFields(field[:])
dowidth(h)
- t.Hmap = h
- h.Map = t
+ t.MapType().Hmap = h
+ h.StructType().Map = t
return h
}
func hiter(t *Type) *Type {
- if t.Hiter != nil {
- return t.Hiter
+ if t.MapType().Hiter != nil {
+ return t.MapType().Hiter
}
// build a struct:
@@ -224,8 +239,8 @@ func hiter(t *Type) *Type {
if i.Width != int64(12*Widthptr) {
Yyerror("hash_iter size not correct %d %d", i.Width, 12*Widthptr)
}
- t.Hiter = i
- i.Map = t
+ t.MapType().Hiter = i
+ i.StructType().Map = t
return i
}
@@ -402,8 +417,6 @@ func imethods(t *Type) []*Sig {
return methods
}
-var dimportpath_gopkg *Pkg
-
func dimportpath(p *Pkg) {
if p.Pathsym != nil {
return
@@ -416,27 +429,18 @@ func dimportpath(p *Pkg) {
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
-
+ var str string
if p == localpkg {
// Note: myimportpath != "", or else dgopkgpath won't call dimportpath.
- gdatastring(n, myimportpath)
+ str = myimportpath
} else {
- gdatastring(n, p.Path)
+ str = p.Path
}
- ggloblsym(n.Sym, int32(Types[TSTRING].Width), obj.DUPOK|obj.RODATA)
+
+ s := obj.Linklookup(Ctxt, "type..importpath."+p.Prefix+".", 0)
+ ot := dnameData(s, 0, str, "", nil, false)
+ ggloblLSym(s, int32(ot), obj.DUPOK|obj.RODATA)
+ p.Pathsym = s
}
func dgopkgpath(s *Sym, ot int, pkg *Pkg) int {
@@ -451,15 +455,34 @@ func dgopkgpathLSym(s *obj.LSym, ot int, pkg *Pkg) int {
if pkg == localpkg && myimportpath == "" {
// If we don't know the full import path of the package being compiled
// (i.e. -p was not passed on the compiler command line), emit a reference to
- // go.importpath.""., which the linker will rewrite using the correct import path.
+ // type..importpath.""., which the linker will rewrite using the correct import path.
// Every package that imports this one directly defines the symbol.
// See also https://groups.google.com/forum/#!topic/golang-dev/myb9s53HxGQ.
- ns := obj.Linklookup(Ctxt, `go.importpath."".`, 0)
+ ns := obj.Linklookup(Ctxt, `type..importpath."".`, 0)
return dsymptrLSym(s, ot, ns, 0)
}
dimportpath(pkg)
- return dsymptrLSym(s, ot, Linksym(pkg.Pathsym), 0)
+ return dsymptrLSym(s, ot, pkg.Pathsym, 0)
+}
+
+// dgopkgpathOffLSym writes an offset relocation in s at offset ot to the pkg path symbol.
+func dgopkgpathOffLSym(s *obj.LSym, ot int, pkg *Pkg) int {
+ if pkg == nil {
+ return duintxxLSym(s, ot, 0, 4)
+ }
+ if pkg == localpkg && myimportpath == "" {
+ // If we don't know the full import path of the package being compiled
+ // (i.e. -p was not passed on the compiler command line), emit a reference to
+ // type..importpath.""., which the linker will rewrite using the correct import path.
+ // Every package that imports this one directly defines the symbol.
+ // See also https://groups.google.com/forum/#!topic/golang-dev/myb9s53HxGQ.
+ ns := obj.Linklookup(Ctxt, `type..importpath."".`, 0)
+ return dsymptrOffLSym(s, ot, ns, 0)
+ }
+
+ dimportpath(pkg)
+ return dsymptrOffLSym(s, ot, pkg.Pathsym, 0)
}
// isExportedField reports whether a struct field is exported.
@@ -478,20 +501,16 @@ func isExportedField(ft *Field) bool {
// dnameField dumps a reflect.name for a struct field.
func dnameField(s *Sym, ot int, ft *Field) int {
- var name, tag string
+ var name string
if ft.Sym != nil && ft.Embedded == 0 {
name = ft.Sym.Name
}
- if ft.Note != nil {
- tag = *ft.Note
- }
- return dname(s, ot, name, tag, nil, isExportedField(ft))
+ nsym := dname(name, ft.Note, nil, isExportedField(ft))
+ return dsymptrLSym(Linksym(s), ot, nsym, 0)
}
-var dnameCount int
-
-// dname dumps a reflect.name for a struct field or method.
-func dname(s *Sym, ot int, name, tag string, pkg *Pkg, exported bool) int {
+// dnameData writes the contents of a reflect.name into s at offset ot.
+func dnameData(s *obj.LSym, ot int, name, tag string, pkg *Pkg, exported bool) int {
if len(name) > 1<<16-1 {
Fatalf("name too long: %s", name)
}
@@ -524,31 +543,46 @@ func dname(s *Sym, ot int, name, tag string, pkg *Pkg, exported bool) int {
copy(tb[2:], tag)
}
- // Very few names require a pkgPath *string (only those
- // defined in a different package than their type). So if
- // there is no pkgPath, we treat the name contents as string
- // data that duplicates across packages.
- var bsym *obj.LSym
+ ot = int(s.WriteBytes(Ctxt, int64(ot), b))
+
+ if pkg != nil {
+ ot = dgopkgpathOffLSym(s, ot, pkg)
+ }
+
+ return ot
+}
+
+var dnameCount int
+
+// dname creates a reflect.name for a struct field or method.
+func dname(name, tag string, pkg *Pkg, exported bool) *obj.LSym {
+ // Write out data as "type.." to signal two things to the
+ // linker, first that when dynamically linking, the symbol
+ // should be moved to a relro section, and second that the
+ // contents should not be decoded as a type.
+ sname := "type..namedata."
if pkg == nil {
- _, bsym = stringsym(string(b))
+ // In the common case, share data with other packages.
+ if name == "" {
+ if exported {
+ sname += "-noname-exported." + tag
+ } else {
+ sname += "-noname-unexported." + tag
+ }
+ } else {
+ sname += name + "." + tag
+ }
} else {
- // Write out data as "type.." to signal two things to the
- // linker, first that when dynamically linking, the symbol
- // should be moved to a relro section, and second that the
- // contents should not be decoded as a type.
- bsymname := fmt.Sprintf(`type..methodname."".%d`, dnameCount)
+ sname = fmt.Sprintf(`%s"".%d`, sname, dnameCount)
dnameCount++
- bsym = obj.Linklookup(Ctxt, bsymname, 0)
- bsym.P = b
- boff := len(b)
- boff = int(Rnd(int64(boff), int64(Widthptr)))
- boff = dgopkgpathLSym(bsym, boff, pkg)
- ggloblLSym(bsym, int32(boff), obj.RODATA|obj.LOCAL)
}
-
- ot = dsymptrLSym(Linksym(s), ot, bsym, 0)
-
- return ot
+ s := obj.Linklookup(Ctxt, sname, 0)
+ if len(s.P) > 0 {
+ return s
+ }
+ ot := dnameData(s, 0, name, tag, pkg, exported)
+ ggloblLSym(s, int32(ot), obj.DUPOK|obj.RODATA)
+ return s
}
// dextratype dumps the fields of a runtime.uncommontype.
@@ -568,15 +602,19 @@ func dextratype(s *Sym, ot int, t *Type, dataAdd int) int {
dtypesym(a.type_)
}
- ot = dgopkgpath(s, ot, typePkg(t))
-
- // slice header
- ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint+dataAdd)
+ ot = dgopkgpathOffLSym(Linksym(s), ot, typePkg(t))
- n := len(m)
- ot = duintxx(s, ot, uint64(n), Widthint)
- ot = duintxx(s, ot, uint64(n), Widthint)
+ dataAdd += 4 + 2 + 2
+ mcount := len(m)
+ if mcount != int(uint16(mcount)) {
+ Fatalf("too many methods on %s: %d", t, mcount)
+ }
+ if dataAdd != int(uint16(dataAdd)) {
+ Fatalf("methods are too far away on %s: %d", t, dataAdd)
+ }
+ ot = duint16(s, ot, uint16(mcount))
+ ot = duint16(s, ot, uint16(dataAdd))
return ot
}
@@ -584,7 +622,7 @@ func typePkg(t *Type) *Pkg {
tsym := t.Sym
if tsym == nil {
switch t.Etype {
- case TARRAY, TPTR32, TPTR64, TCHAN:
+ case TARRAY, TSLICE, TPTR32, TPTR64, TCHAN:
if t.Elem() != nil {
tsym = t.Elem().Sym
}
@@ -599,6 +637,7 @@ func typePkg(t *Type) *Pkg {
// dextratypeData dumps the backing array for the []method field of
// runtime.uncommontype.
func dextratypeData(s *Sym, ot int, t *Type) int {
+ lsym := Linksym(s)
for _, a := range methods(t) {
// ../../../../runtime/type.go:/method
exported := exportname(a.name)
@@ -606,22 +645,24 @@ func dextratypeData(s *Sym, ot int, t *Type) int {
if !exported && a.pkg != typePkg(t) {
pkg = a.pkg
}
- ot = dname(s, ot, a.name, "", pkg, exported)
- ot = dmethodptr(s, ot, dtypesym(a.mtype))
- ot = dmethodptr(s, ot, a.isym)
- ot = dmethodptr(s, ot, a.tsym)
+ nsym := dname(a.name, "", pkg, exported)
+
+ ot = dsymptrOffLSym(lsym, ot, nsym, 0)
+ ot = dmethodptrOffLSym(lsym, ot, Linksym(dtypesym(a.mtype)))
+ ot = dmethodptrOffLSym(lsym, ot, Linksym(a.isym))
+ ot = dmethodptrOffLSym(lsym, ot, Linksym(a.tsym))
}
return ot
}
-func dmethodptr(s *Sym, off int, x *Sym) int {
- duintptr(s, off, 0)
- r := obj.Addrel(Linksym(s))
- r.Off = int32(off)
- r.Siz = uint8(Widthptr)
- r.Sym = Linksym(x)
- r.Type = obj.R_METHOD
- return off + Widthptr
+func dmethodptrOffLSym(s *obj.LSym, ot int, x *obj.LSym) int {
+ duintxxLSym(s, ot, 0, 4)
+ r := obj.Addrel(s)
+ r.Off = int32(ot)
+ r.Siz = 4
+ r.Sym = x
+ r.Type = obj.R_METHODOFF
+ return ot + 4
}
var kinds = []int{
@@ -647,6 +688,7 @@ var kinds = []int{
TCHAN: obj.KindChan,
TMAP: obj.KindMap,
TARRAY: obj.KindArray,
+ TSLICE: obj.KindSlice,
TFUNC: obj.KindFunc,
TCOMPLEX64: obj.KindComplex64,
TCOMPLEX128: obj.KindComplex128,
@@ -654,67 +696,46 @@ var kinds = []int{
}
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 TINT, TUINT, TINT8, TUINT8, TINT16, TUINT16, TINT32, TUINT32, TINT64,
+ TUINT64, TUINTPTR, TFLOAT32, TFLOAT64, TCOMPLEX64, TCOMPLEX128, TBOOL:
+ return false
+
+ case TSLICE:
+ return true
case TARRAY:
- if t.IsSlice() {
- ret = true
- break
+ at := t.Extra.(*ArrayType)
+ if at.Haspointers != 0 {
+ return at.Haspointers-1 != 0
}
- if t.NumElem() == 0 { // empty array
- ret = false
- break
+ ret := false
+ if t.NumElem() != 0 { // non-empty array
+ ret = haspointers(t.Elem())
}
- ret = haspointers(t.Elem())
+ at.Haspointers = 1 + uint8(obj.Bool2int(ret))
+ return ret
case TSTRUCT:
- ret = false
+ st := t.StructType()
+ if st.Haspointers != 0 {
+ return st.Haspointers-1 != 0
+ }
+
+ ret := false
for _, t1 := range t.Fields().Slice() {
if haspointers(t1.Type) {
ret = true
break
}
}
-
- case TSTRING,
- TPTR32,
- TPTR64,
- TUNSAFEPTR,
- TINTER,
- TCHAN,
- TMAP,
- TFUNC:
- fallthrough
- default:
- ret = true
+ st.Haspointers = 1 + uint8(obj.Bool2int(ret))
+ return ret
}
- t.Haspointers = 1 + uint8(obj.Bool2int(ret))
- return ret
+ return true
}
// typeptrdata returns the length in bytes of the prefix of t
@@ -742,11 +763,11 @@ func typeptrdata(t *Type) int64 {
// struct { Type *type; void *data; }
return 2 * int64(Widthptr)
+ case TSLICE:
+ // struct { byte *array; uintgo len; uintgo cap; }
+ return int64(Widthptr)
+
case TARRAY:
- if t.IsSlice() {
- // struct { byte *array; uintgo len; uintgo cap; }
- return int64(Widthptr)
- }
// haspointers already eliminated t.NumElem() == 0.
return (t.NumElem()-1)*t.Elem().Width + typeptrdata(t.Elem())
@@ -766,14 +787,21 @@ func typeptrdata(t *Type) int64 {
}
}
-// tflag is documented in ../../../../reflect/type.go.
-const tflagUncommon = 1
-
-// commonType
-// ../../../../runtime/type.go:/commonType
+// tflag is documented in reflect/type.go.
+//
+// tflag values must be kept in sync with copies in:
+// cmd/compile/internal/gc/reflect.go
+// cmd/link/internal/ld/decodesym.go
+// reflect/type.go
+// runtime/type.go
+const (
+ tflagUncommon = 1 << 0
+ tflagExtraStar = 1 << 1
+)
var dcommontype_algarray *Sym
+// dcommontype dumps the contents of a reflect.rtype (runtime._type).
func dcommontype(s *Sym, ot int, t *Type) int {
if ot != 0 {
Fatalf("dcommontype %d", ot)
@@ -814,7 +842,8 @@ func dcommontype(s *Sym, ot int, t *Type) int {
// kind uint8
// alg *typeAlg
// gcdata *byte
- // string *string
+ // str nameOff
+ // _ int32
// }
ot = duintptr(s, ot, uint64(t.Width))
ot = duintptr(s, ot, uint64(ptrdata))
@@ -825,6 +854,26 @@ func dcommontype(s *Sym, ot int, t *Type) int {
if uncommonSize(t) != 0 {
tflag |= tflagUncommon
}
+
+ exported := false
+ p := Tconv(t, FmtLeft|FmtUnsigned)
+ // If we're writing out type T,
+ // we are very likely to write out type *T as well.
+ // Use the string "*T"[1:] for "T", so that the two
+ // share storage. This is a cheap way to reduce the
+ // amount of space taken up by reflect strings.
+ if !strings.HasPrefix(p, "*") {
+ p = "*" + p
+ tflag |= tflagExtraStar
+ if t.Sym != nil {
+ exported = exportname(t.Sym.Name)
+ }
+ } else {
+ if t.Elem() != nil && t.Elem().Sym != nil {
+ exported = exportname(t.Elem().Sym.Name)
+ }
+ }
+
ot = duint8(s, ot, tflag)
// runtime (and common sense) expects alignment to be a power of two.
@@ -840,9 +889,6 @@ func dcommontype(s *Sym, ot int, t *Type) int {
ot = duint8(s, ot, t.Align) // fieldAlign
i = kinds[t.Etype]
- if t.IsSlice() {
- i = obj.KindSlice
- }
if !haspointers(t) {
i |= obj.KindNoPointers
}
@@ -860,21 +906,9 @@ func dcommontype(s *Sym, ot int, t *Type) int {
}
ot = dsymptr(s, ot, gcsym, 0) // gcdata
- p := Tconv(t, FmtLeft|FmtUnsigned)
-
- // If we're writing out type T,
- // we are very likely to write out type *T as well.
- // Use the string "*T"[1:] for "T", so that the two
- // share storage. This is a cheap way to reduce the
- // amount of space taken up by reflect strings.
- prefix := 0
- if !strings.HasPrefix(p, "*") {
- p = "*" + p
- prefix = 1
- }
- _, symdata := stringsym(p) // string
- ot = dsymptrLSym(Linksym(s), ot, symdata, prefix)
- ot = duintxx(s, ot, uint64(len(p)-prefix), Widthint)
+ nsym := dname(p, "", nil, exported)
+ ot = dsymptrOffLSym(Linksym(s), ot, nsym, 0)
+ ot = duint32(s, ot, 0)
return ot
}
@@ -889,23 +923,9 @@ func tracksym(t *Type, f *Field) *Sym {
return Pkglookup(Tconv(t, FmtLeft)+"."+f.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, FmtLeft|FmtUnsigned) + "\t" + Tconv(t, FmtLeft)
-
- s := Pkglookup(p, typelinkpkg)
-
- //print("typelinksym: %s -> %+S\n", p, s);
-
- return s
+func typelinkLSym(t *Type) *obj.LSym {
+ name := "go.typelink." + Tconv(t, FmtLeft) // complete, unambiguous type name
+ return obj.Linklookup(Ctxt, name, 0)
}
func typesymprefix(prefix string, t *Type) *Sym {
@@ -999,9 +1019,6 @@ func isreflexive(t *Type) bool {
return false
case TARRAY:
- if t.IsSlice() {
- Fatalf("slice can't be a map key: %v", t)
- }
return isreflexive(t.Elem())
case TSTRUCT:
@@ -1049,9 +1066,6 @@ func needkeyupdate(t *Type) bool {
return true
case TARRAY:
- if t.IsSlice() {
- Fatalf("slice can't be a map key: %v", t)
- }
return needkeyupdate(t.Elem())
case TSTRUCT:
@@ -1119,28 +1133,26 @@ ok:
ot = dextratype(s, ot, t, 0)
case TARRAY:
- if t.IsArray() {
- // ../../../../runtime/type.go:/arrayType
- s1 := dtypesym(t.Elem())
- t2 := typSlice(t.Elem())
- s2 := dtypesym(t2)
- ot = dcommontype(s, ot, t)
- ot = dsymptr(s, ot, s1, 0)
- ot = dsymptr(s, ot, s2, 0)
- ot = duintptr(s, ot, uint64(t.NumElem()))
- } else {
- // ../../../../runtime/type.go:/sliceType
- s1 := dtypesym(t.Elem())
+ // ../../../../runtime/type.go:/arrayType
+ s1 := dtypesym(t.Elem())
+ t2 := typSlice(t.Elem())
+ s2 := dtypesym(t2)
+ ot = dcommontype(s, ot, t)
+ ot = dsymptr(s, ot, s1, 0)
+ ot = dsymptr(s, ot, s2, 0)
+ ot = duintptr(s, ot, uint64(t.NumElem()))
+ ot = dextratype(s, ot, t, 0)
- ot = dcommontype(s, ot, t)
- ot = dsymptr(s, ot, s1, 0)
- }
+ case TSLICE:
+ // ../../../../runtime/type.go:/sliceType
+ s1 := dtypesym(t.Elem())
+ ot = dcommontype(s, ot, t)
+ ot = dsymptr(s, ot, s1, 0)
ot = dextratype(s, ot, t, 0)
- // ../../../../runtime/type.go:/chanType
case TCHAN:
+ // ../../../../runtime/type.go:/chanType
s1 := dtypesym(t.Elem())
-
ot = dcommontype(s, ot, t)
ot = dsymptr(s, ot, s1, 0)
ot = duintptr(s, ot, uint64(t.ChanDir()))
@@ -1207,6 +1219,7 @@ ok:
dataAdd := imethodSize() * n
ot = dextratype(s, ot, t, dataAdd)
+ lsym := Linksym(s)
for _, a := range m {
// ../../../../runtime/type.go:/imethod
exported := exportname(a.name)
@@ -1214,8 +1227,10 @@ ok:
if !exported && a.pkg != tpkg {
pkg = a.pkg
}
- ot = dname(s, ot, a.name, "", pkg, exported)
- ot = dsymptr(s, ot, dtypesym(a.type_), 0)
+ nsym := dname(a.name, "", pkg, exported)
+
+ ot = dsymptrOffLSym(lsym, ot, nsym, 0)
+ ot = dsymptrOffLSym(lsym, ot, Linksym(dtypesym(a.type_)), 0)
}
// ../../../../runtime/type.go:/mapType
@@ -1301,18 +1316,29 @@ ok:
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 {
+ // types in the binary, so the runtime can find them.
+ //
+ // When buildmode=shared, all types are in typelinks so the
+ // runtime can deduplicate type pointers.
+ keep := Ctxt.Flag_dynlink
+ if !keep && t.Sym == nil {
+ // For an unnamed type, we only need the link if the type can
+ // be created at run time by reflect.PtrTo and similar
+ // functions. If the type exists in the program, those
+ // functions must return the existing type structure rather
+ // than creating a new one.
switch t.Etype {
- case TPTR32, TPTR64, TARRAY, TCHAN, TFUNC, TMAP, TSTRUCT:
- slink := typelinksym(t)
- dsymptr(slink, 0, s, 0)
- ggloblsym(slink, int32(Widthptr), int16(dupok|obj.RODATA))
+ case TPTR32, TPTR64, TARRAY, TCHAN, TFUNC, TMAP, TSLICE, TSTRUCT:
+ keep = true
}
}
+ if keep {
+ slink := typelinkLSym(t)
+ dsymptrOffLSym(slink, 0, Linksym(s), 0)
+ ggloblLSym(slink, 4, int16(dupok|obj.RODATA))
+ }
return s
}
@@ -1365,6 +1391,11 @@ func dumptypestructs() {
}
// generate import strings for imported packages
+ if forceObjFileStability {
+ // Sorting the packages is not necessary but to compare binaries created
+ // using textual and binary format we sort by path to reduce differences.
+ sort.Sort(pkgByPath(pkgs))
+ }
for _, p := range pkgs {
if p.Direct {
dimportpath(p)
@@ -1393,16 +1424,22 @@ func dumptypestructs() {
// add paths for runtime and main, which 6l imports implicitly.
dimportpath(Runtimepkg)
- if flag_race != 0 {
+ if flag_race {
dimportpath(racepkg)
}
- if flag_msan != 0 {
+ if flag_msan {
dimportpath(msanpkg)
}
dimportpath(mkpkg("main"))
}
}
+type pkgByPath []*Pkg
+
+func (a pkgByPath) Len() int { return len(a) }
+func (a pkgByPath) Less(i, j int) bool { return a[i].Path < a[j].Path }
+func (a pkgByPath) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
func dalgsym(t *Type) *Sym {
var s *Sym
var hashfunc *Sym
@@ -1632,11 +1669,10 @@ func (p *GCProg) emit(t *Type, offset int64) {
p.w.Ptr(offset / int64(Widthptr))
p.w.Ptr(offset/int64(Widthptr) + 1)
+ case TSLICE:
+ p.w.Ptr(offset / int64(Widthptr))
+
case TARRAY:
- if t.IsSlice() {
- p.w.Ptr(offset / int64(Widthptr))
- return
- }
if t.NumElem() == 0 {
// should have been handled by haspointers check above
Fatalf("GCProg.emit: empty array")
@@ -1667,3 +1703,27 @@ func (p *GCProg) emit(t *Type, offset int64) {
}
}
}
+
+// zeroaddr returns the address of a symbol with at least
+// size bytes of zeros.
+func zeroaddr(size int64) *Node {
+ if size >= 1<<31 {
+ Fatalf("map value too big %d", size)
+ }
+ if zerosize < size {
+ zerosize = size
+ }
+ s := Pkglookup("zero", mappkg)
+ if s.Def == nil {
+ x := newname(s)
+ x.Type = Types[TUINT8]
+ x.Class = PEXTERN
+ x.Typecheck = 1
+ s.Def = x
+ }
+ z := Nod(OADDR, s.Def, nil)
+ z.Type = Ptrto(Types[TUINT8])
+ z.Addable = true
+ z.Typecheck = 1
+ return z
+}
diff --git a/src/cmd/compile/internal/gc/reg.go b/src/cmd/compile/internal/gc/reg.go
index 26746a5bcf..5763f79de1 100644
--- a/src/cmd/compile/internal/gc/reg.go
+++ b/src/cmd/compile/internal/gc/reg.go
@@ -33,6 +33,7 @@ package gc
import (
"bytes"
"cmd/internal/obj"
+ "cmd/internal/sys"
"fmt"
"sort"
"strings"
@@ -249,7 +250,7 @@ func addmove(r *Flow, bn int, rn int, f int) {
p1.As = Thearch.Optoas(OAS, Types[uint8(v.etype)])
// TODO(rsc): Remove special case here.
- if (Thearch.Thechar == '0' || Thearch.Thechar == '5' || Thearch.Thechar == '7' || Thearch.Thechar == '9') && v.etype == TBOOL {
+ if Thearch.LinkArch.InFamily(sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64) && v.etype == TBOOL {
p1.As = Thearch.Optoas(OAS, Types[TUINT8])
}
p1.From.Type = obj.TYPE_REG
@@ -302,7 +303,7 @@ func mkvar(f *Flow, a *obj.Addr) Bits {
// TODO(rsc): Remove special case here.
case obj.TYPE_ADDR:
var bit Bits
- if Thearch.Thechar == '0' || Thearch.Thechar == '5' || Thearch.Thechar == '7' || Thearch.Thechar == '9' {
+ if Thearch.LinkArch.InFamily(sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64) {
goto memcase
}
a.Type = obj.TYPE_MEM
@@ -368,7 +369,7 @@ func mkvar(f *Flow, a *obj.Addr) Bits {
if v.etype == et {
if int64(v.width) == w {
// TODO(rsc): Remove special case for arm here.
- if flag == 0 || Thearch.Thechar != '5' {
+ if flag == 0 || Thearch.LinkArch.Family != sys.ARM {
return blsh(uint(i))
}
}
@@ -487,7 +488,7 @@ func mkvar(f *Flow, a *obj.Addr) Bits {
}
if Debug['R'] != 0 {
- fmt.Printf("bit=%2d et=%v w=%d+%d %v %v flag=%d\n", i, Econv(et), o, w, Nconv(node, FmtSharp), Ctxt.Dconv(a), v.addr)
+ fmt.Printf("bit=%2d et=%v w=%d+%d %v %v flag=%d\n", i, et, o, w, Nconv(node, FmtSharp), Ctxt.Dconv(a), v.addr)
}
Ostats.Nvar++
@@ -651,7 +652,7 @@ func allreg(b uint64, r *Rgn) uint64 {
r.regno = 0
switch v.etype {
default:
- Fatalf("unknown etype %d/%v", Bitno(b), Econv(v.etype))
+ Fatalf("unknown etype %d/%v", Bitno(b), v.etype)
case TINT8,
TUINT8,
@@ -1114,7 +1115,7 @@ func regopt(firstp *obj.Prog) {
// Currently we never generate three register forms.
// If we do, this will need to change.
- if p.From3Type() != obj.TYPE_NONE {
+ if p.From3Type() != obj.TYPE_NONE && p.From3Type() != obj.TYPE_CONST {
Fatalf("regopt not implemented for from3")
}
@@ -1146,7 +1147,7 @@ func regopt(firstp *obj.Prog) {
}
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(v.etype), v.width, v.node, v.offset)
+ fmt.Printf("bit=%2d addr=%d et=%v w=%-2d s=%v + %d\n", i, v.addr, v.etype, v.width, v.node, v.offset)
}
}
@@ -1357,7 +1358,7 @@ loop2:
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(v.etype), obj.Rconv(int(rgp.regno)), usedreg, vreg)
+ fmt.Printf("registerize %v+%d (bit=%2d et=%v) in %v usedreg=%#x vreg=%#x\n", v.node, v.offset, rgp.varno, v.etype, obj.Rconv(int(rgp.regno)), usedreg, vreg)
}
paint3(rgp.enter, int(rgp.varno), vreg, int(rgp.regno))
diff --git a/src/cmd/compile/internal/gc/select.go b/src/cmd/compile/internal/gc/select.go
index 22c716f9ce..120a9b8cf1 100644
--- a/src/cmd/compile/internal/gc/select.go
+++ b/src/cmd/compile/internal/gc/select.go
@@ -18,7 +18,7 @@ func typecheckselect(sel *Node) {
ncase = n1
setlineno(ncase)
if ncase.Op != OXCASE {
- Fatalf("typecheckselect %v", Oconv(ncase.Op, 0))
+ Fatalf("typecheckselect %v", ncase.Op)
}
if ncase.List.Len() == 0 {
@@ -120,7 +120,7 @@ func walkselect(sel *Node) {
var ch *Node
switch n.Op {
default:
- Fatalf("select %v", Oconv(n.Op, 0))
+ Fatalf("select %v", n.Op)
// ok already
case OSEND:
@@ -218,7 +218,7 @@ func walkselect(sel *Node) {
r.Ninit.Set(cas.Ninit.Slice())
switch n.Op {
default:
- Fatalf("select %v", Oconv(n.Op, 0))
+ Fatalf("select %v", n.Op)
// if selectnbsend(c, v) { body } else { default body }
case OSEND:
@@ -282,7 +282,7 @@ func walkselect(sel *Node) {
} else {
switch n.Op {
default:
- Fatalf("select %v", Oconv(n.Op, 0))
+ Fatalf("select %v", n.Op)
// selectsend(sel *byte, hchan *chan any, elem *any) (selected bool);
case OSEND:
diff --git a/src/cmd/compile/internal/gc/sinit.go b/src/cmd/compile/internal/gc/sinit.go
index 85ef78b973..c6f2acffbf 100644
--- a/src/cmd/compile/internal/gc/sinit.go
+++ b/src/cmd/compile/internal/gc/sinit.go
@@ -342,8 +342,6 @@ func staticcopy(l *Node, r *Node, out *[]*Node) bool {
return true
}
fallthrough
-
- // fall through
case OSTRUCTLIT:
p := initplans[r]
@@ -563,6 +561,35 @@ func getdyn(n *Node, top int) initGenType {
return mode
}
+// isStaticCompositeLiteral reports whether n is a compile-time constant.
+func isStaticCompositeLiteral(n *Node) bool {
+ switch n.Op {
+ case OARRAYLIT:
+ if n.Type.IsSlice() {
+ return false
+ }
+ case OSTRUCTLIT:
+ case OLITERAL:
+ return true
+ default:
+ return false
+ }
+ for _, r := range n.List.Slice() {
+ if r.Op != OKEY {
+ Fatalf("isStaticCompositeLiteral: rhs not OKEY: %v", r)
+ }
+ index := r.Left
+ if n.Op == OARRAYLIT && index.Op != OLITERAL {
+ return false
+ }
+ value := r.Right
+ if !isStaticCompositeLiteral(value) {
+ return false
+ }
+ }
+ return true
+}
+
func structlit(ctxt int, pass int, n *Node, var_ *Node, init *Nodes) {
for _, r := range n.List.Slice() {
if r.Op != OKEY {
@@ -700,7 +727,7 @@ func slicelit(ctxt int, n *Node, var_ *Node, init *Nodes) {
arraylit(ctxt, 2, n, vstat, init)
// copy static to slice
- a := Nod(OSLICE, vstat, Nod(OKEY, nil, nil))
+ a := Nod(OSLICE, vstat, nil)
a = Nod(OAS, var_, a)
a = typecheck(a, Etop)
@@ -718,15 +745,15 @@ func slicelit(ctxt int, n *Node, var_ *Node, init *Nodes) {
// 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
+ // 5. for each dynamic part assign to the array
+ // vauto[i] = dynamic part
+ // 6. assign slice of allocated heap to var
+ // var = vauto[:]
//
// 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
+ // 5. vauto[i] = dynamic part
+ // 6. var = vauto[:]
// if the literal contains constants,
// make static initialized array (1),(2)
@@ -784,21 +811,14 @@ func slicelit(ctxt int, n *Node, var_ *Node, init *Nodes) {
init.Append(a)
}
- // make slice out of heap (5)
- a = Nod(OAS, var_, Nod(OSLICE, vauto, Nod(OKEY, nil, nil)))
-
- a = typecheck(a, Etop)
- a = orderstmtinplace(a)
- a = walkstmt(a)
- init.Append(a)
- // put dynamics into slice (6)
+ // put dynamics into array (5)
for _, r := range n.List.Slice() {
if r.Op != OKEY {
Fatalf("slicelit: rhs not OKEY: %v", r)
}
index := r.Left
value := r.Right
- a := Nod(OINDEX, var_, index)
+ a := Nod(OINDEX, vauto, index)
a.Bounded = true
// TODO need to check bounds?
@@ -820,7 +840,7 @@ func slicelit(ctxt int, n *Node, var_ *Node, init *Nodes) {
continue
}
- // build list of var[c] = expr
+ // build list of vauto[c] = expr
setlineno(value)
a = Nod(OAS, a, value)
@@ -829,6 +849,14 @@ func slicelit(ctxt int, n *Node, var_ *Node, init *Nodes) {
a = walkstmt(a)
init.Append(a)
}
+
+ // make slice out of heap (6)
+ a = Nod(OAS, var_, Nod(OSLICE, vauto, nil))
+
+ a = typecheck(a, Etop)
+ a = orderstmtinplace(a)
+ a = walkstmt(a)
+ init.Append(a)
}
func maplit(ctxt int, n *Node, var_ *Node, init *Nodes) {
@@ -1005,7 +1033,7 @@ func anylit(ctxt int, n *Node, var_ *Node, init *Nodes) {
t := n.Type
switch n.Op {
default:
- Fatalf("anylit: not lit")
+ Fatalf("anylit: not lit, op=%v node=%v", n.Op, n)
case OPTRLIT:
if !t.IsPtr() {
@@ -1074,13 +1102,13 @@ func anylit(ctxt int, n *Node, var_ *Node, init *Nodes) {
structlit(ctxt, 3, n, var_, init)
case OARRAYLIT:
- if t.Etype != TARRAY {
- Fatalf("anylit: not array")
- }
if t.IsSlice() {
slicelit(ctxt, n, var_, init)
break
}
+ if !t.IsArray() {
+ Fatalf("anylit: not array")
+ }
if var_.isSimpleName() && n.List.Len() > 4 {
if ctxt == 0 {
@@ -1280,28 +1308,22 @@ func addvalue(p *InitPlan, xoffset int64, n *Node) {
func iszero(n *Node) bool {
switch n.Op {
case OLITERAL:
- switch n.Val().Ctype() {
+ switch u := n.Val().U.(type) {
default:
Dump("unexpected literal", n)
Fatalf("iszero")
-
- case CTNIL:
+ case *NilVal:
return true
-
- case CTSTR:
- return n.Val().U.(string) == ""
-
- case CTBOOL:
- return !n.Val().U.(bool)
-
- case CTINT, CTRUNE:
- return n.Val().U.(*Mpint).CmpInt64(0) == 0
-
- case CTFLT:
- return n.Val().U.(*Mpflt).CmpFloat64(0) == 0
-
- case CTCPLX:
- return n.Val().U.(*Mpcplx).Real.CmpFloat64(0) == 0 && n.Val().U.(*Mpcplx).Imag.CmpFloat64(0) == 0
+ case string:
+ return u == ""
+ case bool:
+ return !u
+ case *Mpint:
+ return u.CmpInt64(0) == 0
+ case *Mpflt:
+ return u.CmpFloat64(0) == 0
+ case *Mpcplx:
+ return u.Real.CmpFloat64(0) == 0 && u.Imag.CmpFloat64(0) == 0
}
case OARRAYLIT:
@@ -1309,8 +1331,6 @@ func iszero(n *Node) bool {
break
}
fallthrough
-
- // fall through
case OSTRUCTLIT:
for _, n1 := range n.List.Slice() {
if !iszero(n1.Right) {
@@ -1371,7 +1391,8 @@ func genAsInitNoCheck(n *Node, reportOnly bool) bool {
fallthrough
case OSLICEARR:
- if nr.Right.Op != OKEY || nr.Right.Left != nil || nr.Right.Right != nil {
+ low, high, _ := nr.SliceBounds()
+ if low != nil || high != nil {
return false
}
nr = nr.Left
@@ -1385,7 +1406,7 @@ func genAsInitNoCheck(n *Node, reportOnly bool) bool {
}
// nr is the array being converted to a slice
- if nr.Type == nil || nr.Type.Etype != TARRAY || nr.Type.IsSlice() {
+ if nr.Type == nil || !nr.Type.IsArray() {
return false
}
diff --git a/src/cmd/compile/internal/gc/sizeof_test.go b/src/cmd/compile/internal/gc/sizeof_test.go
index 11c0f419da..a01da13883 100644
--- a/src/cmd/compile/internal/gc/sizeof_test.go
+++ b/src/cmd/compile/internal/gc/sizeof_test.go
@@ -27,7 +27,20 @@ func TestSizeof(t *testing.T) {
{Name{}, 52, 80},
{Node{}, 92, 144},
{Sym{}, 60, 112},
- {Type{}, 116, 184},
+ {Type{}, 52, 80},
+ {MapType{}, 20, 40},
+ {ForwardType{}, 16, 32},
+ {FuncType{}, 28, 48},
+ {StructType{}, 12, 24},
+ {InterType{}, 4, 8},
+ {ChanType{}, 8, 16},
+ {ArrayType{}, 16, 24},
+ {InterMethType{}, 4, 8},
+ {DDDFieldType{}, 4, 8},
+ {FuncArgsType{}, 4, 8},
+ {ChanArgsType{}, 4, 8},
+ {PtrType{}, 4, 8},
+ {SliceType{}, 4, 8},
}
for _, tt := range tests {
diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go
index 359f4b22a2..b31cd878cd 100644
--- a/src/cmd/compile/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -13,6 +13,7 @@ import (
"cmd/compile/internal/ssa"
"cmd/internal/obj"
+ "cmd/internal/sys"
)
var ssaEnabled = true
@@ -24,13 +25,13 @@ func initssa() *ssa.Config {
ssaExp.unimplemented = false
ssaExp.mustImplement = true
if ssaConfig == nil {
- ssaConfig = ssa.NewConfig(Thearch.Thestring, &ssaExp, Ctxt, Debug['N'] == 0)
+ ssaConfig = ssa.NewConfig(Thearch.LinkArch.Name, &ssaExp, Ctxt, Debug['N'] == 0)
}
return ssaConfig
}
func shouldssa(fn *Node) bool {
- switch Thearch.Thestring {
+ switch Thearch.LinkArch.Name {
default:
// Only available for testing.
if os.Getenv("SSATEST") == "" {
@@ -336,12 +337,14 @@ var (
memVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "mem"}}
// dummy nodes for temporary variables
- ptrVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "ptr"}}
- capVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "cap"}}
- typVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "typ"}}
- idataVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "idata"}}
- okVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "ok"}}
- deltaVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "delta"}}
+ ptrVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "ptr"}}
+ lenVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "len"}}
+ newlenVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "newlen"}}
+ capVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "cap"}}
+ typVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "typ"}}
+ idataVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "idata"}}
+ okVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "ok"}}
+ deltaVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "delta"}}
)
// startBlock sets the current block we're generating code in to b.
@@ -552,7 +555,7 @@ func (s *state) stmt(n *Node) {
case OCALLFUNC, OCALLMETH, OCALLINTER:
s.call(n, callNormal)
if n.Op == OCALLFUNC && n.Left.Op == ONAME && n.Left.Class == PFUNC &&
- (compiling_runtime != 0 && n.Left.Sym.Name == "throw" ||
+ (compiling_runtime && n.Left.Sym.Name == "throw" ||
n.Left.Sym.Pkg == Runtimepkg && (n.Left.Sym.Name == "gopanic" || n.Left.Sym.Name == "selectgo" || n.Left.Sym.Name == "block")) {
m := s.mem()
b := s.endBlock()
@@ -577,7 +580,7 @@ func (s *state) stmt(n *Node) {
if n.Left.Class&PHEAP == 0 {
return
}
- if compiling_runtime != 0 {
+ if compiling_runtime {
Fatalf("%v escapes to heap, not allowed in runtime.", n)
}
@@ -661,6 +664,17 @@ func (s *state) stmt(n *Node) {
return
}
+ if n.Left == n.Right && n.Left.Op == ONAME {
+ // An x=x assignment. No point in doing anything
+ // here. In addition, skipping this assignment
+ // prevents generating:
+ // VARDEF x
+ // COPY x -> x
+ // which is bad because x is incorrectly considered
+ // dead before the vardef. See issue #14904.
+ return
+ }
+
var t *Type
if n.Right != nil {
t = n.Right.Type
@@ -670,14 +684,27 @@ func (s *state) stmt(n *Node) {
// Evaluate RHS.
rhs := n.Right
- if rhs != nil && (rhs.Op == OSTRUCTLIT || rhs.Op == OARRAYLIT) {
- // All literals with nonzero fields have already been
- // rewritten during walk. Any that remain are just T{}
- // or equivalents. Use the zero value.
- if !iszero(rhs) {
- Fatalf("literal with nonzero value in SSA: %v", rhs)
+ if rhs != nil {
+ switch rhs.Op {
+ case OSTRUCTLIT, OARRAYLIT:
+ // All literals with nonzero fields have already been
+ // rewritten during walk. Any that remain are just T{}
+ // or equivalents. Use the zero value.
+ if !iszero(rhs) {
+ Fatalf("literal with nonzero value in SSA: %v", rhs)
+ }
+ rhs = nil
+ case OAPPEND:
+ // If we're writing the result of an append back to the same slice,
+ // handle it specially to avoid write barriers on the fast (non-growth) path.
+ // If the slice can be SSA'd, it'll be on the stack,
+ // so there will be no write barriers,
+ // so there's no need to attempt to prevent them.
+ if samesafeexpr(n.Left, rhs.List.First()) && !s.canSSA(n.Left) {
+ s.append(rhs, true)
+ return
+ }
}
- rhs = nil
}
var r *ssa.Value
needwb := n.Op == OASWB && rhs != nil
@@ -696,11 +723,11 @@ func (s *state) stmt(n *Node) {
}
}
if rhs != nil && rhs.Op == OAPPEND {
- // Yuck! The frontend gets rid of the write barrier, but we need it!
- // At least, we need it in the case where growslice is called.
- // TODO: Do the write barrier on just the growslice branch.
+ // The frontend gets rid of the write barrier to enable the special OAPPEND
+ // handling above, but since this is not a special case, we need it.
// TODO: just add a ptr graying to the end of growslice?
- // TODO: check whether we need to do this for ODOTTYPE and ORECV also.
+ // TODO: check whether we need to provide special handling and a write barrier
+ // for ODOTTYPE and ORECV also.
// They get similar wb-removal treatment in walk.go:OAS.
needwb = true
}
@@ -709,14 +736,7 @@ func (s *state) stmt(n *Node) {
if rhs != nil && (rhs.Op == OSLICE || rhs.Op == OSLICE3 || rhs.Op == OSLICESTR) && samesafeexpr(rhs.Left, n.Left) {
// We're assigning a slicing operation back to its source.
// Don't write back fields we aren't changing. See issue #14855.
- i := rhs.Right.Left
- var j, k *Node
- if rhs.Op == OSLICE3 {
- j = rhs.Right.Right.Left
- k = rhs.Right.Right.Right
- } else {
- j = rhs.Right.Right
- }
+ i, j, k := rhs.SliceBounds()
if i != nil && (i.Op == OLITERAL && i.Val().Ctype() == CTINT && i.Int64() == 0) {
// [0:...] is the same as [:...]
i = nil
@@ -942,7 +962,7 @@ func (s *state) stmt(n *Node) {
s.nilCheck(p)
default:
- s.Unimplementedf("unhandled stmt %s", opnames[n.Op])
+ s.Unimplementedf("unhandled stmt %s", n.Op)
}
}
@@ -1101,7 +1121,7 @@ var opToSSA = map[opAndType]ssa.Op{
opAndType{OXOR, TINT64}: ssa.OpXor64,
opAndType{OXOR, TUINT64}: ssa.OpXor64,
- opAndType{OEQ, TBOOL}: ssa.OpEq8,
+ opAndType{OEQ, TBOOL}: ssa.OpEqB,
opAndType{OEQ, TINT8}: ssa.OpEq8,
opAndType{OEQ, TUINT8}: ssa.OpEq8,
opAndType{OEQ, TINT16}: ssa.OpEq16,
@@ -1111,7 +1131,7 @@ var opToSSA = map[opAndType]ssa.Op{
opAndType{OEQ, TINT64}: ssa.OpEq64,
opAndType{OEQ, TUINT64}: ssa.OpEq64,
opAndType{OEQ, TINTER}: ssa.OpEqInter,
- opAndType{OEQ, TARRAY}: ssa.OpEqSlice,
+ opAndType{OEQ, TSLICE}: ssa.OpEqSlice,
opAndType{OEQ, TFUNC}: ssa.OpEqPtr,
opAndType{OEQ, TMAP}: ssa.OpEqPtr,
opAndType{OEQ, TCHAN}: ssa.OpEqPtr,
@@ -1121,7 +1141,7 @@ var opToSSA = map[opAndType]ssa.Op{
opAndType{OEQ, TFLOAT64}: ssa.OpEq64F,
opAndType{OEQ, TFLOAT32}: ssa.OpEq32F,
- opAndType{ONE, TBOOL}: ssa.OpNeq8,
+ opAndType{ONE, TBOOL}: ssa.OpNeqB,
opAndType{ONE, TINT8}: ssa.OpNeq8,
opAndType{ONE, TUINT8}: ssa.OpNeq8,
opAndType{ONE, TINT16}: ssa.OpNeq16,
@@ -1131,7 +1151,7 @@ var opToSSA = map[opAndType]ssa.Op{
opAndType{ONE, TINT64}: ssa.OpNeq64,
opAndType{ONE, TUINT64}: ssa.OpNeq64,
opAndType{ONE, TINTER}: ssa.OpNeqInter,
- opAndType{ONE, TARRAY}: ssa.OpNeqSlice,
+ opAndType{ONE, TSLICE}: ssa.OpNeqSlice,
opAndType{ONE, TFUNC}: ssa.OpNeqPtr,
opAndType{ONE, TMAP}: ssa.OpNeqPtr,
opAndType{ONE, TCHAN}: ssa.OpNeqPtr,
@@ -1220,7 +1240,7 @@ func (s *state) ssaOp(op Op, t *Type) ssa.Op {
etype := s.concreteEtype(t)
x, ok := opToSSA[opAndType{op, etype}]
if !ok {
- s.Unimplementedf("unhandled binary op %s %s", opnames[op], Econv(etype))
+ s.Unimplementedf("unhandled binary op %s %s", op, etype)
}
return x
}
@@ -1378,7 +1398,7 @@ func (s *state) ssaShiftOp(op Op, t *Type, u *Type) ssa.Op {
etype2 := s.concreteEtype(u)
x, ok := shiftOpToSSA[opAndTwoTypes{op, etype1, etype2}]
if !ok {
- s.Unimplementedf("unhandled shift op %s etype=%s/%s", opnames[op], Econv(etype1), Econv(etype2))
+ s.Unimplementedf("unhandled shift op %s etype=%s/%s", op, etype1, etype2)
}
return x
}
@@ -1387,15 +1407,19 @@ func (s *state) ssaRotateOp(op Op, t *Type) ssa.Op {
etype1 := s.concreteEtype(t)
x, ok := opToSSA[opAndType{op, etype1}]
if !ok {
- s.Unimplementedf("unhandled rotate op %s etype=%s", opnames[op], Econv(etype1))
+ s.Unimplementedf("unhandled rotate op %s etype=%s", op, etype1)
}
return x
}
// expr converts the expression n to ssa, adds it to s and returns the ssa result.
func (s *state) expr(n *Node) *ssa.Value {
- s.pushLine(n.Lineno)
- defer s.popLine()
+ if !(n.Op == ONAME || n.Op == OLITERAL && n.Sym != nil) {
+ // ONAMEs and named OLITERALs have the line number
+ // of the decl, not the use. See issue 14742.
+ s.pushLine(n.Lineno)
+ defer s.popLine()
+ }
s.stmtList(n.Ninit)
switch n.Op {
@@ -1421,9 +1445,9 @@ func (s *state) expr(n *Node) *ssa.Value {
addr := s.addr(n, false)
return s.newValue2(ssa.OpLoad, n.Type, addr, s.mem())
case OLITERAL:
- switch n.Val().Ctype() {
- case CTINT:
- i := n.Int64()
+ switch u := n.Val().U.(type) {
+ case *Mpint:
+ i := u.Int64()
switch n.Type.Size() {
case 1:
return s.constInt8(n.Type, int8(i))
@@ -1437,21 +1461,14 @@ func (s *state) expr(n *Node) *ssa.Value {
s.Fatalf("bad integer size %d", n.Type.Size())
return nil
}
- case CTSTR:
- if n.Val().U == "" {
+ case string:
+ if u == "" {
return s.constEmptyString(n.Type)
}
- return s.entryNewValue0A(ssa.OpConstString, n.Type, n.Val().U)
- case CTBOOL:
- v := s.constBool(n.Val().U.(bool))
- // For some reason the frontend gets the line numbers of
- // CTBOOL literals totally wrong. Fix it here by grabbing
- // the line number of the enclosing AST node.
- if len(s.line) >= 2 {
- v.Line = s.line[len(s.line)-2]
- }
- return v
- case CTNIL:
+ return s.entryNewValue0A(ssa.OpConstString, n.Type, u)
+ case bool:
+ return s.constBool(u)
+ case *NilVal:
t := n.Type
switch {
case t.IsSlice():
@@ -1461,36 +1478,30 @@ func (s *state) expr(n *Node) *ssa.Value {
default:
return s.constNil(t)
}
- case CTFLT:
- f := n.Val().U.(*Mpflt)
+ case *Mpflt:
switch n.Type.Size() {
case 4:
- return s.constFloat32(n.Type, f.Float32())
+ return s.constFloat32(n.Type, u.Float32())
case 8:
- return s.constFloat64(n.Type, f.Float64())
+ return s.constFloat64(n.Type, u.Float64())
default:
s.Fatalf("bad float size %d", n.Type.Size())
return nil
}
- case CTCPLX:
- c := n.Val().U.(*Mpcplx)
- r := &c.Real
- i := &c.Imag
+ case *Mpcplx:
+ r := &u.Real
+ i := &u.Imag
switch n.Type.Size() {
case 8:
- {
- pt := Types[TFLOAT32]
- return s.newValue2(ssa.OpComplexMake, n.Type,
- s.constFloat32(pt, r.Float32()),
- s.constFloat32(pt, i.Float32()))
- }
+ pt := Types[TFLOAT32]
+ return s.newValue2(ssa.OpComplexMake, n.Type,
+ s.constFloat32(pt, r.Float32()),
+ s.constFloat32(pt, i.Float32()))
case 16:
- {
- pt := Types[TFLOAT64]
- return s.newValue2(ssa.OpComplexMake, n.Type,
- s.constFloat64(pt, r.Float64()),
- s.constFloat64(pt, i.Float64()))
- }
+ pt := Types[TFLOAT64]
+ return s.newValue2(ssa.OpComplexMake, n.Type,
+ s.constFloat64(pt, r.Float64()),
+ s.constFloat64(pt, i.Float64()))
default:
s.Fatalf("bad float size %d", n.Type.Size())
return nil
@@ -1540,7 +1551,7 @@ func (s *state) expr(n *Node) *ssa.Value {
return nil
}
if etypesign(from.Etype) != etypesign(to.Etype) {
- s.Fatalf("CONVNOP sign mismatch %v (%s) -> %v (%s)\n", from, Econv(from.Etype), to, Econv(to.Etype))
+ s.Fatalf("CONVNOP sign mismatch %v (%s) -> %v (%s)\n", from, from.Etype, to, to.Etype)
return nil
}
@@ -1685,7 +1696,7 @@ func (s *state) expr(n *Node) *ssa.Value {
s.newValue1(op, ttp, s.newValue1(ssa.OpComplexImag, ftp, x)))
}
- s.Unimplementedf("unhandled OCONV %s -> %s", Econv(n.Left.Type.Etype), Econv(n.Type.Etype))
+ s.Unimplementedf("unhandled OCONV %s -> %s", n.Left.Type.Etype, n.Type.Etype)
return nil
case ODOTTYPE:
@@ -1708,7 +1719,7 @@ func (s *state) expr(n *Node) *ssa.Value {
case ONE:
return s.newValue1(ssa.OpNot, Types[TBOOL], c)
default:
- s.Fatalf("ordered complex compare %s", opnames[n.Op])
+ s.Fatalf("ordered complex compare %s", n.Op)
}
}
return s.newValue2(s.ssaOp(n.Op, n.Left.Type), Types[TBOOL], a, b)
@@ -1911,8 +1922,7 @@ func (s *state) expr(n *Node) *ssa.Value {
return s.newValue2(ssa.OpLoad, n.Type, addr, s.mem())
case OIND:
- p := s.expr(n.Left)
- s.nilCheck(p)
+ p := s.exprPtr(n.Left, false, n.Lineno)
return s.newValue2(ssa.OpLoad, n.Type, p, s.mem())
case ODOT:
@@ -1925,8 +1935,7 @@ func (s *state) expr(n *Node) *ssa.Value {
return s.newValue2(ssa.OpLoad, n.Type, p, s.mem())
case ODOTPTR:
- p := s.expr(n.Left)
- s.nilCheck(p)
+ p := s.exprPtr(n.Left, false, n.Lineno)
p = s.newValue1I(ssa.OpOffPtr, p.Type, n.Xoffset, p)
return s.newValue2(ssa.OpLoad, n.Type, p, s.mem())
@@ -2019,38 +2028,34 @@ func (s *state) expr(n *Node) *ssa.Value {
}
return s.newValue2(ssa.OpIMake, n.Type, tab, data)
- case OSLICE, OSLICEARR:
+ case OSLICE, OSLICEARR, OSLICE3, OSLICE3ARR:
v := s.expr(n.Left)
- var i, j *ssa.Value
- if n.Right.Left != nil {
- i = s.extendIndex(s.expr(n.Right.Left))
+ var i, j, k *ssa.Value
+ low, high, max := n.SliceBounds()
+ if low != nil {
+ i = s.extendIndex(s.expr(low))
+ }
+ if high != nil {
+ j = s.extendIndex(s.expr(high))
}
- if n.Right.Right != nil {
- j = s.extendIndex(s.expr(n.Right.Right))
+ if max != nil {
+ k = s.extendIndex(s.expr(max))
}
- p, l, c := s.slice(n.Left.Type, v, i, j, nil)
+ p, l, c := s.slice(n.Left.Type, v, i, j, k)
return s.newValue3(ssa.OpSliceMake, n.Type, p, l, c)
+
case OSLICESTR:
v := s.expr(n.Left)
var i, j *ssa.Value
- if n.Right.Left != nil {
- i = s.extendIndex(s.expr(n.Right.Left))
+ low, high, _ := n.SliceBounds()
+ if low != nil {
+ i = s.extendIndex(s.expr(low))
}
- if n.Right.Right != nil {
- j = s.extendIndex(s.expr(n.Right.Right))
+ if high != nil {
+ j = s.extendIndex(s.expr(high))
}
p, l, _ := s.slice(n.Left.Type, v, i, j, nil)
return s.newValue2(ssa.OpStringMake, n.Type, p, l)
- case OSLICE3, OSLICE3ARR:
- v := s.expr(n.Left)
- var i *ssa.Value
- if n.Right.Left != nil {
- i = s.extendIndex(s.expr(n.Right.Left))
- }
- j := s.extendIndex(s.expr(n.Right.Right.Left))
- k := s.extendIndex(s.expr(n.Right.Right.Right))
- p, l, c := s.slice(n.Left.Type, v, i, j, k)
- return s.newValue3(ssa.OpSliceMake, n.Type, p, l, c)
case OCALLFUNC:
if isIntrinsicCall1(n) {
@@ -2066,32 +2071,67 @@ func (s *state) expr(n *Node) *ssa.Value {
return s.newValue1(ssa.OpGetG, n.Type, s.mem())
case OAPPEND:
- return s.exprAppend(n)
+ return s.append(n, false)
default:
- s.Unimplementedf("unhandled expr %s", opnames[n.Op])
+ s.Unimplementedf("unhandled expr %s", n.Op)
return nil
}
}
-// exprAppend converts an OAPPEND node n to an ssa.Value, adds it to s, and returns the Value.
-func (s *state) exprAppend(n *Node) *ssa.Value {
- // append(s, e1, e2, e3). Compile like:
- // ptr,len,cap := s
+// append converts an OAPPEND node to SSA.
+// If inplace is false, it converts the OAPPEND expression n to an ssa.Value,
+// adds it to s, and returns the Value.
+// If inplace is true, it writes the result of the OAPPEND expression n
+// back to the slice being appended to, and returns nil.
+// inplace MUST be set to false if the slice can be SSA'd.
+func (s *state) append(n *Node, inplace bool) *ssa.Value {
+ // If inplace is false, process as expression "append(s, e1, e2, e3)":
+ //
+ // ptr, len, cap := s
// newlen := len + 3
- // if newlen > s.cap {
- // ptr,_,cap = growslice(s, newlen)
+ // if newlen > cap {
+ // ptr, len, cap = growslice(s, newlen)
+ // newlen = len + 3 // recalculate to avoid a spill
// }
+ // // with write barriers, if needed:
+ // *(ptr+len) = e1
+ // *(ptr+len+1) = e2
+ // *(ptr+len+2) = e3
+ // return makeslice(ptr, newlen, cap)
+ //
+ //
+ // If inplace is true, process as statement "s = append(s, e1, e2, e3)":
+ //
+ // a := &s
+ // ptr, len, cap := s
+ // newlen := len + 3
+ // if newlen > cap {
+ // newptr, len, newcap = growslice(ptr, len, cap, newlen)
+ // vardef(a) // if necessary, advise liveness we are writing a new a
+ // *a.cap = newcap // write before ptr to avoid a spill
+ // *a.ptr = newptr // with write barrier
+ // }
+ // newlen = len + 3 // recalculate to avoid a spill
+ // *a.len = newlen
+ // // with write barriers, if needed:
// *(ptr+len) = e1
// *(ptr+len+1) = e2
// *(ptr+len+2) = e3
- // makeslice(ptr,newlen,cap)
et := n.Type.Elem()
pt := Ptrto(et)
// Evaluate slice
- slice := s.expr(n.List.First())
+ sn := n.List.First() // the slice node is the first in the list
+
+ var slice, addr *ssa.Value
+ if inplace {
+ addr = s.addr(sn, false)
+ slice = s.newValue2(ssa.OpLoad, n.Type, addr, s.mem())
+ } else {
+ slice = s.expr(sn)
+ }
// Allocate new blocks
grow := s.f.NewBlock(ssa.BlockPlain)
@@ -2103,9 +2143,17 @@ func (s *state) exprAppend(n *Node) *ssa.Value {
l := s.newValue1(ssa.OpSliceLen, Types[TINT], slice)
c := s.newValue1(ssa.OpSliceCap, Types[TINT], slice)
nl := s.newValue2(s.ssaOp(OADD, Types[TINT]), Types[TINT], l, s.constInt(Types[TINT], nargs))
+
cmp := s.newValue2(s.ssaOp(OGT, Types[TINT]), Types[TBOOL], nl, c)
s.vars[&ptrVar] = p
- s.vars[&capVar] = c
+
+ if !inplace {
+ s.vars[&newlenVar] = nl
+ s.vars[&capVar] = c
+ } else {
+ s.vars[&lenVar] = l
+ }
+
b := s.endBlock()
b.Kind = ssa.BlockIf
b.Likely = ssa.BranchUnlikely
@@ -2115,20 +2163,40 @@ func (s *state) exprAppend(n *Node) *ssa.Value {
// Call growslice
s.startBlock(grow)
- taddr := s.newValue1A(ssa.OpAddr, Types[TUINTPTR], &ssa.ExternSymbol{Types[TUINTPTR], typenamesym(n.Type)}, s.sb)
+ taddr := s.newValue1A(ssa.OpAddr, Types[TUINTPTR], &ssa.ExternSymbol{Types[TUINTPTR], typenamesym(n.Type.Elem())}, s.sb)
r := s.rtcall(growslice, true, []*Type{pt, Types[TINT], Types[TINT]}, taddr, p, l, c, nl)
- s.vars[&ptrVar] = r[0]
- // Note: we don't need to read r[1], the result's length. It will be nl.
- // (or maybe we should, we just have to spill/restore nl otherwise?)
- s.vars[&capVar] = r[2]
+ if inplace {
+ if sn.Op == ONAME {
+ // Tell liveness we're about to build a new slice
+ s.vars[&memVar] = s.newValue1A(ssa.OpVarDef, ssa.TypeMem, sn, s.mem())
+ }
+ capaddr := s.newValue1I(ssa.OpOffPtr, pt, int64(Array_cap), addr)
+ s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, s.config.IntSize, capaddr, r[2], s.mem())
+ s.insertWBstore(pt, addr, r[0], n.Lineno, 0)
+ // load the value we just stored to avoid having to spill it
+ s.vars[&ptrVar] = s.newValue2(ssa.OpLoad, pt, addr, s.mem())
+ s.vars[&lenVar] = r[1] // avoid a spill in the fast path
+ } else {
+ s.vars[&ptrVar] = r[0]
+ s.vars[&newlenVar] = s.newValue2(s.ssaOp(OADD, Types[TINT]), Types[TINT], r[1], s.constInt(Types[TINT], nargs))
+ s.vars[&capVar] = r[2]
+ }
+
b = s.endBlock()
b.AddEdgeTo(assign)
// assign new elements to slots
s.startBlock(assign)
+ if inplace {
+ l = s.variable(&lenVar, Types[TINT]) // generates phi for len
+ nl = s.newValue2(s.ssaOp(OADD, Types[TINT]), Types[TINT], l, s.constInt(Types[TINT], nargs))
+ lenaddr := s.newValue1I(ssa.OpOffPtr, pt, int64(Array_nel), addr)
+ s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, s.config.IntSize, lenaddr, nl, s.mem())
+ }
+
// Evaluate args
args := make([]*ssa.Value, 0, nargs)
store := make([]bool, 0, nargs)
@@ -2142,8 +2210,11 @@ func (s *state) exprAppend(n *Node) *ssa.Value {
}
}
- p = s.variable(&ptrVar, pt) // generates phi for ptr
- c = s.variable(&capVar, Types[TINT]) // generates phi for cap
+ p = s.variable(&ptrVar, pt) // generates phi for ptr
+ if !inplace {
+ nl = s.variable(&newlenVar, Types[TINT]) // generates phi for nl
+ c = s.variable(&capVar, Types[TINT]) // generates phi for cap
+ }
p2 := s.newValue2(ssa.OpPtrIndex, pt, p, l)
// TODO: just one write barrier call for all of these writes?
// TODO: maybe just one writeBarrier.enabled check?
@@ -2164,9 +2235,14 @@ func (s *state) exprAppend(n *Node) *ssa.Value {
}
}
- // make result
delete(s.vars, &ptrVar)
+ if inplace {
+ delete(s.vars, &lenVar)
+ return nil
+ }
+ delete(s.vars, &newlenVar)
delete(s.vars, &capVar)
+ // make result
return s.newValue3(ssa.OpSliceMake, n.Type, p, nl, c)
}
@@ -2398,7 +2474,7 @@ func isSSAIntrinsic1(s *Sym) bool {
// so far has only been noticed for Bswap32 and the 16-bit count
// leading/trailing instructions, but heuristics might change
// in the future or on different architectures).
- if !ssaEnabled || ssa.IntrinsicsDisable || Thearch.Thechar != '6' {
+ if !ssaEnabled || ssa.IntrinsicsDisable || Thearch.LinkArch.Family != sys.AMD64 {
return false
}
if s != nil && s.Pkg != nil && s.Pkg.Path == "runtime/internal/sys" {
@@ -2484,7 +2560,7 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
// want to set it here.
case OCALLINTER:
if fn.Op != ODOTINTER {
- Fatalf("OCALLINTER: n.Left not an ODOTINTER: %v", Oconv(fn.Op, 0))
+ Fatalf("OCALLINTER: n.Left not an ODOTINTER: %v", fn.Op)
}
i := s.expr(fn.Left)
itab := s.newValue1(ssa.OpITab, Types[TUINTPTR], i)
@@ -2542,7 +2618,7 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
case sym != nil:
call = s.newValue1A(ssa.OpStaticCall, ssa.TypeMem, sym, s.mem())
default:
- Fatalf("bad call type %s %v", opnames[n.Op], n)
+ Fatalf("bad call type %s %v", n.Op, n)
}
call.AuxInt = stksize // Call operations carry the argsize of the callee along with them
@@ -2570,7 +2646,7 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
return nil
}
fp := res.Field(0)
- return s.entryNewValue1I(ssa.OpOffPtr, Ptrto(fp.Type), fp.Offset, s.sp)
+ return s.entryNewValue1I(ssa.OpOffPtr, Ptrto(fp.Type), fp.Offset+Ctxt.FixedFrameSize(), s.sp)
}
// etypesign returns the signed-ness of e, for integer/pointer etypes.
@@ -2680,19 +2756,12 @@ func (s *state) addr(n *Node, bounded bool) *ssa.Value {
return s.newValue2(ssa.OpPtrIndex, Ptrto(n.Left.Type.Elem()), a, i)
}
case OIND:
- p := s.expr(n.Left)
- if !bounded {
- s.nilCheck(p)
- }
- return p
+ return s.exprPtr(n.Left, bounded, n.Lineno)
case ODOT:
p := s.addr(n.Left, bounded)
return s.newValue1I(ssa.OpOffPtr, t, n.Xoffset, p)
case ODOTPTR:
- p := s.expr(n.Left)
- if !bounded {
- s.nilCheck(p)
- }
+ p := s.exprPtr(n.Left, bounded, n.Lineno)
return s.newValue1I(ssa.OpOffPtr, t, n.Xoffset, p)
case OCLOSUREVAR:
return s.newValue1I(ssa.OpOffPtr, t, n.Xoffset,
@@ -2715,7 +2784,7 @@ func (s *state) addr(n *Node, bounded bool) *ssa.Value {
return s.call(n, callNormal)
default:
- s.Unimplementedf("unhandled addr %v", Oconv(n.Op, 0))
+ s.Unimplementedf("unhandled addr %v", n.Op)
return nil
}
}
@@ -2773,9 +2842,6 @@ func canSSAType(t *Type) bool {
}
switch t.Etype {
case TARRAY:
- if t.IsSlice() {
- return true
- }
// We can't do arrays because dynamic indexing is
// not supported on SSA variables.
// TODO: maybe allow if length is <=1? All indexes
@@ -2797,6 +2863,19 @@ func canSSAType(t *Type) bool {
}
}
+// exprPtr evaluates n to a pointer and nil-checks it.
+func (s *state) exprPtr(n *Node, bounded bool, lineno int32) *ssa.Value {
+ p := s.expr(n)
+ if bounded || n.NonNil {
+ if s.f.Config.Debug_checknil() && lineno > 1 {
+ s.f.Config.Warnl(lineno, "removed nil check")
+ }
+ return p
+ }
+ s.nilCheck(p)
+ return p
+}
+
// nilCheck generates nil pointer checking code.
// Starts a new block on return, unless nil checks are disabled.
// Used only for automatically inserted nil checks,
@@ -3687,6 +3766,10 @@ func (s *state) resolveFwdRef(v *ssa.Value) {
if b == s.f.Entry {
// Live variable at start of function.
if s.canSSA(name) {
+ if strings.HasPrefix(name.Sym.Name, "autotmp_") {
+ // It's likely that this is an uninitialized variable in the entry block.
+ s.Fatalf("Treating auto as if it were arg, func %s, node %v, value %v", b.Func.Name, name, v)
+ }
v.Op = ssa.OpArg
v.Aux = name
return
@@ -4207,7 +4290,7 @@ func (e *ssaExport) SplitInterface(name ssa.LocalSlot) (ssa.LocalSlot, ssa.Local
func (e *ssaExport) SplitSlice(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot, ssa.LocalSlot) {
n := name.N.(*Node)
- ptrType := Ptrto(n.Type.Type)
+ ptrType := Ptrto(name.Type.ElemType().(*Type))
lenType := Types[TINT]
if n.Class == PAUTO && !n.Addrtaken {
// Split this slice up into three separate variables.
@@ -4241,6 +4324,20 @@ func (e *ssaExport) SplitComplex(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSl
return ssa.LocalSlot{n, t, name.Off}, ssa.LocalSlot{n, t, name.Off + s}
}
+func (e *ssaExport) SplitStruct(name ssa.LocalSlot, i int) ssa.LocalSlot {
+ n := name.N.(*Node)
+ st := name.Type
+ ft := st.FieldType(i)
+ if n.Class == PAUTO && !n.Addrtaken {
+ // Note: the _ field may appear several times. But
+ // have no fear, identically-named but distinct Autos are
+ // ok, albeit maybe confusing for a debugger.
+ x := e.namedAuto(n.Sym.Name+"."+st.FieldName(i), ft)
+ return ssa.LocalSlot{x, ft, 0}
+ }
+ return ssa.LocalSlot{n, ft, name.Off + st.FieldOff(i)}
+}
+
// namedAuto returns a new AUTO variable with the given name and type.
func (e *ssaExport) namedAuto(name string, typ ssa.Type) ssa.GCNode {
t := typ.(*Type)
diff --git a/src/cmd/compile/internal/gc/ssa_test.go b/src/cmd/compile/internal/gc/ssa_test.go
index 59a240237b..46e1b0a7d3 100644
--- a/src/cmd/compile/internal/gc/ssa_test.go
+++ b/src/cmd/compile/internal/gc/ssa_test.go
@@ -99,3 +99,7 @@ func TestUnsafe(t *testing.T) { runTest(t, "unsafe_ssa.go") }
func TestPhi(t *testing.T) { runTest(t, "phi_ssa.go") }
func TestSlice(t *testing.T) { runTest(t, "slice.go") }
+
+func TestNamedReturn(t *testing.T) { runTest(t, "namedReturn.go") }
+
+func TestDuplicateLoad(t *testing.T) { runTest(t, "dupLoad.go") }
diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go
index a61b8bcd27..6f2ed6a839 100644
--- a/src/cmd/compile/internal/gc/subr.go
+++ b/src/cmd/compile/internal/gc/subr.go
@@ -372,30 +372,6 @@ func saveorignode(n *Node) {
n.Orig = norig
}
-// checkMapKeyType checks that Type key is valid for use as a map key.
-func checkMapKeyType(key *Type) {
- alg, bad := algtype1(key)
- if alg != ANOEQ {
- return
- }
- switch bad.Etype {
- default:
- Yyerror("invalid map key type %v", key)
- case TANY:
- // Will be resolved later.
- case TFORW:
- // 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.
- if key.Maplineno == 0 {
- key.Maplineno = lineno
- }
- }
-}
-
// methcmp sorts by symbol, then by package path for unexported symbols.
type methcmp []*Field
@@ -540,8 +516,15 @@ func treecopy(n *Node, lineno int32) *Node {
}
return n
+ case OPACK:
+ // OPACK nodes are never valid in const value declarations,
+ // but allow them like any other declared symbol to avoid
+ // crashing (golang.org/issue/11361).
+ fallthrough
+
case ONAME, OLITERAL, OTYPE:
return n
+
}
}
@@ -611,6 +594,7 @@ func methtype(t *Type, mustname int) *Type {
case TSTRUCT,
TARRAY,
+ TSLICE,
TMAP,
TCHAN,
TSTRING,
@@ -631,14 +615,10 @@ func cplxsubtype(et EType) EType {
return TFLOAT64
}
- Fatalf("cplxsubtype: %v\n", Econv(et))
+ Fatalf("cplxsubtype: %v\n", et)
return 0
}
-func eqnote(a, b *string) bool {
- return a == b || a != nil && b != nil && *a == *b
-}
-
// Eqtype reports whether t1 and t2 are identical, following the spec rules.
//
// Any cyclic type must go through a named type, and if one is
@@ -658,7 +638,7 @@ func eqtype1(t1, t2 *Type, assumedEqual map[typePair]struct{}) bool {
if t1 == t2 {
return true
}
- if t1 == nil || t2 == nil || t1.Etype != t2.Etype {
+ if t1 == nil || t2 == nil || t1.Etype != t2.Etype || t1.Broke || t2.Broke {
return false
}
if t1.Sym != nil || t2.Sym != nil {
@@ -686,7 +666,7 @@ func eqtype1(t1, t2 *Type, assumedEqual map[typePair]struct{}) bool {
t1, i1 := IterFields(t1)
t2, i2 := IterFields(t2)
for ; t1 != nil && t2 != nil; t1, t2 = i1.Next(), i2.Next() {
- if t1.Sym != t2.Sym || t1.Embedded != t2.Embedded || !eqtype1(t1.Type, t2.Type, assumedEqual) || !eqnote(t1.Note, t2.Note) {
+ if t1.Sym != t2.Sym || t1.Embedded != t2.Embedded || !eqtype1(t1.Type, t2.Type, assumedEqual) || t1.Note != t2.Note {
return false
}
}
@@ -767,7 +747,7 @@ func assignop(src *Type, dst *Type, why *string) Op {
// 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 {
+ if safemode && importpkg == nil && src != nil && src.Etype == TUNSAFEPTR {
Yyerror("cannot use unsafe.Pointer")
errorexit()
}
@@ -853,18 +833,13 @@ func assignop(src *Type, dst *Type, why *string) Op {
// 5. src is the predeclared identifier nil and dst is a nillable type.
if src.Etype == TNIL {
switch dst.Etype {
- case TARRAY:
- if !dst.IsSlice() {
- break
- }
- fallthrough
-
case TPTR32,
TPTR64,
TFUNC,
TMAP,
TCHAN,
- TINTER:
+ TINTER,
+ TSLICE:
return OCONVNOP
}
}
@@ -1045,6 +1020,68 @@ func Is64(t *Type) bool {
return false
}
+// SliceBounds returns n's slice bounds: low, high, and max in expr[low:high:max].
+// n must be a slice expression. max is nil if n is a simple slice expression.
+func (n *Node) SliceBounds() (low, high, max *Node) {
+ switch n.Op {
+ case OSLICE, OSLICEARR, OSLICESTR:
+ if n.Right == nil {
+ return nil, nil, nil
+ }
+ if n.Right.Op != OKEY {
+ Fatalf("SliceBounds right %s", opnames[n.Right.Op])
+ }
+ return n.Right.Left, n.Right.Right, nil
+ case OSLICE3, OSLICE3ARR:
+ if n.Right.Op != OKEY || n.Right.Right.Op != OKEY {
+ Fatalf("SliceBounds right %s %s", opnames[n.Right.Op], opnames[n.Right.Right.Op])
+ }
+ return n.Right.Left, n.Right.Right.Left, n.Right.Right.Right
+ }
+ Fatalf("SliceBounds op %s: %v", n.Op, n)
+ return nil, nil, nil
+}
+
+// SetSliceBounds sets n's slice bounds, where n is a slice expression.
+// n must be a slice expression. If max is non-nil, n must be a full slice expression.
+func (n *Node) SetSliceBounds(low, high, max *Node) {
+ switch n.Op {
+ case OSLICE, OSLICEARR, OSLICESTR:
+ if max != nil {
+ Fatalf("SetSliceBounds %s given three bounds", n.Op)
+ }
+ if n.Right == nil {
+ n.Right = Nod(OKEY, low, high)
+ return
+ }
+ n.Right.Left = low
+ n.Right.Right = high
+ return
+ case OSLICE3, OSLICE3ARR:
+ if n.Right == nil {
+ n.Right = Nod(OKEY, low, Nod(OKEY, high, max))
+ }
+ n.Right.Left = low
+ n.Right.Right.Left = high
+ n.Right.Right.Right = max
+ return
+ }
+ Fatalf("SetSliceBounds op %s: %v", n.Op, n)
+}
+
+// IsSlice3 reports whether o is a slice3 op (OSLICE3, OSLICE3ARR).
+// o must be a slicing op.
+func (o Op) IsSlice3() bool {
+ switch o {
+ case OSLICE, OSLICEARR, OSLICESTR:
+ return false
+ case OSLICE3, OSLICE3ARR:
+ return true
+ }
+ Fatalf("IsSlice3 op %v", o)
+ return false
+}
+
// Is a conversion between t1 and t2 a no-op?
func Noconv(t1 *Type, t2 *Type) bool {
e1 := Simtype[t1.Etype]
@@ -1081,6 +1118,10 @@ func syslook(name string) *Node {
return s.Def
}
+func (s *Sym) IsRuntimeCall(name string) bool {
+ return s.Pkg == Runtimepkg && s.Name == name
+}
+
// typehash computes a hash value for type t to use in type switch
// statements.
func typehash(t *Type) uint32 {
@@ -1162,9 +1203,9 @@ func printframenode(n *Node) {
}
switch n.Op {
case ONAME:
- fmt.Printf("%v %v G%d %v width=%d\n", Oconv(n.Op, 0), n.Sym, n.Name.Vargen, n.Type, w)
+ fmt.Printf("%v %v G%d %v width=%d\n", n.Op, n.Sym, n.Name.Vargen, n.Type, w)
case OTYPE:
- fmt.Printf("%v %v width=%d\n", Oconv(n.Op, 0), n.Type, w)
+ fmt.Printf("%v %v width=%d\n", n.Op, n.Type, w)
}
}
@@ -1245,7 +1286,7 @@ func badtype(op Op, tl *Type, tr *Type) {
}
s := fmt_
- Yyerror("illegal types for operand: %v%s", Oconv(op, 0), s)
+ Yyerror("illegal types for operand: %v%s", op, s)
}
// Brcom returns !(op).
@@ -1265,7 +1306,7 @@ func Brcom(op Op) Op {
case OGE:
return OLT
}
- Fatalf("brcom: no com for %v\n", Oconv(op, 0))
+ Fatalf("brcom: no com for %v\n", op)
return op
}
@@ -1286,7 +1327,7 @@ func Brrev(op Op) Op {
case OGE:
return OLE
}
- Fatalf("brrev: no rev for %v\n", Oconv(op, 0))
+ Fatalf("brrev: no rev for %v\n", op)
return op
}
@@ -1341,6 +1382,11 @@ func safeexpr(n *Node, init *Nodes) *Node {
a.Right = r
a = walkexpr(a, init)
return a
+
+ case OSTRUCTLIT, OARRAYLIT:
+ if isStaticCompositeLiteral(n) {
+ return n
+ }
}
// make a copy; must not be used as an lvalue
diff --git a/src/cmd/compile/internal/gc/swt.go b/src/cmd/compile/internal/gc/swt.go
index 3b08b13508..aac92fd311 100644
--- a/src/cmd/compile/internal/gc/swt.go
+++ b/src/cmd/compile/internal/gc/swt.go
@@ -350,7 +350,7 @@ func casebody(sw *Node, typeswvar *Node) {
for i, n := range sw.List.Slice() {
setlineno(n)
if n.Op != OXCASE {
- Fatalf("casebody %v", Oconv(n.Op, 0))
+ Fatalf("casebody %v", n.Op)
}
n.Op = OCASE
needvar := n.List.Len() != 1 || n.List.First().Op == OLITERAL
diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go
index 2f3b98a8ef..8a675ac157 100644
--- a/src/cmd/compile/internal/gc/syntax.go
+++ b/src/cmd/compile/internal/gc/syntax.go
@@ -54,6 +54,7 @@ type Node struct {
Addable bool // addressable
Etype EType // op for OASOP, etype for OTYPE, exclam for export, 6g saved reg, ChanDir for OTCHAN
Bounded bool // bounds check unnecessary
+ NonNil bool // guaranteed to be non-nil
Class Class // PPARAM, PAUTO, PEXTERN, etc
Embedded uint8 // ODCLFIELD embedded type
Colas bool // OAS resulting from :=
diff --git a/src/cmd/compile/internal/gc/testdata/dupLoad.go b/src/cmd/compile/internal/gc/testdata/dupLoad.go
new file mode 100644
index 0000000000..d18dc733e1
--- /dev/null
+++ b/src/cmd/compile/internal/gc/testdata/dupLoad.go
@@ -0,0 +1,83 @@
+// run
+
+// Copyright 2016 The Go 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 test makes sure that we don't split a single
+// load up into two separate loads.
+
+package main
+
+import "fmt"
+
+//go:noinline
+func read1(b []byte) (uint16, uint16) {
+ // There is only a single read of b[0]. The two
+ // returned values must have the same low byte.
+ v := b[0]
+ return uint16(v), uint16(v) | uint16(b[1])<<8
+}
+
+const N = 100000
+
+func main1() {
+ done := make(chan struct{})
+ b := make([]byte, 2)
+ go func() {
+ for i := 0; i < N; i++ {
+ b[0] = byte(i)
+ b[1] = byte(i)
+ }
+ done <- struct{}{}
+ }()
+ go func() {
+ for i := 0; i < N; i++ {
+ x, y := read1(b)
+ if byte(x) != byte(y) {
+ fmt.Printf("x=%x y=%x\n", x, y)
+ panic("bad")
+ }
+ }
+ done <- struct{}{}
+ }()
+ <-done
+ <-done
+}
+
+//go:noinline
+func read2(b []byte) (uint16, uint16) {
+ // There is only a single read of b[1]. The two
+ // returned values must have the same high byte.
+ v := uint16(b[1]) << 8
+ return v, uint16(b[0]) | v
+}
+
+func main2() {
+ done := make(chan struct{})
+ b := make([]byte, 2)
+ go func() {
+ for i := 0; i < N; i++ {
+ b[0] = byte(i)
+ b[1] = byte(i)
+ }
+ done <- struct{}{}
+ }()
+ go func() {
+ for i := 0; i < N; i++ {
+ x, y := read2(b)
+ if x&0xff00 != y&0xff00 {
+ fmt.Printf("x=%x y=%x\n", x, y)
+ panic("bad")
+ }
+ }
+ done <- struct{}{}
+ }()
+ <-done
+ <-done
+}
+
+func main() {
+ main1()
+ main2()
+}
diff --git a/src/cmd/compile/internal/gc/testdata/namedReturn.go b/src/cmd/compile/internal/gc/testdata/namedReturn.go
new file mode 100644
index 0000000000..19ef8a7e43
--- /dev/null
+++ b/src/cmd/compile/internal/gc/testdata/namedReturn.go
@@ -0,0 +1,105 @@
+// run
+
+// Copyright 2016 The Go 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 test makes sure that naming named
+// return variables in a return statement works.
+// See issue #14904.
+
+package main
+
+import (
+ "fmt"
+ "runtime"
+)
+
+// Our heap-allocated object that will be GC'd incorrectly.
+// Note that we always check the second word because that's
+// where 0xdeaddeaddeaddead is written.
+type B [4]int
+
+// small (SSAable) array
+type T1 [3]*B
+
+//go:noinline
+func f1() (t T1) {
+ t[0] = &B{91, 92, 93, 94}
+ runtime.GC()
+ return t
+}
+
+// large (non-SSAable) array
+type T2 [8]*B
+
+//go:noinline
+func f2() (t T2) {
+ t[0] = &B{91, 92, 93, 94}
+ runtime.GC()
+ return t
+}
+
+// small (SSAable) struct
+type T3 struct {
+ a, b, c *B
+}
+
+//go:noinline
+func f3() (t T3) {
+ t.a = &B{91, 92, 93, 94}
+ runtime.GC()
+ return t
+}
+
+// large (non-SSAable) struct
+type T4 struct {
+ a, b, c, d, e, f *B
+}
+
+//go:noinline
+func f4() (t T4) {
+ t.a = &B{91, 92, 93, 94}
+ runtime.GC()
+ return t
+}
+
+var sink *B
+
+func f5() int {
+ b := &B{91, 92, 93, 94}
+ t := T4{b, nil, nil, nil, nil, nil}
+ sink = b // make sure b is heap allocated ...
+ sink = nil // ... but not live
+ runtime.GC()
+ t = t
+ return t.a[1]
+}
+
+func main() {
+ failed := false
+
+ if v := f1()[0][1]; v != 92 {
+ fmt.Printf("f1()[0][1]=%d, want 92\n", v)
+ failed = true
+ }
+ if v := f2()[0][1]; v != 92 {
+ fmt.Printf("f2()[0][1]=%d, want 92\n", v)
+ failed = true
+ }
+ if v := f3().a[1]; v != 92 {
+ fmt.Printf("f3().a[1]=%d, want 92\n", v)
+ failed = true
+ }
+ if v := f4().a[1]; v != 92 {
+ fmt.Printf("f4().a[1]=%d, want 92\n", v)
+ failed = true
+ }
+ if v := f5(); v != 92 {
+ fmt.Printf("f5()=%d, want 92\n", v)
+ failed = true
+ }
+ if failed {
+ panic("bad")
+ }
+}
diff --git a/src/cmd/compile/internal/gc/type.go b/src/cmd/compile/internal/gc/type.go
index b89c5dbf22..9f049babc2 100644
--- a/src/cmd/compile/internal/gc/type.go
+++ b/src/cmd/compile/internal/gc/type.go
@@ -44,6 +44,7 @@ const (
TPTR64
TFUNC
+ TSLICE
TARRAY
TSTRUCT
TCHAN
@@ -70,9 +71,18 @@ const (
NTYPE
)
+// ChanDir is whether a channel can send, receive, or both.
+type ChanDir uint8
+
+func (c ChanDir) CanRecv() bool { return c&Crecv != 0 }
+func (c ChanDir) CanSend() bool { return c&Csend != 0 }
+
const (
- sliceBound = -1 // slices have Bound=sliceBound
- dddBound = -100 // arrays declared as [...]T start life with Bound=dddBound
+ // types of channel
+ // must match ../../../../reflect/type.go:/ChanDir
+ Crecv ChanDir = 1 << 0
+ Csend ChanDir = 1 << 1
+ Cboth ChanDir = Crecv | Csend
)
// Types stores pointers to predeclared named types.
@@ -108,55 +118,168 @@ var (
// A Type represents a Go type.
type Type struct {
- Etype EType
- Noalg bool
- Chan ChanDir
- Trecur uint8 // to detect loops
- Printed bool
- Funarg bool // on TSTRUCT and TFIELD
- Local bool // created in this file
- Deferwidth bool
- Broke bool // broken type definition.
- Align uint8
- Haspointers uint8 // 0 unknown, 1 no, 2 yes
- Outnamed bool // on TFUNC
+ // Extra contains extra etype-specific fields.
+ // As an optimization, those etype-specific structs which contain exactly
+ // one pointer-shaped field are stored as values rather than pointers when possible.
+ //
+ // TMAP: *MapType
+ // TFORW: *ForwardType
+ // TFUNC: *FuncType
+ // TINTERMETHOD: InterMethType
+ // TSTRUCT: *StructType
+ // TINTER: *InterType
+ // TDDDFIELD: DDDFieldType
+ // TFUNCARGS: FuncArgsType
+ // TCHANARGS: ChanArgsType
+ // TCHAN: *ChanType
+ // TPTR32, TPTR64: PtrType
+ // TARRAY: *ArrayType
+ // TSLICE: SliceType
+ Extra interface{}
- Nod *Node // canonical OTYPE node
- Orig *Type // original type (type literal or predefined type)
+ // Width is the width of this Type in bytes.
+ Width int64
methods Fields
allMethods Fields
- Sym *Sym
+ Nod *Node // canonical OTYPE node
+ Orig *Type // original type (type literal or predefined type)
+
+ Sym *Sym // symbol containing name, for named types
Vargen int32 // unique name for OTYPE/ONAME
- Lineno int32
+ Lineno int32 // line at which this type was declared, implicitly or explicitly
+
+ Etype EType // kind of type
+ Noalg bool // suppress hash and eq algorithm generation
+ Trecur uint8 // to detect loops
+ Printed bool // prevent duplicate export printing
+ Local bool // created in this file
+ Deferwidth bool
+ Broke bool // broken type definition.
+ Align uint8 // the required alignment of this type, in bytes
+}
+
+// MapType contains Type fields specific to maps.
+type MapType struct {
+ Key *Type // Key type
+ Val *Type // Val (elem) type
+
+ Bucket *Type // internal struct type representing a hash bucket
+ Hmap *Type // internal struct type representing the Hmap (map header object)
+ Hiter *Type // internal struct type representing hash iterator state
+}
+
+// MapType returns t's extra map-specific fields.
+func (t *Type) MapType() *MapType {
+ t.wantEtype(TMAP)
+ return t.Extra.(*MapType)
+}
- nname *Node
+// ForwardType contains Type fields specific to forward types.
+type ForwardType struct {
+ Copyto []*Node // where to copy the eventual value to
+ Embedlineno int32 // first use of this type as an embedded type
+}
+
+// ForwardType returns t's extra forward-type-specific fields.
+func (t *Type) ForwardType() *ForwardType {
+ t.wantEtype(TFORW)
+ return t.Extra.(*ForwardType)
+}
+
+// FuncType contains Type fields specific to func types.
+type FuncType struct {
+ Receiver *Type // function receiver
+ Results *Type // function results
+ Params *Type // function params
+
+ Nname *Node
+
+ // Argwid is the total width of the function receiver, params, and results.
+ // It gets calculated via a temporary TFUNCARGS type.
+ // Note that TFUNC's Width is Widthptr.
Argwid int64
- // most nodes
- Type *Type // element type for TARRAY, TCHAN, TMAP, TPTRxx
- Width int64
+ Outnamed bool
+}
- // TSTRUCT
+// FuncType returns t's extra func-specific fields.
+func (t *Type) FuncType() *FuncType {
+ t.wantEtype(TFUNC)
+ return t.Extra.(*FuncType)
+}
+
+// InterMethType contains Type fields specific to interface method psuedo-types.
+type InterMethType struct {
+ Nname *Node
+}
+
+// StructType contains Type fields specific to struct types.
+type StructType struct {
fields Fields
- Down *Type // key type in TMAP; next struct in Funarg TSTRUCT
+ // Maps have three associated internal structs (see struct MapType).
+ // Map links such structs back to their map type.
+ Map *Type
- // TARRAY
- Bound int64 // negative is slice
+ Funarg bool // whether this struct represents function parameters
+ Haspointers uint8 // 0 unknown, 1 no, 2 yes
+}
- // 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.
+// StructType returns t's extra struct-specific fields.
+func (t *Type) StructType() *StructType {
+ t.wantEtype(TSTRUCT)
+ return t.Extra.(*StructType)
+}
- Maplineno int32 // first use of TFORW as map key
- Embedlineno int32 // first use of TFORW as embedded type
+// InterType contains Type fields specific to interface types.
+type InterType struct {
+ fields Fields
+}
+
+// PtrType contains Type fields specific to pointer types.
+type PtrType struct {
+ Elem *Type // element type
+}
+
+// DDDFieldType contains Type fields specific to TDDDFIELD types.
+type DDDFieldType struct {
+ T *Type // reference to a slice type for ... args
+}
+
+// ChanArgsType contains Type fields specific to TCHANARGS types.
+type ChanArgsType struct {
+ T *Type // reference to a chan type whose elements need a width check
+}
- // for TFORW, where to copy the eventual value to
- Copyto []*Node
+// // FuncArgsType contains Type fields specific to TFUNCARGS types.
+type FuncArgsType struct {
+ T *Type // reference to a func type whose elements need a width check
+}
+
+// ChanType contains Type fields specific to channel types.
+type ChanType struct {
+ Elem *Type // element type
+ Dir ChanDir // channel direction
+}
+
+// ChanType returns t's extra channel-specific fields.
+func (t *Type) ChanType() *ChanType {
+ t.wantEtype(TCHAN)
+ return t.Extra.(*ChanType)
+}
+
+// ArrayType contains Type fields specific to array types.
+type ArrayType struct {
+ Elem *Type // element type
+ Bound int64 // number of elements; <0 if unknown yet
+ Haspointers uint8 // 0 unknown, 1 no, 2 yes
+}
+
+// SliceType contains Type fields specific to slice types.
+type SliceType struct {
+ Elem *Type // element type
}
// A Field represents a field in a struct or a method in an interface or
@@ -177,7 +300,7 @@ type Field struct {
// or interface Type.
Offset int64
- Note *string // literal string annotation
+ Note string // literal string annotation
}
// End returns the offset of the first byte immediately after this field.
@@ -209,6 +332,12 @@ func (f *Fields) Slice() []*Field {
return *f.s
}
+// Index returns the i'th element of Fields.
+// It panics if f does not have at least i+1 elements.
+func (f *Fields) Index(i int) *Field {
+ return (*f.s)[i]
+}
+
// Set sets f to a slice.
// This takes ownership of the slice.
func (f *Fields) Set(s []*Field) {
@@ -238,71 +367,103 @@ func typ(et EType) *Type {
Lineno: lineno,
}
t.Orig = t
+ // TODO(josharian): lazily initialize some of these?
+ switch t.Etype {
+ case TMAP:
+ t.Extra = new(MapType)
+ case TFORW:
+ t.Extra = new(ForwardType)
+ case TFUNC:
+ t.Extra = new(FuncType)
+ case TINTERMETH:
+ t.Extra = InterMethType{}
+ case TSTRUCT:
+ t.Extra = new(StructType)
+ case TINTER:
+ t.Extra = new(InterType)
+ case TPTR32, TPTR64:
+ t.Extra = PtrType{}
+ case TCHANARGS:
+ t.Extra = ChanArgsType{}
+ case TFUNCARGS:
+ t.Extra = FuncArgsType{}
+ case TDDDFIELD:
+ t.Extra = DDDFieldType{}
+ case TCHAN:
+ t.Extra = new(ChanType)
+ }
return t
}
// typArray returns a new fixed-length array Type.
func typArray(elem *Type, bound int64) *Type {
+ if bound < 0 {
+ Fatalf("typArray: invalid bound %v", bound)
+ }
t := typ(TARRAY)
- t.Type = elem
- t.Bound = bound
+ t.Extra = &ArrayType{Elem: elem, Bound: bound}
return t
}
// typSlice returns a new slice Type.
func typSlice(elem *Type) *Type {
- t := typ(TARRAY)
- t.Type = elem
- t.Bound = sliceBound
+ t := typ(TSLICE)
+ t.Extra = SliceType{Elem: elem}
return t
}
// typDDDArray returns a new [...]T array Type.
func typDDDArray(elem *Type) *Type {
t := typ(TARRAY)
- t.Type = elem
- t.Bound = dddBound
+ t.Extra = &ArrayType{Elem: elem, Bound: -1}
return t
}
// typChan returns a new chan Type with direction dir.
func typChan(elem *Type, dir ChanDir) *Type {
t := typ(TCHAN)
- t.Type = elem
- t.Chan = dir
+ ct := t.ChanType()
+ ct.Elem = elem
+ ct.Dir = dir
return t
}
// typMap returns a new map Type with key type k and element (aka value) type v.
func typMap(k, v *Type) *Type {
- if k != nil {
- checkMapKeyType(k)
- }
-
t := typ(TMAP)
- t.Down = k
- t.Type = v
+ mt := t.MapType()
+ mt.Key = k
+ mt.Val = v
return t
}
// typPtr returns a new pointer type pointing to t.
func typPtr(elem *Type) *Type {
t := typ(Tptr)
- t.Type = elem
+ t.Extra = PtrType{Elem: elem}
t.Width = int64(Widthptr)
t.Align = uint8(Widthptr)
return t
}
-// typWrapper returns a new wrapper psuedo-type.
-func typWrapper(et EType, wrapped *Type) *Type {
- switch et {
- case TCHANARGS, TFUNCARGS, TDDDFIELD:
- default:
- Fatalf("typWrapper bad etype %s", et)
- }
- t := typ(et)
- t.Type = wrapped
+// typDDDField returns a new TDDDFIELD type for slice type s.
+func typDDDField(s *Type) *Type {
+ t := typ(TDDDFIELD)
+ t.Extra = DDDFieldType{T: s}
+ return t
+}
+
+// typChanArgs returns a new TCHANARGS type for channel type c.
+func typChanArgs(c *Type) *Type {
+ t := typ(TCHANARGS)
+ t.Extra = ChanArgsType{T: c}
+ return t
+}
+
+// typFuncArgs returns a new TFUNCARGS type for func type f.
+func typFuncArgs(f *Type) *Type {
+ t := typ(TFUNCARGS)
+ t.Extra = FuncArgsType{T: f}
return t
}
@@ -348,20 +509,41 @@ func substAny(t *Type, types *[]*Type) *Type {
t = (*types)[0]
*types = (*types)[1:]
- case TPTR32, TPTR64, TCHAN, TARRAY:
- elem := substAny(t.Type, types)
- if elem != t.Type {
+ case TPTR32, TPTR64:
+ elem := substAny(t.Elem(), types)
+ if elem != t.Elem() {
t = t.Copy()
- t.Type = elem
+ t.Extra = PtrType{Elem: elem}
+ }
+
+ case TARRAY:
+ elem := substAny(t.Elem(), types)
+ if elem != t.Elem() {
+ t = t.Copy()
+ t.Extra.(*ArrayType).Elem = elem
+ }
+
+ case TSLICE:
+ elem := substAny(t.Elem(), types)
+ if elem != t.Elem() {
+ t = t.Copy()
+ t.Extra = SliceType{Elem: elem}
+ }
+
+ case TCHAN:
+ elem := substAny(t.Elem(), types)
+ if elem != t.Elem() {
+ t = t.Copy()
+ t.Extra.(*ChanType).Elem = elem
}
case TMAP:
- key := substAny(t.Down, types)
- val := substAny(t.Type, types)
- if key != t.Down || val != t.Type {
+ key := substAny(t.Key(), types)
+ val := substAny(t.Val(), types)
+ if key != t.Key() || val != t.Val() {
t = t.Copy()
- t.Down = key
- t.Type = val
+ t.Extra.(*MapType).Key = key
+ t.Extra.(*MapType).Val = val
}
case TFUNC:
@@ -412,6 +594,30 @@ func (t *Type) Copy() *Type {
return nil
}
nt := *t
+ // copy any *T Extra fields, to avoid aliasing
+ switch t.Etype {
+ case TMAP:
+ x := *t.Extra.(*MapType)
+ nt.Extra = &x
+ case TFORW:
+ x := *t.Extra.(*ForwardType)
+ nt.Extra = &x
+ case TFUNC:
+ x := *t.Extra.(*FuncType)
+ nt.Extra = &x
+ case TSTRUCT:
+ x := *t.Extra.(*StructType)
+ nt.Extra = &x
+ case TINTER:
+ x := *t.Extra.(*InterType)
+ nt.Extra = &x
+ case TCHAN:
+ x := *t.Extra.(*ChanType)
+ nt.Extra = &x
+ case TARRAY:
+ x := *t.Extra.(*ArrayType)
+ nt.Extra = &x
+ }
// TODO(mdempsky): Find out why this is necessary and explain.
if t.Orig == t {
nt.Orig = &nt
@@ -469,17 +675,17 @@ func (t *Type) wantEtype2(et1, et2 EType) {
func (t *Type) RecvsP() **Type {
t.wantEtype(TFUNC)
- return &t.Type
+ return &t.Extra.(*FuncType).Receiver
}
func (t *Type) ParamsP() **Type {
t.wantEtype(TFUNC)
- return &t.Type.Down.Down
+ return &t.Extra.(*FuncType).Params
}
func (t *Type) ResultsP() **Type {
t.wantEtype(TFUNC)
- return &t.Type.Down
+ return &t.Extra.(*FuncType).Results
}
func (t *Type) Recvs() *Type { return *t.RecvsP() }
@@ -510,46 +716,77 @@ var paramsResults = [2]func(*Type) *Type{
// Key returns the key type of map type t.
func (t *Type) Key() *Type {
t.wantEtype(TMAP)
- return t.Down
+ return t.Extra.(*MapType).Key
}
// Val returns the value type of map type t.
func (t *Type) Val() *Type {
t.wantEtype(TMAP)
- return t.Type
+ return t.Extra.(*MapType).Val
}
// Elem returns the type of elements of t.
// Usable with pointers, channels, arrays, and slices.
func (t *Type) Elem() *Type {
switch t.Etype {
- case TPTR32, TPTR64, TCHAN, TARRAY:
- default:
- Fatalf("Type.Elem %s", t.Etype)
+ case TPTR32, TPTR64:
+ return t.Extra.(PtrType).Elem
+ case TARRAY:
+ return t.Extra.(*ArrayType).Elem
+ case TSLICE:
+ return t.Extra.(SliceType).Elem
+ case TCHAN:
+ return t.Extra.(*ChanType).Elem
}
- return t.Type
+ Fatalf("Type.Elem %s", t.Etype)
+ return nil
}
-// Wrapped returns the type that pseudo-type t wraps.
-func (t *Type) Wrapped() *Type {
- switch t.Etype {
- case TCHANARGS, TFUNCARGS, TDDDFIELD:
- default:
- Fatalf("Type.Wrapped %s", t.Etype)
- }
- return t.Type
+// DDDField returns the slice ... type for TDDDFIELD type t.
+func (t *Type) DDDField() *Type {
+ t.wantEtype(TDDDFIELD)
+ return t.Extra.(DDDFieldType).T
+}
+
+// ChanArgs returns the channel type for TCHANARGS type t.
+func (t *Type) ChanArgs() *Type {
+ t.wantEtype(TCHANARGS)
+ return t.Extra.(ChanArgsType).T
+}
+
+// FuncArgs returns the channel type for TFUNCARGS type t.
+func (t *Type) FuncArgs() *Type {
+ t.wantEtype(TFUNCARGS)
+ return t.Extra.(FuncArgsType).T
}
// Nname returns the associated function's nname.
func (t *Type) Nname() *Node {
- t.wantEtype2(TFUNC, TINTERMETH)
- return t.nname
+ switch t.Etype {
+ case TFUNC:
+ return t.Extra.(*FuncType).Nname
+ case TINTERMETH:
+ return t.Extra.(InterMethType).Nname
+ }
+ Fatalf("Type.Nname %v %v", t.Etype, t)
+ return nil
}
// Nname sets the associated function's nname.
func (t *Type) SetNname(n *Node) {
- t.wantEtype2(TFUNC, TINTERMETH)
- t.nname = n
+ switch t.Etype {
+ case TFUNC:
+ t.Extra.(*FuncType).Nname = n
+ case TINTERMETH:
+ t.Extra = InterMethType{Nname: n}
+ default:
+ Fatalf("Type.SetNname %v %v", t.Etype, t)
+ }
+}
+
+// IsFuncArgStruct reports whether t is a struct representing function parameters.
+func (t *Type) IsFuncArgStruct() bool {
+ return t.Etype == TSTRUCT && t.Extra.(*StructType).Funarg
}
func (t *Type) Methods() *Fields {
@@ -563,10 +800,14 @@ func (t *Type) AllMethods() *Fields {
}
func (t *Type) Fields() *Fields {
- if t.Etype != TSTRUCT && t.Etype != TINTER {
- Fatalf("Fields: type %v does not have fields", t)
+ switch t.Etype {
+ case TSTRUCT:
+ return &t.Extra.(*StructType).fields
+ case TINTER:
+ return &t.Extra.(*InterType).fields
}
- return &t.fields
+ Fatalf("Fields: type %v does not have fields", t)
+ return nil
}
// Field returns the i'th field/method of struct/interface type t.
@@ -589,15 +830,14 @@ func (t *Type) isDDDArray() bool {
if t.Etype != TARRAY {
return false
}
- t.checkBound()
- return t.Bound == dddBound
+ return t.Extra.(*ArrayType).Bound < 0
}
// ArgWidth returns the total aligned argument size for a function.
// It includes the receiver, parameters, and results.
func (t *Type) ArgWidth() int64 {
t.wantEtype(TFUNC)
- return t.Argwid
+ return t.Extra.(*FuncType).Argwid
}
func (t *Type) Size() int64 {
@@ -611,22 +851,15 @@ func (t *Type) Alignment() int64 {
}
func (t *Type) SimpleString() string {
- return Econv(t.Etype)
-}
-
-func (t *Type) Equal(u ssa.Type) bool {
- x, ok := u.(*Type)
- return ok && Eqtype(t, x)
+ return t.Etype.String()
}
// Compare compares types for purposes of the SSA back
// end, returning an ssa.Cmp (one of CMPlt, CMPeq, CMPgt).
// The answers are correct for an optimizer
-// or code generator, but not for Go source.
-// For example, "type gcDrainFlags int" results in
-// two Go-different types that Compare equal.
-// The order chosen is also arbitrary, only division into
-// equivalence classes (Types that compare CMPeq) matters.
+// or code generator, but not necessarily typechecking.
+// The order chosen is arbitrary, only consistency and division
+// into equivalence classes (Types that compare CMPeq) matters.
func (t *Type) Compare(u ssa.Type) ssa.Cmp {
x, ok := u.(*Type)
// ssa.CompilerType is smaller than gc.Type
@@ -740,25 +973,25 @@ func (t *Type) cmp(x *Type) ssa.Cmp {
}
return t.Val().cmp(x.Val())
- case TPTR32, TPTR64:
- // No special cases for these two, they are handled
+ case TPTR32, TPTR64, TSLICE:
+ // No special cases for these, they are handled
// by the general code after the switch.
case TSTRUCT:
- if t.Map == nil {
- if x.Map != nil {
+ if t.StructType().Map == nil {
+ if x.StructType().Map != nil {
return ssa.CMPlt // nil < non-nil
}
// to the fallthrough
- } else if x.Map == nil {
+ } else if x.StructType().Map == nil {
return ssa.CMPgt // nil > non-nil
- } else if t.Map.Bucket == t {
+ } else if t.StructType().Map.MapType().Bucket == t {
// Both have non-nil Map
// Special case for Maps which include a recursive type where the recursion is not broken with a named type
- if x.Map.Bucket != x {
+ if x.StructType().Map.MapType().Bucket != x {
return ssa.CMPlt // bucket maps are least
}
- return t.Map.cmp(x.Map)
+ return t.StructType().Map.cmp(x.StructType().Map)
} // If t != t.Map.Bucket, fall through to general case
fallthrough
@@ -770,15 +1003,7 @@ func (t *Type) cmp(x *Type) ssa.Cmp {
return cmpForNe(t1.Embedded < x1.Embedded)
}
if t1.Note != x1.Note {
- if t1.Note == nil {
- return ssa.CMPlt
- }
- if x1.Note == nil {
- return ssa.CMPgt
- }
- if *t1.Note != *x1.Note {
- return cmpForNe(*t1.Note < *x1.Note)
- }
+ return cmpForNe(t1.Note < x1.Note)
}
if c := t1.Sym.cmpsym(x1.Sym); c != ssa.CMPeq {
return c
@@ -826,7 +1051,7 @@ func (t *Type) cmp(x *Type) ssa.Cmp {
panic(e)
}
- // Common element type comparison for TARRAY, TCHAN, TPTR32, and TPTR64.
+ // Common element type comparison for TARRAY, TCHAN, TPTR32, TPTR64, and TSLICE.
return t.Elem().cmp(x.Elem())
}
@@ -869,6 +1094,11 @@ func (t *Type) IsPtr() bool {
return t.Etype == TPTR32 || t.Etype == TPTR64
}
+// IsUnsafePtr reports whether t is an unsafe pointer.
+func (t *Type) IsUnsafePtr() bool {
+ return t.Etype == TUNSAFEPTR
+}
+
// IsPtrShaped reports whether t is represented by a single machine pointer.
// In addition to regular Go pointer types, this includes map, channel, and
// function types and unsafe.Pointer. It does not include array or struct types
@@ -891,21 +1121,12 @@ func (t *Type) IsChan() bool {
return t.Etype == TCHAN
}
-// checkBound enforces that Bound has an acceptable value.
-func (t *Type) checkBound() {
- if t.Bound != sliceBound && t.Bound < 0 && t.Bound != dddBound {
- Fatalf("bad TARRAY bounds %d %s", t.Bound, t)
- }
-}
-
func (t *Type) IsSlice() bool {
- t.checkBound()
- return t.Etype == TARRAY && t.Bound == sliceBound
+ return t.Etype == TSLICE
}
func (t *Type) IsArray() bool {
- t.checkBound()
- return t.Etype == TARRAY && t.Bound >= 0
+ return t.Etype == TARRAY
}
func (t *Type) IsStruct() bool {
@@ -939,27 +1160,36 @@ func (t *Type) FieldType(i int) ssa.Type {
func (t *Type) FieldOff(i int) int64 {
return t.Field(i).Offset
}
+func (t *Type) FieldName(i int) string {
+ return t.Field(i).Sym.Name
+}
func (t *Type) NumElem() int64 {
t.wantEtype(TARRAY)
- t.checkBound()
- return t.Bound
+ at := t.Extra.(*ArrayType)
+ if at.Bound < 0 {
+ Fatalf("NumElem array %v does not have bound yet", t)
+ }
+ return at.Bound
}
// SetNumElem sets the number of elements in an array type.
-// It should not be used if at all possible.
-// Create a new array/slice/dddArray with typX instead.
-// TODO(josharian): figure out how to get rid of this.
+// The only allowed use is on array types created with typDDDArray.
+// For other uses, create a new array with typArray instead.
func (t *Type) SetNumElem(n int64) {
t.wantEtype(TARRAY)
- t.Bound = n
+ at := t.Extra.(*ArrayType)
+ if at.Bound >= 0 {
+ Fatalf("SetNumElem array %v already has bound %d", t, at.Bound)
+ }
+ at.Bound = n
}
// ChanDir returns the direction of a channel type t.
// The direction will be one of Crecv, Csend, or Cboth.
func (t *Type) ChanDir() ChanDir {
t.wantEtype(TCHAN)
- return t.Chan
+ return t.Extra.(*ChanType).Dir
}
func (t *Type) IsMemory() bool { return false }
diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go
index 688936e926..cf44ac8678 100644
--- a/src/cmd/compile/internal/gc/typecheck.go
+++ b/src/cmd/compile/internal/gc/typecheck.go
@@ -11,6 +11,16 @@ import (
"strings"
)
+const (
+ Etop = 1 << iota // evaluated at statement level
+ Erv // evaluated in value context
+ Etype // evaluated in type context
+ Ecall // call-only expressions are ok
+ Efnstruct // multivalue function returns are ok
+ Easgn // assigning to expression
+ Ecomplit // type in composite literal
+)
+
// type check the whole tree of an expression.
// calculates expression types.
// evaluates compile time constants.
@@ -66,6 +76,7 @@ var _typekind = []string{
TCHAN: "chan",
TMAP: "map",
TARRAY: "array",
+ TSLICE: "slice",
TFUNC: "func",
TNIL: "nil",
TIDEAL: "untyped number",
@@ -267,7 +278,7 @@ OpSwitch:
default:
Dump("typecheck", n)
- Fatalf("typecheck %v", Oconv(n.Op, 0))
+ Fatalf("typecheck %v", n.Op)
// names
case OLITERAL:
@@ -324,7 +335,6 @@ OpSwitch:
ok |= Etype
if n.Type == nil {
- n.Type = nil
return n
}
@@ -403,6 +413,18 @@ OpSwitch:
}
n.Op = OTYPE
n.Type = typMap(l.Type, r.Type)
+
+ // map key validation
+ alg, bad := algtype1(l.Type)
+ if alg == ANOEQ {
+ if bad.Etype == TFORW {
+ // queue check for map until all the types are done settling.
+ mapqueue = append(mapqueue, mapqueueval{l, n.Lineno})
+ } else if bad.Etype != TANY {
+ // no need to queue, key is already bad
+ Yyerror("invalid map key type %v", l.Type)
+ }
+ }
n.Left = nil
n.Right = nil
@@ -435,7 +457,6 @@ OpSwitch:
n.Op = OTYPE
n.Type = tointerface(n.List.Slice())
if n.Type == nil {
- n.Type = nil
return n
}
@@ -444,7 +465,6 @@ OpSwitch:
n.Op = OTYPE
n.Type = functype(n.Left, n.List.Slice(), n.Rlist.Slice())
if n.Type == nil {
- n.Type = nil
return n
}
n.Left = nil
@@ -453,13 +473,7 @@ OpSwitch:
// 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
- n.Left = typecheck(n.Left, ntop)
+ n.Left = typecheck(n.Left, Erv|Etype|top&Ecomplit)
l := n.Left
t := l.Type
if t == nil {
@@ -533,8 +547,8 @@ OpSwitch:
op = Op(n.Etype)
} else {
ok |= Erv
- n.Left = typecheck(n.Left, Erv|top&Eiota)
- n.Right = typecheck(n.Right, Erv|top&Eiota)
+ n.Left = typecheck(n.Left, Erv)
+ n.Right = typecheck(n.Right, Erv)
l = n.Left
r = n.Right
if l.Type == nil || r.Type == nil {
@@ -597,7 +611,7 @@ OpSwitch:
aop = assignop(l.Type, r.Type, nil)
if aop != 0 {
if r.Type.IsInterface() && !l.Type.IsInterface() && !l.Type.IsComparable() {
- Yyerror("invalid operation: %v (operator %v not defined on %s)", n, Oconv(op, 0), typekind(l.Type))
+ Yyerror("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(l.Type))
n.Type = nil
return n
}
@@ -619,7 +633,7 @@ OpSwitch:
aop = assignop(r.Type, l.Type, nil)
if aop != 0 {
if l.Type.IsInterface() && !r.Type.IsInterface() && !r.Type.IsComparable() {
- Yyerror("invalid operation: %v (operator %v not defined on %s)", n, Oconv(op, 0), typekind(r.Type))
+ Yyerror("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(r.Type))
n.Type = nil
return n
}
@@ -650,7 +664,7 @@ OpSwitch:
}
if !okfor[op][et] {
- Yyerror("invalid operation: %v (operator %v not defined on %s)", n, Oconv(op, 0), typekind(t))
+ Yyerror("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(t))
n.Type = nil
return n
}
@@ -752,7 +766,7 @@ OpSwitch:
case OCOM, OMINUS, ONOT, OPLUS:
ok |= Erv
- n.Left = typecheck(n.Left, Erv|top&Eiota)
+ n.Left = typecheck(n.Left, Erv)
l := n.Left
t := l.Type
if t == nil {
@@ -760,7 +774,7 @@ OpSwitch:
return n
}
if !okfor[n.Op][t.Etype] {
- Yyerror("invalid operation: %v %v", Oconv(n.Op, 0), t)
+ Yyerror("invalid operation: %v %v", n.Op, t)
n.Type = nil
return n
}
@@ -772,7 +786,7 @@ OpSwitch:
case OADDR:
ok |= Erv
- n.Left = typecheck(n.Left, Erv|Eaddr)
+ n.Left = typecheck(n.Left, Erv)
if n.Left.Type == nil {
n.Type = nil
return n
@@ -808,7 +822,6 @@ OpSwitch:
ok |= Erv
n = typecheckcomplit(n)
if n.Type == nil {
- n.Type = nil
return n
}
break OpSwitch
@@ -850,7 +863,6 @@ OpSwitch:
if n.Type.Etype != TFUNC || n.Type.Recv() == nil {
Yyerror("type %v has no method %v", n.Left.Type, Sconv(n.Right.Sym, FmtShort))
n.Type = nil
- n.Type = nil
return n
}
@@ -986,7 +998,7 @@ OpSwitch:
n.Type = nil
return n
- case TSTRING, TARRAY:
+ case TSTRING, TARRAY, TSLICE:
n.Right = indexlit(n.Right)
if t.IsString() {
n.Type = bytetype
@@ -994,12 +1006,10 @@ OpSwitch:
n.Type = t.Elem()
}
why := "string"
- if t.Etype == TARRAY {
- if t.IsArray() {
- why = "array"
- } else {
- why = "slice"
- }
+ if t.IsArray() {
+ why = "array"
+ } else if t.IsSlice() {
+ why = "slice"
}
if n.Right.Type != nil && !n.Right.Type.IsInteger() {
@@ -1095,14 +1105,19 @@ OpSwitch:
n.Type = nil
break OpSwitch
- case OSLICE:
+ case OSLICE, OSLICE3:
ok |= Erv
n.Left = typecheck(n.Left, top)
- n.Right.Left = typecheck(n.Right.Left, Erv)
- n.Right.Right = typecheck(n.Right.Right, Erv)
+ low, high, max := n.SliceBounds()
+ hasmax := n.Op.IsSlice3()
+ low = typecheck(low, Erv)
+ high = typecheck(high, Erv)
+ max = typecheck(max, Erv)
n.Left = defaultlit(n.Left, nil)
- n.Right.Left = indexlit(n.Right.Left)
- n.Right.Right = indexlit(n.Right.Right)
+ low = indexlit(low)
+ high = indexlit(high)
+ max = indexlit(max)
+ n.SetSliceBounds(low, high, max)
l := n.Left
if l.Type.IsArray() {
if !islvalue(n.Left) {
@@ -1124,78 +1139,22 @@ OpSwitch:
}
var tp *Type
if t.IsString() {
+ if hasmax {
+ Yyerror("invalid operation %v (3-index slice of string)", n)
+ n.Type = nil
+ return n
+ }
n.Type = t
n.Op = OSLICESTR
} else if t.IsPtr() && t.Elem().IsArray() {
tp = t.Elem()
n.Type = typSlice(tp.Elem())
dowidth(n.Type)
- n.Op = OSLICEARR
- } else if t.IsSlice() {
- n.Type = t
- } else {
- Yyerror("cannot slice %v (type %v)", l, t)
- n.Type = nil
- return n
- }
-
- lo := n.Right.Left
- if lo != nil && !checksliceindex(l, lo, tp) {
- n.Type = nil
- return n
- }
- hi := n.Right.Right
- if hi != nil && !checksliceindex(l, hi, tp) {
- n.Type = nil
- return n
- }
- if !checksliceconst(lo, hi) {
- n.Type = nil
- return n
- }
- break OpSwitch
-
- case OSLICE3:
- ok |= Erv
- n.Left = typecheck(n.Left, top)
- n.Right.Left = typecheck(n.Right.Left, Erv)
- n.Right.Right.Left = typecheck(n.Right.Right.Left, Erv)
- n.Right.Right.Right = typecheck(n.Right.Right.Right, Erv)
- n.Left = defaultlit(n.Left, nil)
- n.Right.Left = indexlit(n.Right.Left)
- n.Right.Right.Left = indexlit(n.Right.Right.Left)
- n.Right.Right.Right = indexlit(n.Right.Right.Right)
- l := n.Left
- if l.Type.IsArray() {
- if !islvalue(n.Left) {
- Yyerror("invalid operation %v (slice of unaddressable value)", n)
- n.Type = nil
- return n
+ if hasmax {
+ n.Op = OSLICE3ARR
+ } else {
+ n.Op = OSLICEARR
}
-
- n.Left = Nod(OADDR, n.Left, nil)
- n.Left.Implicit = true
- n.Left = typecheck(n.Left, Erv)
- l = n.Left
- }
-
- t := l.Type
- if t == nil {
- n.Type = nil
- return n
- }
- if t.IsString() {
- Yyerror("invalid operation %v (3-index slice of string)", n)
- n.Type = nil
- return n
- }
-
- var tp *Type
- if t.IsPtr() && t.Elem().IsArray() {
- tp = t.Elem()
- n.Type = typSlice(tp.Elem())
- dowidth(n.Type)
- n.Op = OSLICE3ARR
} else if t.IsSlice() {
n.Type = t
} else {
@@ -1204,22 +1163,19 @@ OpSwitch:
return n
}
- lo := n.Right.Left
- if lo != nil && !checksliceindex(l, lo, tp) {
+ if low != nil && !checksliceindex(l, low, tp) {
n.Type = nil
return n
}
- mid := n.Right.Right.Left
- if mid != nil && !checksliceindex(l, mid, tp) {
+ if high != nil && !checksliceindex(l, high, tp) {
n.Type = nil
return n
}
- hi := n.Right.Right.Right
- if hi != nil && !checksliceindex(l, hi, tp) {
+ if max != nil && !checksliceindex(l, max, tp) {
n.Type = nil
return n
}
- if !checksliceconst(lo, hi) || !checksliceconst(lo, mid) || !checksliceconst(mid, hi) {
+ if !checksliceconst(low, high) || !checksliceconst(low, max) || !checksliceconst(high, max) {
n.Type = nil
return n
}
@@ -1241,7 +1197,7 @@ OpSwitch:
}
}
- n.Left = typecheck(n.Left, Erv|Etype|Ecall|top&Eproc)
+ n.Left = typecheck(n.Left, Erv|Etype|Ecall)
n.Diag |= n.Left.Diag
l = n.Left
if l.Op == ONAME && l.Etype != 0 {
@@ -1333,7 +1289,7 @@ OpSwitch:
if t.Results().NumFields() == 1 {
n.Type = l.Type.Results().Field(0).Type
- if n.Op == OCALLFUNC && n.Left.Op == ONAME && (compiling_runtime != 0 || n.Left.Sym.Pkg == Runtimepkg) && n.Left.Sym.Name == "getg" {
+ if n.Op == OCALLFUNC && n.Left.Op == ONAME && (compiling_runtime || 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
@@ -1358,7 +1314,7 @@ OpSwitch:
case OCAP, OLEN, OREAL, OIMAG:
ok |= Erv
- if !onearg(n, "%v", Oconv(n.Op, 0)) {
+ if !onearg(n, "%v", n.Op) {
n.Type = nil
return n
}
@@ -1411,9 +1367,6 @@ OpSwitch:
}
case TARRAY:
- if t.IsSlice() {
- break
- }
if callrecv(l) { // has call or receive
break
}
@@ -1427,7 +1380,7 @@ OpSwitch:
break OpSwitch
badcall1:
- Yyerror("invalid argument %v for %v", Nconv(n.Left, FmtLong), Oconv(n.Op, 0))
+ Yyerror("invalid argument %v for %v", Nconv(n.Left, FmtLong), n.Op)
n.Type = nil
return n
@@ -1458,8 +1411,8 @@ OpSwitch:
n.Type = nil
return n
}
- n.Left = typecheck(n.Left, Erv|top&Eiota)
- n.Right = typecheck(n.Right, Erv|top&Eiota)
+ n.Left = typecheck(n.Left, Erv)
+ n.Right = typecheck(n.Right, Erv)
l = n.Left
r = n.Right
if l.Type == nil || r.Type == nil {
@@ -1510,7 +1463,7 @@ OpSwitch:
break OpSwitch
case OCLOSE:
- if !onearg(n, "%v", Oconv(n.Op, 0)) {
+ if !onearg(n, "%v", n.Op) {
n.Type = nil
return n
}
@@ -1593,7 +1546,7 @@ OpSwitch:
// Unpack multiple-return result before type-checking.
var funarg *Type
- if t.IsStruct() && t.Funarg {
+ if t.IsFuncArgStruct() {
funarg = t
t = t.Field(0).Type
}
@@ -1717,7 +1670,7 @@ OpSwitch:
case OCONV:
ok |= Erv
saveorignode(n)
- n.Left = typecheck(n.Left, Erv|top&(Eindir|Eiota))
+ n.Left = typecheck(n.Left, Erv)
n.Left = convlit1(n.Left, n.Type, true, noReuse)
t := n.Left.Type
if t == nil || n.Type == nil {
@@ -1784,13 +1737,7 @@ OpSwitch:
n.Type = nil
return n
- case TARRAY:
- if !t.IsSlice() {
- Yyerror("cannot make type %v", t)
- n.Type = nil
- return n
- }
-
+ case TSLICE:
if i >= len(args) {
Yyerror("missing len argument to make(%v)", t)
n.Type = nil
@@ -1905,7 +1852,7 @@ OpSwitch:
case OPRINT, OPRINTN:
ok |= Etop
- typecheckslice(n.List.Slice(), Erv|Eindir) // Eindir: address does not escape
+ typecheckslice(n.List.Slice(), Erv)
ls := n.List.Slice()
for i1, n1 := range ls {
// Special case for print: int constant is int64, not int.
@@ -1947,7 +1894,6 @@ OpSwitch:
ok |= Erv
typecheckclosure(n, top)
if n.Type == nil {
- n.Type = nil
return n
}
break OpSwitch
@@ -2042,7 +1988,7 @@ OpSwitch:
case OPROC:
ok |= Etop
- n.Left = typecheck(n.Left, Etop|Eproc|Erv)
+ n.Left = typecheck(n.Left, Etop|Erv)
checkdefergo(n)
break OpSwitch
@@ -2089,7 +2035,7 @@ OpSwitch:
return n
}
- if Curfn.Type.Outnamed && n.List.Len() == 0 {
+ if Curfn.Type.FuncType().Outnamed && n.List.Len() == 0 {
break OpSwitch
}
typecheckaste(ORETURN, nil, false, Curfn.Type.Results(), n.List, func() string { return "return argument" })
@@ -2145,14 +2091,10 @@ OpSwitch:
}
t := n.Type
- if t != nil && !t.Funarg && n.Op != OTYPE {
+ if t != nil && !t.IsFuncArgStruct() && n.Op != OTYPE {
switch t.Etype {
- case TFUNC, // might have TANY; wait until its called
- TANY,
- TFORW,
- TIDEAL,
- TNIL,
- TBLANK:
+ case TFUNC, // might have TANY; wait until it's called
+ TANY, TFORW, TIDEAL, TNIL, TBLANK:
break
default:
@@ -2160,7 +2102,7 @@ OpSwitch:
}
}
- if safemode != 0 && incannedimport == 0 && importpkg == nil && compiling_wrappers == 0 && t != nil && t.Etype == TUNSAFEPTR {
+ if safemode && incannedimport == 0 && importpkg == nil && compiling_wrappers == 0 && t != nil && t.Etype == TUNSAFEPTR {
Yyerror("cannot use unsafe.Pointer")
}
@@ -2342,19 +2284,19 @@ func twoarg(n *Node) bool {
return true
}
if n.List.Len() == 0 {
- Yyerror("missing argument to %v - %v", Oconv(n.Op, 0), n)
+ Yyerror("missing argument to %v - %v", n.Op, n)
return false
}
n.Left = n.List.First()
if n.List.Len() == 1 {
- Yyerror("missing argument to %v - %v", Oconv(n.Op, 0), n)
+ Yyerror("missing argument to %v - %v", n.Op, n)
n.List.Set(nil)
return false
}
if n.List.Len() > 2 {
- Yyerror("too many arguments to %v - %v", Oconv(n.Op, 0), n)
+ Yyerror("too many arguments to %v - %v", n.Op, n)
n.List.Set(nil)
return false
}
@@ -2597,7 +2539,7 @@ func typecheckaste(op Op, call *Node, isddd bool, tstruct *Type, nl Nodes, desc
if nl.Len() == 1 {
n = nl.First()
if n.Type != nil {
- if n.Type.IsStruct() && n.Type.Funarg {
+ if n.Type.IsFuncArgStruct() {
if !hasddd(tstruct) {
n1 := tstruct.NumFields()
n2 := n.Type.NumFields()
@@ -2720,7 +2662,7 @@ func typecheckaste(op Op, call *Node, isddd bool, tstruct *Type, nl Nodes, desc
if call != nil {
Yyerror("invalid use of ... in call to %v", call)
} else {
- Yyerror("invalid use of ... in %v", Oconv(op, 0))
+ Yyerror("invalid use of ... in %v", op)
}
}
@@ -2740,7 +2682,7 @@ notenough:
Yyerror("not enough arguments in call to %v", call)
}
} else {
- Yyerror("not enough arguments to %v", Oconv(op, 0))
+ Yyerror("not enough arguments to %v", op)
}
if n != nil {
n.Diag = 1
@@ -2753,7 +2695,7 @@ toomany:
if call != nil {
Yyerror("too many arguments in call to %v", call)
} else {
- Yyerror("too many arguments to %v", Oconv(op, 0))
+ Yyerror("too many arguments to %v", op)
}
goto out
}
@@ -2842,19 +2784,19 @@ func indexdup(n *Node, hash map[int64]*Node) {
hash[v] = n
}
+// iscomptype reports whether type t is a composite literal type
+// or a pointer to one.
func iscomptype(t *Type) bool {
+ if t.IsPtr() {
+ t = t.Elem()
+ }
+
switch t.Etype {
- case TARRAY, TSTRUCT, TMAP:
+ case TARRAY, TSLICE, TSTRUCT, TMAP:
return true
-
- case TPTR32, TPTR64:
- switch t.Elem().Etype {
- case TARRAY, TSTRUCT, TMAP:
- return true
- }
+ default:
+ return false
}
-
- return false
}
func pushtype(n *Node, t *Type) {
@@ -2937,7 +2879,7 @@ func typecheckcomplit(n *Node) *Node {
Yyerror("invalid type for composite literal: %v", t)
n.Type = nil
- case TARRAY:
+ case TARRAY, TSLICE:
// Only allocate hash if there are some key/value pairs.
var hash map[int64]*Node
for _, n1 := range n.List.Slice() {
@@ -2948,6 +2890,7 @@ func typecheckcomplit(n *Node) *Node {
}
length := int64(0)
i := 0
+ checkBounds := t.IsArray() && !t.isDDDArray()
for i2, n2 := range n.List.Slice() {
l := n2
setlineno(l)
@@ -2973,11 +2916,10 @@ func typecheckcomplit(n *Node) *Node {
i++
if int64(i) > length {
length = int64(i)
- if t.IsArray() && length > t.NumElem() {
+ if checkBounds && length > t.NumElem() {
setlineno(l)
Yyerror("array index %d out of bounds [0:%d]", length-1, t.NumElem())
- // suppress any further errors out of bounds errors for the same type by pretending it is a slice
- t.SetNumElem(sliceBound)
+ checkBounds = false
}
}
@@ -3083,7 +3025,12 @@ func typecheckcomplit(n *Node) *Node {
}
s := l.Left.Sym
- if s == nil {
+
+ // An OXDOT uses the Sym field to hold
+ // the field to the right of the dot,
+ // so s will be non-nil, but an OXDOT
+ // is never a valid struct literal key.
+ if s == nil || l.Left.Op == OXDOT {
Yyerror("invalid field name %v in struct initializer", l.Left)
l.Right = typecheck(l.Right, Erv)
continue
@@ -3152,8 +3099,6 @@ func islvalue(n *Node) bool {
return false
}
fallthrough
-
- // fall through
case OIND, ODOTPTR, OCLOSUREVAR, OPARAM:
return true
@@ -3345,7 +3290,7 @@ func typecheckas2(n *Node) {
}
switch r.Op {
case OCALLMETH, OCALLINTER, OCALLFUNC:
- if !r.Type.IsStruct() || !r.Type.Funarg {
+ if !r.Type.IsFuncArgStruct() {
break
}
cr = r.Type.NumFields()
@@ -3503,18 +3448,23 @@ func domethod(n *Node) {
checkwidth(n.Type)
}
-var mapqueue []*Node
+type mapqueueval struct {
+ n *Node
+ lno int32
+}
+
+// tracks the line numbers at which forward types are first used as map keys
+var mapqueue []mapqueueval
func copytype(n *Node, t *Type) {
if t.Etype == TFORW {
// This type isn't computed yet; when it is, update n.
- t.Copyto = append(t.Copyto, n)
+ t.ForwardType().Copyto = append(t.ForwardType().Copyto, n)
return
}
- maplineno := n.Type.Maplineno
- embedlineno := n.Type.Embedlineno
- l := n.Type.Copyto
+ embedlineno := n.Type.ForwardType().Embedlineno
+ l := n.Type.ForwardType().Copyto
// TODO(mdempsky): Fix Type rekinding.
*n.Type = *t
@@ -3530,7 +3480,6 @@ func copytype(n *Node, t *Type) {
t.Nod = nil
t.Printed = false
t.Deferwidth = false
- t.Copyto = nil
// Update nodes waiting on this type.
for _, n := range l {
@@ -3542,18 +3491,12 @@ func copytype(n *Node, t *Type) {
if embedlineno != 0 {
lineno = embedlineno
- if t.IsPtr() {
+ if t.IsPtr() || t.IsUnsafePtr() {
Yyerror("embedded type cannot be a pointer")
}
}
lineno = lno
-
- // Queue check for map until all the types are done settling.
- if maplineno != 0 {
- t.Maplineno = maplineno
- mapqueue = append(mapqueue, n)
- }
}
func typecheckdeftype(n *Node) {
@@ -3598,12 +3541,13 @@ ret:
domethod(n)
}
}
-
- for _, n := range mapqueue {
- lineno = n.Type.Maplineno
- checkMapKeyType(n.Type)
+ for _, e := range mapqueue {
+ lineno = e.lno
+ if !e.n.Type.IsComparable() {
+ Yyerror("invalid map key type %v", e.n.Type)
+ }
}
-
+ mapqueue = nil
lineno = lno
}
@@ -3662,7 +3606,7 @@ func typecheckdef(n *Node) *Node {
switch n.Op {
default:
- Fatalf("typecheckdef %v", Oconv(n.Op, 0))
+ Fatalf("typecheckdef %v", n.Op)
// not really syms
case OGOTO, OLABEL:
@@ -3687,7 +3631,7 @@ func typecheckdef(n *Node) *Node {
Yyerror("xxx")
}
- e = typecheck(e, Erv|Eiota)
+ e = typecheck(e, Erv)
if Isconst(e, CTNIL) {
Yyerror("const initializer cannot be nil")
goto ret
@@ -3858,11 +3802,8 @@ func markbreak(n *Node, implicit *Node) {
ORANGE:
implicit = n
fallthrough
-
- // fall through
default:
markbreak(n.Left, implicit)
-
markbreak(n.Right, implicit)
markbreaklist(n.Ninit, implicit)
markbreaklist(n.Nbody, implicit)
@@ -3958,7 +3899,7 @@ func (n *Node) isterminating() bool {
}
func checkreturn(fn *Node) {
- if fn.Type.Results().NumFields() != 0 && len(fn.Nbody.Slice()) != 0 {
+ if fn.Type.Results().NumFields() != 0 && fn.Nbody.Len() != 0 {
markbreaklist(fn.Nbody, nil)
if !fn.Nbody.isterminating() {
yyerrorl(fn.Func.Endlineno, "missing return at end of function")
diff --git a/src/cmd/compile/internal/gc/universe.go b/src/cmd/compile/internal/gc/universe.go
index c2ba9c9a93..84df22502f 100644
--- a/src/cmd/compile/internal/gc/universe.go
+++ b/src/cmd/compile/internal/gc/universe.go
@@ -228,6 +228,7 @@ func typeinit() {
okforcap[TARRAY] = true
okforcap[TCHAN] = true
+ okforcap[TSLICE] = true
okforconst[TBOOL] = true
okforconst[TSTRING] = true
@@ -235,6 +236,7 @@ func typeinit() {
okforlen[TARRAY] = true
okforlen[TCHAN] = true
okforlen[TMAP] = true
+ okforlen[TSLICE] = true
okforlen[TSTRING] = true
okforeq[TPTR32] = true
@@ -246,8 +248,9 @@ func typeinit() {
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
+ okforeq[TSLICE] = true // nil only; refined in typecheck
+ okforeq[TARRAY] = true // only if element type is comparable; refined in typecheck
+ okforeq[TSTRUCT] = true // only if all struct fields are comparable; refined in typecheck
okforcmp[TSTRING] = true
@@ -359,16 +362,16 @@ func lexinit1() {
// t = interface { Error() string }
rcvr := typ(TSTRUCT)
- rcvr.Funarg = true
+ rcvr.StructType().Funarg = true
field := newField()
field.Type = Ptrto(typ(TSTRUCT))
rcvr.SetFields([]*Field{field})
in := typ(TSTRUCT)
- in.Funarg = true
+ in.StructType().Funarg = true
out := typ(TSTRUCT)
- out.Funarg = true
+ out.StructType().Funarg = true
field = newField()
field.Type = Types[TSTRING]
out.SetFields([]*Field{field})
diff --git a/src/cmd/compile/internal/gc/unsafe.go b/src/cmd/compile/internal/gc/unsafe.go
index 338f3c0eae..5935cd98ff 100644
--- a/src/cmd/compile/internal/gc/unsafe.go
+++ b/src/cmd/compile/internal/gc/unsafe.go
@@ -9,7 +9,7 @@ func unsafenmagic(nn *Node) *Node {
fn := nn.Left
args := nn.List
- if safemode != 0 || fn == nil || fn.Op != ONAME {
+ if safemode || fn == nil || fn.Op != ONAME {
return nil
}
s := fn.Sym
@@ -82,7 +82,7 @@ func unsafenmagic(nn *Node) *Node {
v += r1.Xoffset
default:
Dump("unsafenmagic", r)
- Fatalf("impossible %v node after dot insertion", Oconv(r1.Op, FmtSharp))
+ Fatalf("impossible %v node after dot insertion", oconv(r1.Op, FmtSharp))
goto bad
}
}
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
index b7edae5af4..6ec06453ef 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -6,6 +6,7 @@ package gc
import (
"cmd/internal/obj"
+ "cmd/internal/sys"
"fmt"
"strings"
)
@@ -69,7 +70,7 @@ func walk(fn *Node) {
}
heapmoves()
- if Debug['W'] != 0 && len(Curfn.Func.Enter.Slice()) > 0 {
+ if Debug['W'] != 0 && Curfn.Func.Enter.Len() > 0 {
s := fmt.Sprintf("enter %v", Curfn.Func.Nname.Sym)
dumplist(s, Curfn.Func.Enter)
}
@@ -158,7 +159,7 @@ func walkstmt(n *Node) *Node {
if n.Op == ONAME {
Yyerror("%v is not a top level statement", n.Sym)
} else {
- Yyerror("%v is not a top level statement", Oconv(n.Op, 0))
+ Yyerror("%v is not a top level statement", n.Op)
}
Dump("nottop", n)
@@ -287,7 +288,7 @@ func walkstmt(n *Node) *Node {
if n.List.Len() == 0 {
break
}
- if (Curfn.Type.Outnamed && n.List.Len() > 1) || paramoutheap(Curfn) {
+ if (Curfn.Type.FuncType().Outnamed && n.List.Len() > 1) || paramoutheap(Curfn) {
// assign to the function out parameters,
// so that reorder3 can fix up conflicts
var rl []*Node
@@ -593,8 +594,7 @@ opswitch:
// for a struct containing a reflect.Value, which itself has
// an unexported field of type unsafe.Pointer.
old_safemode := safemode
-
- safemode = 0
+ safemode = false
n = walkcompare(n, init)
safemode = old_safemode
@@ -672,8 +672,7 @@ opswitch:
walkexprlist(n.List.Slice(), init)
if n.Left.Op == ONAME && n.Left.Sym.Name == "Sqrt" && n.Left.Sym.Pkg.Path == "math" {
- switch Thearch.Thechar {
- case '5', '6', '7', '9':
+ if Thearch.LinkArch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.PPC64, sys.S390X) {
n.Op = OSQRT
n.Left = n.List.First()
n.List.Set(nil)
@@ -865,8 +864,14 @@ opswitch:
// a = *var
a := n.List.First()
- fn := mapfn(p, t)
- r = mkcall1(fn, fn.Type.Results(), init, typename(t), r.Left, key)
+ if w := t.Val().Width; w <= 1024 { // 1024 must match ../../../../runtime/hashmap.go:maxZero
+ fn := mapfn(p, t)
+ r = mkcall1(fn, fn.Type.Results(), init, typename(t), r.Left, key)
+ } else {
+ fn := mapfn("mapaccess2_fat", t)
+ z := zeroaddr(w)
+ r = mkcall1(fn, fn.Type.Results(), init, typename(t), r.Left, key, z)
+ }
// mapaccess2* returns a typed bool, but due to spec changes,
// the boolean result of i.(T) is now untyped so we make it the
@@ -881,6 +886,7 @@ opswitch:
if !isblank(a) {
var_ := temp(Ptrto(t.Val()))
var_.Typecheck = 1
+ var_.NonNil = true // mapaccess always returns a non-nil pointer
n.List.SetIndex(0, var_)
n = walkexpr(n, init)
init.Append(n)
@@ -890,8 +896,6 @@ opswitch:
n = typecheck(n, Etop)
n = walkexpr(n, init)
- // TODO: ptr is always non-nil, so disable nil check for this OIND op.
-
case ODELETE:
init.AppendNodes(&n.Ninit)
map_ := n.List.First()
@@ -1056,7 +1060,7 @@ opswitch:
n = walkexpr(n, init)
case OCONV, OCONVNOP:
- if Thearch.Thechar == '5' {
+ if Thearch.LinkArch.Family == sys.ARM {
if n.Left.Type.IsFloat() {
if n.Type.Etype == TINT64 {
n = mkcall("float64toint64", n.Type, init, conv(n.Left, Types[TFLOAT64]))
@@ -1219,11 +1223,17 @@ opswitch:
// 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.Val()), init, typename(t), n.Left, key)
+ if w := t.Val().Width; w <= 1024 { // 1024 must match ../../../../runtime/hashmap.go:maxZero
+ n = mkcall1(mapfn(p, t), Ptrto(t.Val()), init, typename(t), n.Left, key)
+ } else {
+ p = "mapaccess1_fat"
+ z := zeroaddr(w)
+ n = mkcall1(mapfn(p, t), Ptrto(t.Val()), init, typename(t), n.Left, key, z)
+ }
+ n.NonNil = true // mapaccess always returns a non-nil pointer
n = Nod(OIND, n, nil)
n.Type = t.Val()
n.Typecheck = 1
@@ -1231,35 +1241,28 @@ opswitch:
case ORECV:
Fatalf("walkexpr ORECV") // should see inside OAS only
- case OSLICE, OSLICEARR, OSLICESTR:
- n.Left = walkexpr(n.Left, init)
- n.Right.Left = 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
- }
- n.Right.Right = walkexpr(n.Right.Right, init)
- n = reduceSlice(n)
-
- case OSLICE3, OSLICE3ARR:
+ case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR:
n.Left = walkexpr(n.Left, init)
- n.Right.Left = 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
+ low, high, max := n.SliceBounds()
+ low = walkexpr(low, init)
+ if low != nil && iszero(low) {
+ // Reduce x[0:j] to x[:j] and x[0:j:k] to x[:j:k].
+ low = nil
}
- n.Right.Right.Left = walkexpr(n.Right.Right.Left, init)
- n.Right.Right.Right = 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
+ high = walkexpr(high, init)
+ max = walkexpr(max, init)
+ n.SetSliceBounds(low, high, max)
+ if n.Op.IsSlice3() {
+ if max != nil && max.Op == OCAP && samesafeexpr(n.Left, max.Left) {
+ // Reduce x[i:j:cap(x)] to x[i:j].
+ if n.Op == OSLICE3 {
+ n.Op = OSLICE
+ } else {
+ n.Op = OSLICEARR
+ }
+ n = reduceSlice(n)
}
+ } else {
n = reduceSlice(n)
}
@@ -1415,17 +1418,18 @@ opswitch:
a := Nod(OAS, var_, nil) // zero temp
a = typecheck(a, Etop)
init.Append(a)
- r := Nod(OSLICE, var_, Nod(OKEY, nil, l)) // arr[:l]
- r = conv(r, n.Type) // in case n.Type is named.
+ r := Nod(OSLICE, var_, nil) // arr[:l]
+ r.SetSliceBounds(nil, l, nil)
+ r = conv(r, n.Type) // in case n.Type is named.
r = typecheck(r, Erv)
r = walkexpr(r, init)
n = r
} else {
- // makeslice(t *Type, nel int64, max int64) (ary []any)
+ // makeslice(et *Type, nel int64, max int64) (ary []any)
fn := syslook("makeslice")
fn = substArgTypes(fn, t.Elem()) // any-1
- n = mkcall1(fn, n.Type, init, typename(n.Type), conv(l, Types[TINT64]), conv(r, Types[TINT64]))
+ n = mkcall1(fn, n.Type, init, typename(t.Elem()), conv(l, Types[TINT64]), conv(r, Types[TINT64]))
}
case ORUNESTR:
@@ -1501,7 +1505,7 @@ opswitch:
// ifaceeq(i1 any-1, i2 any-2) (ret bool);
case OCMPIFACE:
if !Eqtype(n.Left.Type, n.Right.Type) {
- Fatalf("ifaceeq %v %v %v", Oconv(n.Op, 0), n.Left.Type, n.Right.Type)
+ Fatalf("ifaceeq %v %v %v", n.Op, n.Left.Type, n.Right.Type)
}
var fn *Node
if n.Left.Type.IsEmptyInterface() {
@@ -1532,6 +1536,19 @@ opswitch:
n = r
case OARRAYLIT, OMAPLIT, OSTRUCTLIT, OPTRLIT:
+ if isStaticCompositeLiteral(n) {
+ // n can be directly represented in the read-only data section.
+ // Make direct reference to the static data. See issue 12841.
+ vstat := staticname(n.Type, 0)
+ if n.Op == OSTRUCTLIT {
+ structlit(0, 1, n, vstat, init)
+ } else {
+ arraylit(0, 1, n, vstat, init)
+ }
+ n = vstat
+ n = typecheck(n, Erv)
+ break
+ }
var_ := temp(n.Type)
anylit(0, n, var_, init)
n = var_
@@ -1573,13 +1590,15 @@ opswitch:
return n
}
+// TODO(josharian): combine this with its caller and simplify
func reduceSlice(n *Node) *Node {
- r := n.Right.Right
- if r != nil && r.Op == OLEN && samesafeexpr(n.Left, r.Left) {
+ low, high, max := n.SliceBounds()
+ if high != nil && high.Op == OLEN && samesafeexpr(n.Left, high.Left) {
// Reduce x[i:len(x)] to x[i:].
- n.Right.Right = nil
+ high = nil
}
- if (n.Op == OSLICE || n.Op == OSLICESTR) && n.Right.Left == nil && n.Right.Right == nil {
+ n.SetSliceBounds(low, high, max)
+ if (n.Op == OSLICE || n.Op == OSLICESTR) && low == nil && high == nil {
// Reduce x[:] to x.
if Debug_slice > 0 {
Warn("slice: omit slice operation")
@@ -1632,7 +1651,7 @@ func ascompatee(op Op, nl, nr []*Node, init *Nodes) []*Node {
var nln, nrn Nodes
nln.Set(nl)
nrn.Set(nr)
- Yyerror("error in shape across %v %v %v / %d %d [%s]", Hconv(nln, FmtSign), Oconv(op, 0), Hconv(nrn, FmtSign), len(nl), len(nr), Curfn.Func.Nname.Sym.Name)
+ Yyerror("error in shape across %v %v %v / %d %d [%s]", Hconv(nln, FmtSign), op, Hconv(nrn, FmtSign), len(nl), len(nr), Curfn.Func.Nname.Sym.Name)
}
return nn
}
@@ -1716,6 +1735,7 @@ func mkdotargslice(lr0, nn []*Node, l *Field, fp int, init *Nodes, ddd *Node) []
}
tslice := typSlice(l.Type.Elem())
+ tslice.Noalg = true
var n *Node
if len(lr0) == 0 {
@@ -1783,7 +1803,7 @@ func ascompatte(op Op, call *Node, isddd bool, nl *Type, lr []*Node, fp int, ini
var nn []*Node
// f(g()) where g has multiple return values
- if r != nil && len(lr) <= 1 && r.Type.IsStruct() && r.Type.Funarg {
+ if r != nil && len(lr) <= 1 && r.Type.IsFuncArgStruct() {
// optimization - can do block copy
if eqtypenoname(r.Type, nl) {
arg := nodarg(nl, fp)
@@ -1846,9 +1866,9 @@ func ascompatte(op Op, call *Node, isddd bool, nl *Type, lr []*Node, fp int, ini
l1 := dumptypes(nl, "expected")
l2 := dumpnodetypes(lr0, "given")
if l != nil {
- Yyerror("not enough arguments to %v\n\t%s\n\t%s", Oconv(op, 0), l1, l2)
+ Yyerror("not enough arguments to %v\n\t%s\n\t%s", op, l1, l2)
} else {
- Yyerror("too many arguments to %v\n\t%s\n\t%s", Oconv(op, 0), l1, l2)
+ Yyerror("too many arguments to %v\n\t%s\n\t%s", op, l1, l2)
}
}
@@ -1938,7 +1958,7 @@ func walkprint(nn *Node, init *Nodes) *Node {
on = 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" {
+ if (t.Sym.Pkg == Runtimepkg || compiling_runtime) && t.Sym.Name == "hex" {
on = syslook("printhex")
} else {
on = syslook("printuint")
@@ -1991,7 +2011,9 @@ func callnew(t *Type) *Node {
dowidth(t)
fn := syslook("newobject")
fn = substArgTypes(fn, t)
- return mkcall1(fn, Ptrto(t), nil, typename(t))
+ v := mkcall1(fn, Ptrto(t), nil, typename(t))
+ v.NonNil = true
+ return v
}
func iscallret(n *Node) bool {
@@ -2041,7 +2063,7 @@ func isglobal(n *Node) bool {
// Do we need a write barrier for the assignment l = r?
func needwritebarrier(l *Node, r *Node) bool {
- if use_writebarrier == 0 {
+ if !use_writebarrier {
return false
}
@@ -2120,7 +2142,7 @@ func applywritebarrier(n *Node) *Node {
func convas(n *Node, init *Nodes) *Node {
if n.Op != OAS {
- Fatalf("convas: not OAS %v", Oconv(n.Op, 0))
+ Fatalf("convas: not OAS %v", n.Op)
}
n.Typecheck = 1
@@ -2263,7 +2285,7 @@ func reorder3(all []*Node) []*Node {
switch l.Op {
default:
- Fatalf("reorder3 unexpected lvalue %v", Oconv(l.Op, FmtSharp))
+ Fatalf("reorder3 unexpected lvalue %v", oconv(l.Op, FmtSharp))
case ONAME:
break
@@ -2550,7 +2572,7 @@ func paramstoheap(params *Type, out bool) []*Node {
}
// generate allocation & copying code
- if compiling_runtime != 0 {
+ if compiling_runtime {
Yyerror("%v escapes to heap, not allowed in runtime.", v)
}
if prealloc[v] == nil {
@@ -2723,8 +2745,7 @@ func addstr(n *Node, init *Nodes) *Node {
prealloc[slice] = prealloc[n]
}
slice.List.Set(args[1:]) // skip buf arg
- args = []*Node{buf}
- args = append(args, slice)
+ args = []*Node{buf, slice}
slice.Esc = EscNone
}
@@ -2787,18 +2808,19 @@ func appendslice(n *Node, init *Nodes) *Node {
fn = substArgTypes(fn, s.Type.Elem(), s.Type.Elem())
// s = growslice(T, s, n)
- nif.Nbody.Set1(Nod(OAS, s, mkcall1(fn, s.Type, &nif.Ninit, typename(s.Type), s, nn)))
+ nif.Nbody.Set1(Nod(OAS, s, mkcall1(fn, s.Type, &nif.Ninit, typename(s.Type.Elem()), s, nn)))
l = append(l, nif)
// s = s[:n]
- nt := Nod(OSLICE, s, Nod(OKEY, nil, nn))
+ nt := Nod(OSLICE, s, nil)
+ nt.SetSliceBounds(nil, nn, nil)
nt.Etype = 1
l = append(l, Nod(OAS, s, nt))
if haspointers(l1.Type.Elem()) {
// copy(s[len(l1):], l2)
- nptr1 := Nod(OSLICE, s, Nod(OKEY, Nod(OLEN, l1, nil), nil))
-
+ nptr1 := Nod(OSLICE, s, nil)
+ nptr1.SetSliceBounds(Nod(OLEN, l1, nil), nil, nil)
nptr1.Etype = 1
nptr2 := l2
fn := syslook("typedslicecopy")
@@ -2810,8 +2832,8 @@ func appendslice(n *Node, init *Nodes) *Node {
} else if instrumenting {
// rely on runtime to instrument copy.
// copy(s[len(l1):], l2)
- nptr1 := Nod(OSLICE, s, Nod(OKEY, Nod(OLEN, l1, nil), nil))
-
+ nptr1 := Nod(OSLICE, s, nil)
+ nptr1.SetSliceBounds(Nod(OLEN, l1, nil), nil, nil)
nptr1.Etype = 1
nptr2 := l2
var fn *Node
@@ -2917,7 +2939,7 @@ func walkappend(n *Node, init *Nodes, dst *Node) *Node {
fn = substArgTypes(fn, ns.Type.Elem(), ns.Type.Elem())
nx.Nbody.Set1(Nod(OAS, ns,
- mkcall1(fn, ns.Type, &nx.Ninit, typename(ns.Type), ns,
+ mkcall1(fn, ns.Type, &nx.Ninit, typename(ns.Type.Elem()), ns,
Nod(OADD, Nod(OLEN, ns, nil), na))))
l = append(l, nx)
@@ -2925,7 +2947,8 @@ func walkappend(n *Node, init *Nodes, dst *Node) *Node {
nn := temp(Types[TINT])
l = append(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 = Nod(OSLICE, ns, nil) // ...s[:n+argc]
+ nx.SetSliceBounds(nil, Nod(OADD, nn, na), nil)
nx.Etype = 1
l = append(l, Nod(OAS, ns, nx)) // s = s[:n+argc]
@@ -3101,12 +3124,7 @@ func walkcompare(n *Node, init *Nodes) *Node {
default:
return n
- case TARRAY:
- if t.IsSlice() {
- return n
- }
-
- case TSTRUCT:
+ case TARRAY, TSTRUCT:
break
}
@@ -3274,7 +3292,7 @@ func samecheap(a *Node, b *Node) bool {
// The result of walkrotate MUST be assigned back to n, e.g.
// n.Left = walkrotate(n.Left)
func walkrotate(n *Node) *Node {
- if Thearch.Thechar == '0' || Thearch.Thechar == '7' || Thearch.Thechar == '9' {
+ if Thearch.LinkArch.InFamily(sys.MIPS64, sys.ARM64, sys.PPC64) {
return n
}
@@ -3294,6 +3312,11 @@ func walkrotate(n *Node) *Node {
// Constants adding to width?
w := int(l.Type.Width * 8)
+ if Thearch.LinkArch.Family == sys.S390X && w != 32 && w != 64 {
+ // only supports 32-bit and 64-bit rotates
+ return n
+ }
+
if Smallintconst(l.Right) && Smallintconst(r.Right) {
sl := int(l.Right.Int64())
if sl >= 0 {
@@ -3401,7 +3424,7 @@ func walkdiv(n *Node, init *Nodes) *Node {
// if >= 0, nr is 1<<pow // 1 if nr is negative.
// TODO(minux)
- if Thearch.Thechar == '0' || Thearch.Thechar == '7' || Thearch.Thechar == '9' {
+ if Thearch.LinkArch.InFamily(sys.MIPS64, sys.PPC64) {
return n
}
@@ -3462,6 +3485,16 @@ func walkdiv(n *Node, init *Nodes) *Node {
goto ret
}
+ // TODO(zhongwei) Test shows that TUINT8, TINT8, TUINT16 and TINT16's "quick division" method
+ // on current arm64 backend is slower than hardware div instruction on ARM64 due to unnecessary
+ // data movement between registers. It could be enabled when generated code is good enough.
+ if Thearch.LinkArch.Family == sys.ARM64 {
+ switch Simtype[nl.Type.Etype] {
+ case TUINT8, TINT8, TUINT16, TINT16:
+ return n
+ }
+ }
+
switch Simtype[nl.Type.Etype] {
default:
return n
@@ -3765,7 +3798,7 @@ func usefield(n *Node) {
switch n.Op {
default:
- Fatalf("usefield %v", Oconv(n.Op, 0))
+ Fatalf("usefield %v", n.Op)
case ODOT, ODOTPTR:
break
@@ -3784,7 +3817,7 @@ func usefield(n *Node) {
if field == nil {
Fatalf("usefield %v %v without paramfld", n.Left.Type, n.Sym)
}
- if field.Note == nil || !strings.Contains(*field.Note, "go:\"track\"") {
+ if !strings.Contains(field.Note, "go:\"track\"") {
return
}
diff --git a/src/cmd/compile/internal/mips64/galign.go b/src/cmd/compile/internal/mips64/galign.go
index 9d582f4b51..22890c891b 100644
--- a/src/cmd/compile/internal/mips64/galign.go
+++ b/src/cmd/compile/internal/mips64/galign.go
@@ -11,18 +11,12 @@ import (
)
func betypeinit() {
- gc.Widthptr = 8
- gc.Widthint = 8
- gc.Widthreg = 8
}
func Main() {
- gc.Thearch.Thechar = '0'
- gc.Thearch.Thestring = "mips64"
- gc.Thearch.Thelinkarch = &mips.Linkmips64
+ gc.Thearch.LinkArch = &mips.Linkmips64
if obj.Getgoarch() == "mips64le" {
- gc.Thearch.Thestring = "mips64le"
- gc.Thearch.Thelinkarch = &mips.Linkmips64le
+ gc.Thearch.LinkArch = &mips.Linkmips64le
}
gc.Thearch.REGSP = mips.REGSP
gc.Thearch.REGCTXT = mips.REGCTXT
diff --git a/src/cmd/compile/internal/mips64/gsubr.go b/src/cmd/compile/internal/mips64/gsubr.go
index 60805270af..a2bff29ecc 100644
--- a/src/cmd/compile/internal/mips64/gsubr.go
+++ b/src/cmd/compile/internal/mips64/gsubr.go
@@ -148,7 +148,7 @@ func ginscmp(op gc.Op, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog {
case gc.TFLOAT32:
switch op {
default:
- gc.Fatalf("ginscmp: no entry for op=%v type=%v", gc.Oconv(op, 0), t)
+ gc.Fatalf("ginscmp: no entry for op=%s type=%v", op, t)
case gc.OEQ,
gc.ONE:
@@ -165,7 +165,7 @@ func ginscmp(op gc.Op, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog {
case gc.TFLOAT64:
switch op {
default:
- gc.Fatalf("ginscmp: no entry for op=%v type=%v", gc.Oconv(op, 0), t)
+ gc.Fatalf("ginscmp: no entry for op=%s type=%v", op, t)
case gc.OEQ,
gc.ONE:
@@ -715,7 +715,7 @@ func optoas(op gc.Op, t *gc.Type) obj.As {
a := obj.AXXX
switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) {
default:
- gc.Fatalf("optoas: no entry for op=%v type=%v", gc.Oconv(op, 0), t)
+ gc.Fatalf("optoas: no entry for op=%s type=%v", op, t)
case OEQ_ | gc.TBOOL,
OEQ_ | gc.TINT8,
diff --git a/src/cmd/compile/internal/ppc64/galign.go b/src/cmd/compile/internal/ppc64/galign.go
index 91bece6080..a83dff9a8b 100644
--- a/src/cmd/compile/internal/ppc64/galign.go
+++ b/src/cmd/compile/internal/ppc64/galign.go
@@ -11,23 +11,16 @@ import (
)
func betypeinit() {
- gc.Widthptr = 8
- gc.Widthint = 8
- gc.Widthreg = 8
-
- if gc.Ctxt.Flag_shared != 0 {
+ if gc.Ctxt.Flag_shared {
gc.Thearch.ReservedRegs = append(gc.Thearch.ReservedRegs, ppc64.REG_R2)
gc.Thearch.ReservedRegs = append(gc.Thearch.ReservedRegs, ppc64.REG_R12)
}
}
func Main() {
- gc.Thearch.Thechar = '9'
- gc.Thearch.Thestring = "ppc64"
- gc.Thearch.Thelinkarch = &ppc64.Linkppc64
+ gc.Thearch.LinkArch = &ppc64.Linkppc64
if obj.Getgoarch() == "ppc64le" {
- gc.Thearch.Thestring = "ppc64le"
- gc.Thearch.Thelinkarch = &ppc64.Linkppc64le
+ gc.Thearch.LinkArch = &ppc64.Linkppc64le
}
gc.Thearch.REGSP = ppc64.REGSP
gc.Thearch.REGCTXT = ppc64.REGCTXT
diff --git a/src/cmd/compile/internal/ppc64/gsubr.go b/src/cmd/compile/internal/ppc64/gsubr.go
index de6e2fbe05..1137c50678 100644
--- a/src/cmd/compile/internal/ppc64/gsubr.go
+++ b/src/cmd/compile/internal/ppc64/gsubr.go
@@ -580,7 +580,7 @@ func rawgins(as obj.As, f *gc.Node, t *gc.Node) *obj.Prog {
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.
- if gc.Ctxt.Flag_shared != 0 {
+ if gc.Ctxt.Flag_shared {
// Make sure function pointer is in R12 as well when
// compiling Go into PIC.
// TODO(mwhudson): it would obviously be better to
@@ -602,7 +602,7 @@ func rawgins(as obj.As, f *gc.Node, t *gc.Node) *obj.Prog {
p.To.Type = obj.TYPE_REG
p.To.Reg = ppc64.REG_CTR
- if gc.Ctxt.Flag_shared != 0 {
+ if gc.Ctxt.Flag_shared {
// When compiling Go into PIC, the function we just
// called via pointer might have been implemented in
// a separate module and so overwritten the TOC
@@ -712,7 +712,7 @@ func optoas(op gc.Op, t *gc.Type) obj.As {
a := obj.AXXX
switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) {
default:
- gc.Fatalf("optoas: no entry for op=%v type=%v", gc.Oconv(op, 0), t)
+ gc.Fatalf("optoas: no entry for op=%v type=%v", op, t)
case OEQ_ | gc.TBOOL,
OEQ_ | gc.TINT8,
diff --git a/src/cmd/compile/internal/ppc64/reg.go b/src/cmd/compile/internal/ppc64/reg.go
index 447679e207..558ba4a4f4 100644
--- a/src/cmd/compile/internal/ppc64/reg.go
+++ b/src/cmd/compile/internal/ppc64/reg.go
@@ -113,7 +113,7 @@ func excludedregs() uint64 {
// Exclude registers with fixed functions
regbits := 1<<0 | RtoB(ppc64.REGSP) | RtoB(ppc64.REGG) | RtoB(ppc64.REGTLS) | RtoB(ppc64.REGTMP)
- if gc.Ctxt.Flag_shared != 0 {
+ if gc.Ctxt.Flag_shared {
// When compiling Go into PIC, R2 is reserved to be the TOC pointer
// and R12 so that calls via function pointer can stomp on it.
regbits |= RtoB(ppc64.REG_R2)
diff --git a/src/cmd/compile/internal/s390x/cgen.go b/src/cmd/compile/internal/s390x/cgen.go
new file mode 100644
index 0000000000..28bb34e0ef
--- /dev/null
+++ b/src/cmd/compile/internal/s390x/cgen.go
@@ -0,0 +1,178 @@
+// Copyright 2016 The Go 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 s390x
+
+import (
+ "cmd/compile/internal/gc"
+ "cmd/internal/obj"
+ "cmd/internal/obj/s390x"
+)
+
+type direction int
+
+const (
+ _FORWARDS direction = iota
+ _BACKWARDS
+)
+
+// blockcopy copies w bytes from &n to &res
+func blockcopy(n, res *gc.Node, osrc, odst, w int64) {
+ 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(s390x.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)
+ }
+ defer gc.Regfree(&src)
+ defer gc.Regfree(&dst)
+
+ var tmp gc.Node
+ gc.Regalloc(&tmp, gc.Types[gc.Tptr], nil)
+ defer gc.Regfree(&tmp)
+
+ offset := int64(0)
+ dir := _FORWARDS
+ if osrc < odst && odst < osrc+w {
+ // Reverse. Can't use MVC, fall back onto basic moves.
+ dir = _BACKWARDS
+ const copiesPerIter = 2
+ if w >= 8*copiesPerIter {
+ cnt := w - (w % (8 * copiesPerIter))
+ ginscon(s390x.AADD, w, &src)
+ ginscon(s390x.AADD, w, &dst)
+
+ var end gc.Node
+ gc.Regalloc(&end, gc.Types[gc.Tptr], nil)
+ p := gins(s390x.ASUB, nil, &end)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = cnt
+ p.Reg = src.Reg
+
+ var label *obj.Prog
+ for i := 0; i < copiesPerIter; i++ {
+ offset := int64(-8 * (i + 1))
+ p := gins(s390x.AMOVD, &src, &tmp)
+ p.From.Type = obj.TYPE_MEM
+ p.From.Offset = offset
+ if i == 0 {
+ label = p
+ }
+ p = gins(s390x.AMOVD, &tmp, &dst)
+ p.To.Type = obj.TYPE_MEM
+ p.To.Offset = offset
+ }
+
+ ginscon(s390x.ASUB, 8*copiesPerIter, &src)
+ ginscon(s390x.ASUB, 8*copiesPerIter, &dst)
+ gins(s390x.ACMP, &src, &end)
+ gc.Patch(gc.Gbranch(s390x.ABNE, nil, 0), label)
+ gc.Regfree(&end)
+
+ w -= cnt
+ } else {
+ offset = w
+ }
+ }
+
+ if dir == _FORWARDS && w > 1024 {
+ // Loop over MVCs
+ cnt := w - (w % 256)
+
+ var end gc.Node
+ gc.Regalloc(&end, gc.Types[gc.Tptr], nil)
+ add := gins(s390x.AADD, nil, &end)
+ add.From.Type = obj.TYPE_CONST
+ add.From.Offset = cnt
+ add.Reg = src.Reg
+
+ mvc := gins(s390x.AMVC, &src, &dst)
+ mvc.From.Type = obj.TYPE_MEM
+ mvc.From.Offset = 0
+ mvc.To.Type = obj.TYPE_MEM
+ mvc.To.Offset = 0
+ mvc.From3 = new(obj.Addr)
+ mvc.From3.Type = obj.TYPE_CONST
+ mvc.From3.Offset = 256
+
+ ginscon(s390x.AADD, 256, &src)
+ ginscon(s390x.AADD, 256, &dst)
+ gins(s390x.ACMP, &src, &end)
+ gc.Patch(gc.Gbranch(s390x.ABNE, nil, 0), mvc)
+ gc.Regfree(&end)
+
+ w -= cnt
+ }
+
+ for w > 0 {
+ cnt := w
+ // If in reverse we can only do 8, 4, 2 or 1 bytes at a time.
+ if dir == _BACKWARDS {
+ switch {
+ case cnt >= 8:
+ cnt = 8
+ case cnt >= 4:
+ cnt = 4
+ case cnt >= 2:
+ cnt = 2
+ }
+ } else if cnt > 256 {
+ cnt = 256
+ }
+
+ switch cnt {
+ case 8, 4, 2, 1:
+ op := s390x.AMOVB
+ switch cnt {
+ case 8:
+ op = s390x.AMOVD
+ case 4:
+ op = s390x.AMOVW
+ case 2:
+ op = s390x.AMOVH
+ }
+ load := gins(op, &src, &tmp)
+ load.From.Type = obj.TYPE_MEM
+ load.From.Offset = offset
+
+ store := gins(op, &tmp, &dst)
+ store.To.Type = obj.TYPE_MEM
+ store.To.Offset = offset
+
+ if dir == _BACKWARDS {
+ load.From.Offset -= cnt
+ store.To.Offset -= cnt
+ }
+
+ default:
+ p := gins(s390x.AMVC, &src, &dst)
+ p.From.Type = obj.TYPE_MEM
+ p.From.Offset = offset
+ p.To.Type = obj.TYPE_MEM
+ p.To.Offset = offset
+ p.From3 = new(obj.Addr)
+ p.From3.Type = obj.TYPE_CONST
+ p.From3.Offset = cnt
+ }
+
+ switch dir {
+ case _FORWARDS:
+ offset += cnt
+ case _BACKWARDS:
+ offset -= cnt
+ }
+ w -= cnt
+ }
+}
diff --git a/src/cmd/compile/internal/s390x/galign.go b/src/cmd/compile/internal/s390x/galign.go
new file mode 100644
index 0000000000..d0d621e557
--- /dev/null
+++ b/src/cmd/compile/internal/s390x/galign.go
@@ -0,0 +1,66 @@
+// Copyright 2016 The Go 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 s390x
+
+import (
+ "cmd/compile/internal/gc"
+ "cmd/internal/obj/s390x"
+)
+
+func betypeinit() {
+ gc.Widthptr = 8
+ gc.Widthint = 8
+ gc.Widthreg = 8
+}
+
+func Main() {
+ gc.Thearch.LinkArch = &s390x.Links390x
+ gc.Thearch.REGSP = s390x.REGSP
+ gc.Thearch.REGCTXT = s390x.REGCTXT
+ gc.Thearch.REGCALLX = s390x.REG_R3
+ gc.Thearch.REGCALLX2 = s390x.REG_R4
+ gc.Thearch.REGRETURN = s390x.REG_R3
+ gc.Thearch.REGMIN = s390x.REG_R0
+ gc.Thearch.REGMAX = s390x.REG_R15
+ gc.Thearch.FREGMIN = s390x.REG_F0
+ gc.Thearch.FREGMAX = s390x.REG_F15
+ gc.Thearch.MAXWIDTH = 1 << 50
+ 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.Peep = peep
+ gc.Thearch.Proginfo = proginfo
+ gc.Thearch.Regtyp = isReg
+ 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/s390x/ggen.go b/src/cmd/compile/internal/s390x/ggen.go
new file mode 100644
index 0000000000..39885baace
--- /dev/null
+++ b/src/cmd/compile/internal/s390x/ggen.go
@@ -0,0 +1,577 @@
+// Copyright 2016 The Go 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 s390x
+
+import (
+ "cmd/compile/internal/gc"
+ "cmd/internal/obj"
+ "cmd/internal/obj/s390x"
+ "fmt"
+)
+
+// clearLoopCutOff is the (somewhat arbitrary) value above which it is better
+// to have a loop of clear instructions (e.g. XCs) rather than just generating
+// multiple instructions (i.e. loop unrolling).
+// Must be between 256 and 4096.
+const clearLoopCutoff = 1024
+
+func defframe(ptxt *obj.Prog) {
+ // fill in argument size, stack size
+ ptxt.To.Type = obj.TYPE_TEXTSIZE
+
+ ptxt.To.Val = int32(gc.Rnd(gc.Curfn.Type.ArgWidth(), 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 _, n := range gc.Curfn.Func.Dcl {
+ if !n.Name.Needzero {
+ continue
+ }
+ if n.Class != gc.PAUTO {
+ gc.Fatalf("needzero class %d", n.Class)
+ }
+ if n.Type.Width%int64(gc.Widthptr) != 0 || n.Xoffset%int64(gc.Widthptr) != 0 || n.Type.Width == 0 {
+ gc.Fatalf("var %v has size %d offset %d", gc.Nconv(n, gc.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)
+}
+
+// zerorange clears the stack in the given range.
+func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog {
+ cnt := hi - lo
+ if cnt == 0 {
+ return p
+ }
+
+ // Adjust the frame to account for LR.
+ frame += gc.Ctxt.FixedFrameSize()
+ offset := frame + lo
+ reg := int16(s390x.REGSP)
+
+ // If the offset cannot fit in a 12-bit unsigned displacement then we
+ // need to create a copy of the stack pointer that we can adjust.
+ // We also need to do this if we are going to loop.
+ if offset < 0 || offset > 4096-clearLoopCutoff || cnt > clearLoopCutoff {
+ p = appendpp(p, s390x.AADD, obj.TYPE_CONST, 0, offset, obj.TYPE_REG, s390x.REGRT1, 0)
+ p.Reg = int16(s390x.REGSP)
+ reg = s390x.REGRT1
+ offset = 0
+ }
+
+ // Generate a loop of large clears.
+ if cnt > clearLoopCutoff {
+ n := cnt - (cnt % 256)
+ end := int16(s390x.REGRT2)
+ p = appendpp(p, s390x.AADD, obj.TYPE_CONST, 0, offset+n, obj.TYPE_REG, end, 0)
+ p.Reg = reg
+ p = appendpp(p, s390x.AXC, obj.TYPE_MEM, reg, offset, obj.TYPE_MEM, reg, offset)
+ p.From3 = new(obj.Addr)
+ p.From3.Type = obj.TYPE_CONST
+ p.From3.Offset = 256
+ pl := p
+ p = appendpp(p, s390x.AADD, obj.TYPE_CONST, 0, 256, obj.TYPE_REG, reg, 0)
+ p = appendpp(p, s390x.ACMP, obj.TYPE_REG, reg, 0, obj.TYPE_REG, end, 0)
+ p = appendpp(p, s390x.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0)
+ gc.Patch(p, pl)
+
+ cnt -= n
+ }
+
+ // Generate remaining clear instructions without a loop.
+ for cnt > 0 {
+ n := cnt
+
+ // Can clear at most 256 bytes per instruction.
+ if n > 256 {
+ n = 256
+ }
+
+ switch n {
+ // Handle very small clears with move instructions.
+ case 8, 4, 2, 1:
+ ins := s390x.AMOVB
+ switch n {
+ case 8:
+ ins = s390x.AMOVD
+ case 4:
+ ins = s390x.AMOVW
+ case 2:
+ ins = s390x.AMOVH
+ }
+ p = appendpp(p, ins, obj.TYPE_CONST, 0, 0, obj.TYPE_MEM, reg, offset)
+
+ // Handle clears that would require multiple move instructions with XC.
+ default:
+ p = appendpp(p, s390x.AXC, obj.TYPE_MEM, reg, offset, obj.TYPE_MEM, reg, offset)
+ p.From3 = new(obj.Addr)
+ p.From3.Type = obj.TYPE_CONST
+ p.From3.Offset = n
+ }
+
+ cnt -= n
+ offset += n
+ }
+
+ return p
+}
+
+func appendpp(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int16, foffset int64, ttype obj.AddrType, treg int16, toffset int64) *obj.Prog {
+ q := gc.Ctxt.NewProg()
+ gc.Clearp(q)
+ q.As = as
+ q.Lineno = p.Lineno
+ q.From.Type = ftype
+ q.From.Reg = freg
+ q.From.Offset = foffset
+ q.To.Type = ttype
+ q.To.Reg = treg
+ q.To.Offset = toffset
+ q.Link = p.Link
+ p.Link = q
+ return q
+}
+
+func ginsnop() {
+ var reg gc.Node
+ gc.Nodreg(&reg, gc.Types[gc.TINT], s390x.REG_R0)
+ gins(s390x.AOR, &reg, &reg)
+}
+
+var panicdiv *gc.Node
+
+/*
+ * generate division.
+ * generates one of:
+ * res = nl / nr
+ * res = nl % nr
+ * according to op.
+ */
+func dodiv(op gc.Op, 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 t.IsSigned() {
+ check = 1
+ if gc.Isconst(nl, gc.CTINT) && nl.Int64() != -(1<<uint64(t.Width*8-1)) {
+ check = 0
+ } else if gc.Isconst(nr, gc.CTINT) && nr.Int64() != -1 {
+ check = 0
+ }
+ }
+
+ if t.Width < 8 {
+ if t.IsSigned() {
+ t = gc.Types[gc.TINT64]
+ } else {
+ t = gc.Types[gc.TUINT64]
+ }
+ check = 0
+ }
+
+ a := optoas(gc.ODIV, t)
+
+ var tl gc.Node
+ gc.Regalloc(&tl, t0, nil)
+ var tr gc.Node
+ gc.Regalloc(&tr, t0, nil)
+ if nl.Ullman >= 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 = s390x.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 {
+ nl, nr = nr, nl
+ }
+
+ t := nl.Type
+ w := 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 := gins(s390x.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 := gins(s390x.ASRD, nil, &n1)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = int64(w)
+
+ case gc.TINT64:
+ gins(s390x.AMULHD, &n2, &n1)
+
+ case gc.TUINT64:
+ gins(s390x.AMULHDU, &n2, &n1)
+
+ default:
+ gc.Fatalf("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 gc.Op, 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.Int64())
+ 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 := gc.Gbranch(optoas(gc.OLT, tcount), nil, 1)
+ if op == gc.ORSH && nl.Type.IsSigned() {
+ 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)
+}
+
+// clearfat clears (i.e. replaces with zeros) the value pointed to by nl.
+func clearfat(nl *gc.Node) {
+ if gc.Debug['g'] != 0 {
+ fmt.Printf("clearfat %v (%v, size: %d)\n", nl, nl.Type, nl.Type.Width)
+ }
+
+ // Avoid taking the address for simple enough types.
+ if gc.Componentgen(nil, nl) {
+ return
+ }
+
+ var dst gc.Node
+ gc.Regalloc(&dst, gc.Types[gc.Tptr], nil)
+ gc.Agen(nl, &dst)
+
+ var boff int64
+ w := nl.Type.Width
+ if w > clearLoopCutoff {
+ // Generate a loop clearing 256 bytes per iteration using XCs.
+ var end gc.Node
+ gc.Regalloc(&end, gc.Types[gc.Tptr], nil)
+ p := gins(s390x.AMOVD, &dst, &end)
+ p.From.Type = obj.TYPE_ADDR
+ p.From.Offset = w - (w % 256)
+
+ p = gins(s390x.AXC, &dst, &dst)
+ p.From.Type = obj.TYPE_MEM
+ p.From.Offset = 0
+ p.To.Type = obj.TYPE_MEM
+ p.To.Offset = 0
+ p.From3 = new(obj.Addr)
+ p.From3.Offset = 256
+ p.From3.Type = obj.TYPE_CONST
+ pl := p
+
+ ginscon(s390x.AADD, 256, &dst)
+ gins(s390x.ACMP, &dst, &end)
+ gc.Patch(gc.Gbranch(s390x.ABNE, nil, 0), pl)
+ gc.Regfree(&end)
+ w = w % 256
+ }
+
+ // Generate instructions to clear the remaining memory.
+ for w > 0 {
+ n := w
+
+ // Can clear at most 256 bytes per instruction.
+ if n > 256 {
+ n = 256
+ }
+
+ switch n {
+ // Handle very small clears using moves.
+ case 8, 4, 2, 1:
+ ins := s390x.AMOVB
+ switch n {
+ case 8:
+ ins = s390x.AMOVD
+ case 4:
+ ins = s390x.AMOVW
+ case 2:
+ ins = s390x.AMOVH
+ }
+ p := gins(ins, nil, &dst)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = 0
+ p.To.Type = obj.TYPE_MEM
+ p.To.Offset = boff
+
+ // Handle clears that would require multiple moves with a XC.
+ default:
+ p := gins(s390x.AXC, &dst, &dst)
+ p.From.Type = obj.TYPE_MEM
+ p.From.Offset = boff
+ p.To.Type = obj.TYPE_MEM
+ p.To.Offset = boff
+ p.From3 = new(obj.Addr)
+ p.From3.Offset = n
+ p.From3.Type = obj.TYPE_CONST
+ }
+
+ boff += n
+ w -= n
+ }
+
+ gc.Regfree(&dst)
+}
+
+// Called after regopt and peep have run.
+// Expand CHECKNIL pseudo-op into actual nil pointer check.
+func expandchecks(firstp *obj.Prog) {
+ for p := 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(p.Lineno, "generated nil check")
+ }
+ if p.From.Type != obj.TYPE_REG {
+ gc.Fatalf("invalid nil check %v\n", p)
+ }
+
+ // check is
+ // CMPBNE arg, $0, 2(PC) [likely]
+ // MOVD R0, 0(R0)
+ p1 := gc.Ctxt.NewProg()
+
+ gc.Clearp(p1)
+ p1.Link = p.Link
+ p.Link = p1
+ p1.Lineno = p.Lineno
+ p1.Pc = 9999
+ p.As = s390x.ACMPBNE
+ p.From3 = new(obj.Addr)
+ p.From3.Type = obj.TYPE_CONST
+ p.From3.Offset = 0
+
+ p.To.Type = obj.TYPE_BRANCH
+ p.To.Val = p1.Link
+
+ // crash by write to memory address 0.
+ p1.As = s390x.AMOVD
+
+ p1.From.Type = obj.TYPE_REG
+ p1.From.Reg = s390x.REGZERO
+ p1.To.Type = obj.TYPE_MEM
+ p1.To.Reg = s390x.REGZERO
+ p1.To.Offset = 0
+ }
+}
+
+// res = runtime.getg()
+func getg(res *gc.Node) {
+ var n1 gc.Node
+ gc.Nodreg(&n1, res.Type, s390x.REGG)
+ gmove(&n1, res)
+}
diff --git a/src/cmd/compile/internal/s390x/gsubr.go b/src/cmd/compile/internal/s390x/gsubr.go
new file mode 100644
index 0000000000..3e8782f5e6
--- /dev/null
+++ b/src/cmd/compile/internal/s390x/gsubr.go
@@ -0,0 +1,1115 @@
+// 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 s390x
+
+import (
+ "cmd/compile/internal/gc"
+ "cmd/internal/obj"
+ "cmd/internal/obj/s390x"
+ "fmt"
+)
+
+var resvd = []int{
+ s390x.REGZERO, // R0
+ s390x.REGTMP, // R10
+ s390x.REGTMP2, // R11
+ s390x.REGCTXT, // R12
+ s390x.REGG, // R13
+ s390x.REG_LR, // R14
+ s390x.REGSP, // R15
+}
+
+// generate
+// as $c, n
+func ginscon(as obj.As, c int64, n2 *gc.Node) {
+ var n1 gc.Node
+
+ gc.Nodconst(&n1, gc.Types[gc.TINT64], c)
+
+ if as != s390x.AMOVD && (c < -s390x.BIG || c > s390x.BIG) || n2.Op != gc.OREGISTER || as == s390x.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(s390x.AMOVD, &n1, &ntmp)
+ rawgins(as, &ntmp, n2)
+ gc.Regfree(&ntmp)
+ return
+ }
+
+ rawgins(as, &n1, n2)
+}
+
+// generate
+// as n, $c (CMP/CMPU)
+func ginscon2(as obj.As, n2 *gc.Node, c int64) {
+ var n1 gc.Node
+
+ gc.Nodconst(&n1, gc.Types[gc.TINT64], c)
+
+ switch as {
+ default:
+ gc.Fatalf("ginscon2")
+
+ case s390x.ACMP:
+ if -s390x.BIG <= c && c <= s390x.BIG {
+ rawgins(as, n2, &n1)
+ return
+ }
+
+ case s390x.ACMPU:
+ if 0 <= c && c <= 2*s390x.BIG {
+ rawgins(as, n2, &n1)
+ return
+ }
+ }
+
+ // MOV n1 into register first
+ var ntmp gc.Node
+ gc.Regalloc(&ntmp, gc.Types[gc.TINT64], nil)
+
+ rawgins(s390x.AMOVD, &n1, &ntmp)
+ rawgins(as, n2, &ntmp)
+ gc.Regfree(&ntmp)
+}
+
+func ginscmp(op gc.Op, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog {
+ if t.IsInteger() && 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 t.IsInteger() && gc.Isconst(n2, gc.CTINT) {
+ ginscon2(optoas(gc.OCMP, t), &r1, n2.Int64())
+ } 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)
+}
+
+// gmvc tries to move f to t using a mvc instruction.
+// If successful it returns true, otherwise it returns false.
+func gmvc(f, t *gc.Node) bool {
+ ft := int(gc.Simsimtype(f.Type))
+ tt := int(gc.Simsimtype(t.Type))
+
+ if ft != tt {
+ return false
+ }
+
+ if f.Op != gc.OINDREG || t.Op != gc.OINDREG {
+ return false
+ }
+
+ if f.Xoffset < 0 || f.Xoffset >= 4096-8 {
+ return false
+ }
+
+ if t.Xoffset < 0 || t.Xoffset >= 4096-8 {
+ return false
+ }
+
+ var len int64
+ switch ft {
+ case gc.TUINT8, gc.TINT8, gc.TBOOL:
+ len = 1
+ case gc.TUINT16, gc.TINT16:
+ len = 2
+ case gc.TUINT32, gc.TINT32, gc.TFLOAT32:
+ len = 4
+ case gc.TUINT64, gc.TINT64, gc.TFLOAT64, gc.TPTR64:
+ len = 8
+ case gc.TUNSAFEPTR:
+ len = int64(gc.Widthptr)
+ default:
+ return false
+ }
+
+ p := gc.Prog(s390x.AMVC)
+ gc.Naddr(&p.From, f)
+ gc.Naddr(&p.To, t)
+ p.From3 = new(obj.Addr)
+ p.From3.Offset = len
+ p.From3.Type = obj.TYPE_CONST
+ return true
+}
+
+// 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, gc.FmtLong), gc.Nconv(t, gc.FmtLong))
+ }
+
+ ft := int(gc.Simsimtype(f.Type))
+ tt := int(gc.Simsimtype(t.Type))
+ cvt := t.Type
+
+ if gc.Iscomplex[ft] || gc.Iscomplex[tt] {
+ gc.Complexmove(f, t)
+ return
+ }
+
+ var a obj.As
+
+ // cannot have two memory operands
+ if gc.Ismem(f) && gc.Ismem(t) {
+ if gmvc(f, t) {
+ return
+ }
+ 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 t.Type.IsFloat() {
+ goto hard
+ }
+
+ // all immediates are 16-bit sign-extended
+ // unless moving into a register.
+ if t.Type.IsInteger() {
+ if i := con.Int64(); int64(int16(i)) != i {
+ goto hard
+ }
+ }
+
+ // immediate moves to memory have a 12-bit unsigned displacement
+ if t.Xoffset < 0 || t.Xoffset >= 4096-8 {
+ goto hard
+ }
+ }
+ }
+
+ // a float-to-int or int-to-float conversion requires the source operand in a register
+ if gc.Ismem(f) && ((f.Type.IsFloat() && t.Type.IsInteger()) || (f.Type.IsInteger() && t.Type.IsFloat())) {
+ cvt = f.Type
+ goto hard
+ }
+
+ // a float32-to-float64 or float64-to-float32 conversion requires the source operand in a register
+ if gc.Ismem(f) && f.Type.IsFloat() && t.Type.IsFloat() && (ft != tt) {
+ cvt = f.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:
+ gc.Fatalf("gmove %v -> %v", gc.Tconv(f.Type, gc.FmtLong), gc.Tconv(t.Type, gc.FmtLong))
+
+ // integer copy and truncate
+ case gc.TINT8<<16 | gc.TINT8,
+ gc.TUINT8<<16 | gc.TINT8,
+ gc.TINT16<<16 | gc.TINT8,
+ 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 = s390x.AMOVB
+
+ case gc.TINT8<<16 | gc.TUINT8,
+ gc.TUINT8<<16 | gc.TUINT8,
+ 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 = s390x.AMOVBZ
+
+ case gc.TINT16<<16 | gc.TINT16,
+ gc.TUINT16<<16 | gc.TINT16,
+ gc.TINT32<<16 | gc.TINT16,
+ gc.TUINT32<<16 | gc.TINT16,
+ gc.TINT64<<16 | gc.TINT16,
+ gc.TUINT64<<16 | gc.TINT16:
+ a = s390x.AMOVH
+
+ case gc.TINT16<<16 | gc.TUINT16,
+ gc.TUINT16<<16 | gc.TUINT16,
+ gc.TINT32<<16 | gc.TUINT16,
+ gc.TUINT32<<16 | gc.TUINT16,
+ gc.TINT64<<16 | gc.TUINT16,
+ gc.TUINT64<<16 | gc.TUINT16:
+ a = s390x.AMOVHZ
+
+ case gc.TINT32<<16 | gc.TINT32,
+ gc.TUINT32<<16 | gc.TINT32,
+ gc.TINT64<<16 | gc.TINT32,
+ gc.TUINT64<<16 | gc.TINT32:
+ a = s390x.AMOVW
+
+ case gc.TINT32<<16 | gc.TUINT32,
+ gc.TUINT32<<16 | gc.TUINT32,
+ gc.TINT64<<16 | gc.TUINT32,
+ gc.TUINT64<<16 | gc.TUINT32:
+ a = s390x.AMOVWZ
+
+ case gc.TINT64<<16 | gc.TINT64,
+ gc.TINT64<<16 | gc.TUINT64,
+ gc.TUINT64<<16 | gc.TINT64,
+ gc.TUINT64<<16 | gc.TUINT64:
+ a = s390x.AMOVD
+
+ // sign extend int8
+ case gc.TINT8<<16 | gc.TINT16,
+ 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 = s390x.AMOVB
+ goto rdst
+
+ // sign extend uint8
+ case gc.TUINT8<<16 | gc.TINT16,
+ 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 = s390x.AMOVBZ
+ goto rdst
+
+ // sign extend int16
+ case gc.TINT16<<16 | gc.TINT32,
+ gc.TINT16<<16 | gc.TUINT32,
+ gc.TINT16<<16 | gc.TINT64,
+ gc.TINT16<<16 | gc.TUINT64:
+ a = s390x.AMOVH
+ goto rdst
+
+ // zero extend uint16
+ case gc.TUINT16<<16 | gc.TINT32,
+ gc.TUINT16<<16 | gc.TUINT32,
+ gc.TUINT16<<16 | gc.TINT64,
+ gc.TUINT16<<16 | gc.TUINT64:
+ a = s390x.AMOVHZ
+ goto rdst
+
+ // sign extend int32
+ case gc.TINT32<<16 | gc.TINT64,
+ gc.TINT32<<16 | gc.TUINT64:
+ a = s390x.AMOVW
+ goto rdst
+
+ // zero extend uint32
+ case gc.TUINT32<<16 | gc.TINT64,
+ gc.TUINT32<<16 | gc.TUINT64:
+ a = s390x.AMOVWZ
+ goto rdst
+
+ // float to integer
+ case gc.TFLOAT32<<16 | gc.TUINT8,
+ gc.TFLOAT32<<16 | gc.TUINT16:
+ cvt = gc.Types[gc.TUINT32]
+ goto hard
+
+ case gc.TFLOAT32<<16 | gc.TUINT32:
+ a = s390x.ACLFEBR
+ goto rdst
+
+ case gc.TFLOAT32<<16 | gc.TUINT64:
+ a = s390x.ACLGEBR
+ goto rdst
+
+ case gc.TFLOAT64<<16 | gc.TUINT8,
+ gc.TFLOAT64<<16 | gc.TUINT16:
+ cvt = gc.Types[gc.TUINT32]
+ goto hard
+
+ case gc.TFLOAT64<<16 | gc.TUINT32:
+ a = s390x.ACLFDBR
+ goto rdst
+
+ case gc.TFLOAT64<<16 | gc.TUINT64:
+ a = s390x.ACLGDBR
+ goto rdst
+
+ case gc.TFLOAT32<<16 | gc.TINT8,
+ gc.TFLOAT32<<16 | gc.TINT16:
+ cvt = gc.Types[gc.TINT32]
+ goto hard
+
+ case gc.TFLOAT32<<16 | gc.TINT32:
+ a = s390x.ACFEBRA
+ goto rdst
+
+ case gc.TFLOAT32<<16 | gc.TINT64:
+ a = s390x.ACGEBRA
+ goto rdst
+
+ case gc.TFLOAT64<<16 | gc.TINT8,
+ gc.TFLOAT64<<16 | gc.TINT16:
+ cvt = gc.Types[gc.TINT32]
+ goto hard
+
+ case gc.TFLOAT64<<16 | gc.TINT32:
+ a = s390x.ACFDBRA
+ goto rdst
+
+ case gc.TFLOAT64<<16 | gc.TINT64:
+ a = s390x.ACGDBRA
+ goto rdst
+
+ // integer to float
+ case gc.TUINT8<<16 | gc.TFLOAT32,
+ gc.TUINT16<<16 | gc.TFLOAT32:
+ cvt = gc.Types[gc.TUINT32]
+ goto hard
+
+ case gc.TUINT32<<16 | gc.TFLOAT32:
+ a = s390x.ACELFBR
+ goto rdst
+
+ case gc.TUINT64<<16 | gc.TFLOAT32:
+ a = s390x.ACELGBR
+ goto rdst
+
+ case gc.TUINT8<<16 | gc.TFLOAT64,
+ gc.TUINT16<<16 | gc.TFLOAT64:
+ cvt = gc.Types[gc.TUINT32]
+ goto hard
+
+ case gc.TUINT32<<16 | gc.TFLOAT64:
+ a = s390x.ACDLFBR
+ goto rdst
+
+ case gc.TUINT64<<16 | gc.TFLOAT64:
+ a = s390x.ACDLGBR
+ goto rdst
+
+ case gc.TINT8<<16 | gc.TFLOAT32,
+ gc.TINT16<<16 | gc.TFLOAT32:
+ cvt = gc.Types[gc.TINT32]
+ goto hard
+
+ case gc.TINT32<<16 | gc.TFLOAT32:
+ a = s390x.ACEFBRA
+ goto rdst
+
+ case gc.TINT64<<16 | gc.TFLOAT32:
+ a = s390x.ACEGBRA
+ goto rdst
+
+ case gc.TINT8<<16 | gc.TFLOAT64,
+ gc.TINT16<<16 | gc.TFLOAT64:
+ cvt = gc.Types[gc.TINT32]
+ goto hard
+
+ case gc.TINT32<<16 | gc.TFLOAT64:
+ a = s390x.ACDFBRA
+ goto rdst
+
+ case gc.TINT64<<16 | gc.TFLOAT64:
+ a = s390x.ACDGBRA
+ goto rdst
+
+ // float to float
+ case gc.TFLOAT32<<16 | gc.TFLOAT32:
+ a = s390x.AFMOVS
+
+ case gc.TFLOAT64<<16 | gc.TFLOAT64:
+ a = s390x.AFMOVD
+
+ case gc.TFLOAT32<<16 | gc.TFLOAT64:
+ a = s390x.ALDEBR
+ goto rdst
+
+ case gc.TFLOAT64<<16 | gc.TFLOAT32:
+ a = s390x.ALEDBR
+ goto rdst
+ }
+
+ gins(a, f, t)
+ return
+
+ // requires register destination
+rdst:
+ if t != nil && t.Op == gc.OREGISTER {
+ gins(a, f, t)
+ return
+ } else {
+ 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 intLiteral(n *gc.Node) (x int64, ok bool) {
+ switch {
+ case n == nil:
+ return
+ case gc.Isconst(n, gc.CTINT):
+ return n.Int64(), 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 obj.As, f, t *gc.Node) *obj.Prog {
+ if t != nil {
+ if as >= obj.A_ARCHSPECIFIC {
+ if x, ok := intLiteral(f); ok {
+ ginscon(as, x, t)
+ return nil // caller must not use
+ }
+ }
+ if as == s390x.ACMP || as == s390x.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 obj.As, f *gc.Node, t *gc.Node) *obj.Prog {
+ // self move check
+ // TODO(mundaym): use sized math and extend to MOVB, MOVWZ etc.
+ switch as {
+ case s390x.AMOVD, s390x.AFMOVS, s390x.AFMOVD:
+ if f != nil && t != nil &&
+ f.Op == gc.OREGISTER && t.Op == gc.OREGISTER &&
+ f.Reg == t.Reg {
+ return nil
+ }
+ }
+
+ p := gc.Prog(as)
+ gc.Naddr(&p.From, f)
+ gc.Naddr(&p.To, t)
+
+ switch as {
+ // Bad things the front end has done to us. Crash to find call stack.
+ case s390x.AMULLD:
+ if p.From.Type == obj.TYPE_CONST {
+ gc.Debug['h'] = 1
+ gc.Fatalf("bad inst: %v", p)
+ }
+ case s390x.ACMP, s390x.ACMPU:
+ if p.From.Type == obj.TYPE_MEM || p.To.Type == obj.TYPE_MEM {
+ gc.Debug['h'] = 1
+ gc.Fatalf("bad inst: %v", p)
+ }
+ }
+
+ if gc.Debug['g'] != 0 {
+ fmt.Printf("%v\n", p)
+ }
+
+ w := int32(0)
+ switch as {
+ case s390x.AMOVB, s390x.AMOVBZ:
+ w = 1
+
+ case s390x.AMOVH, s390x.AMOVHZ:
+ w = 2
+
+ case s390x.AMOVW, s390x.AMOVWZ:
+ w = 4
+
+ case s390x.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.Fatalf("bad width: %v (%d, %d)\n", p, p.From.Width, p.To.Width)
+ }
+
+ return p
+}
+
+// optoas returns the Axxx equivalent of Oxxx for type t
+func optoas(op gc.Op, t *gc.Type) obj.As {
+ if t == nil {
+ gc.Fatalf("optoas: t is nil")
+ }
+
+ // avoid constant conversions in switches below
+ const (
+ OMINUS_ = uint32(gc.OMINUS) << 16
+ OLSH_ = uint32(gc.OLSH) << 16
+ ORSH_ = uint32(gc.ORSH) << 16
+ OADD_ = uint32(gc.OADD) << 16
+ OSUB_ = uint32(gc.OSUB) << 16
+ OMUL_ = uint32(gc.OMUL) << 16
+ ODIV_ = uint32(gc.ODIV) << 16
+ OOR_ = uint32(gc.OOR) << 16
+ OAND_ = uint32(gc.OAND) << 16
+ OXOR_ = uint32(gc.OXOR) << 16
+ OEQ_ = uint32(gc.OEQ) << 16
+ ONE_ = uint32(gc.ONE) << 16
+ OLT_ = uint32(gc.OLT) << 16
+ OLE_ = uint32(gc.OLE) << 16
+ OGE_ = uint32(gc.OGE) << 16
+ OGT_ = uint32(gc.OGT) << 16
+ OCMP_ = uint32(gc.OCMP) << 16
+ OAS_ = uint32(gc.OAS) << 16
+ OHMUL_ = uint32(gc.OHMUL) << 16
+ OSQRT_ = uint32(gc.OSQRT) << 16
+ OLROT_ = uint32(gc.OLROT) << 16
+ )
+
+ a := obj.AXXX
+ switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) {
+ default:
+ gc.Fatalf("optoas: no entry for op=%v type=%v", op, t)
+
+ case OEQ_ | gc.TBOOL,
+ OEQ_ | gc.TINT8,
+ OEQ_ | gc.TUINT8,
+ OEQ_ | gc.TINT16,
+ OEQ_ | gc.TUINT16,
+ OEQ_ | gc.TINT32,
+ OEQ_ | gc.TUINT32,
+ OEQ_ | gc.TINT64,
+ OEQ_ | gc.TUINT64,
+ OEQ_ | gc.TPTR32,
+ OEQ_ | gc.TPTR64,
+ OEQ_ | gc.TFLOAT32,
+ OEQ_ | gc.TFLOAT64:
+ a = s390x.ABEQ
+
+ case ONE_ | gc.TBOOL,
+ ONE_ | gc.TINT8,
+ ONE_ | gc.TUINT8,
+ ONE_ | gc.TINT16,
+ ONE_ | gc.TUINT16,
+ ONE_ | gc.TINT32,
+ ONE_ | gc.TUINT32,
+ ONE_ | gc.TINT64,
+ ONE_ | gc.TUINT64,
+ ONE_ | gc.TPTR32,
+ ONE_ | gc.TPTR64,
+ ONE_ | gc.TFLOAT32,
+ ONE_ | gc.TFLOAT64:
+ a = s390x.ABNE
+
+ case OLT_ | gc.TINT8, // ACMP
+ OLT_ | gc.TINT16,
+ OLT_ | gc.TINT32,
+ OLT_ | gc.TINT64,
+ OLT_ | gc.TUINT8,
+ // ACMPU
+ OLT_ | gc.TUINT16,
+ OLT_ | gc.TUINT32,
+ OLT_ | gc.TUINT64,
+ OLT_ | gc.TFLOAT32,
+ // AFCMPU
+ OLT_ | gc.TFLOAT64:
+ a = s390x.ABLT
+
+ case OLE_ | gc.TINT8, // ACMP
+ OLE_ | gc.TINT16,
+ OLE_ | gc.TINT32,
+ OLE_ | gc.TINT64,
+ OLE_ | gc.TUINT8,
+ // ACMPU
+ OLE_ | gc.TUINT16,
+ OLE_ | gc.TUINT32,
+ OLE_ | gc.TUINT64,
+ OLE_ | gc.TFLOAT32,
+ OLE_ | gc.TFLOAT64:
+ a = s390x.ABLE
+
+ case OGT_ | gc.TINT8,
+ OGT_ | gc.TINT16,
+ OGT_ | gc.TINT32,
+ OGT_ | gc.TINT64,
+ OGT_ | gc.TUINT8,
+ OGT_ | gc.TUINT16,
+ OGT_ | gc.TUINT32,
+ OGT_ | gc.TUINT64,
+ OGT_ | gc.TFLOAT32,
+ OGT_ | gc.TFLOAT64:
+ a = s390x.ABGT
+
+ case OGE_ | gc.TINT8,
+ OGE_ | gc.TINT16,
+ OGE_ | gc.TINT32,
+ OGE_ | gc.TINT64,
+ OGE_ | gc.TUINT8,
+ OGE_ | gc.TUINT16,
+ OGE_ | gc.TUINT32,
+ OGE_ | gc.TUINT64,
+ OGE_ | gc.TFLOAT32,
+ OGE_ | gc.TFLOAT64:
+ a = s390x.ABGE
+
+ case OCMP_ | gc.TBOOL,
+ OCMP_ | gc.TINT8,
+ OCMP_ | gc.TINT16,
+ OCMP_ | gc.TINT32,
+ OCMP_ | gc.TPTR32,
+ OCMP_ | gc.TINT64:
+ a = s390x.ACMP
+
+ case OCMP_ | gc.TUINT8,
+ OCMP_ | gc.TUINT16,
+ OCMP_ | gc.TUINT32,
+ OCMP_ | gc.TUINT64,
+ OCMP_ | gc.TPTR64:
+ a = s390x.ACMPU
+
+ case OCMP_ | gc.TFLOAT32:
+ a = s390x.ACEBR
+
+ case OCMP_ | gc.TFLOAT64:
+ a = s390x.AFCMPU
+
+ case OAS_ | gc.TBOOL,
+ OAS_ | gc.TINT8:
+ a = s390x.AMOVB
+
+ case OAS_ | gc.TUINT8:
+ a = s390x.AMOVBZ
+
+ case OAS_ | gc.TINT16:
+ a = s390x.AMOVH
+
+ case OAS_ | gc.TUINT16:
+ a = s390x.AMOVHZ
+
+ case OAS_ | gc.TINT32:
+ a = s390x.AMOVW
+
+ case OAS_ | gc.TUINT32,
+ OAS_ | gc.TPTR32:
+ a = s390x.AMOVWZ
+
+ case OAS_ | gc.TINT64,
+ OAS_ | gc.TUINT64,
+ OAS_ | gc.TPTR64:
+ a = s390x.AMOVD
+
+ case OAS_ | gc.TFLOAT32:
+ a = s390x.AFMOVS
+
+ case OAS_ | gc.TFLOAT64:
+ a = s390x.AFMOVD
+
+ case OADD_ | gc.TINT8,
+ OADD_ | gc.TUINT8,
+ OADD_ | gc.TINT16,
+ OADD_ | gc.TUINT16,
+ OADD_ | gc.TINT32,
+ OADD_ | gc.TUINT32,
+ OADD_ | gc.TPTR32,
+ OADD_ | gc.TINT64,
+ OADD_ | gc.TUINT64,
+ OADD_ | gc.TPTR64:
+ a = s390x.AADD
+
+ case OADD_ | gc.TFLOAT32:
+ a = s390x.AFADDS
+
+ case OADD_ | gc.TFLOAT64:
+ a = s390x.AFADD
+
+ case OSUB_ | gc.TINT8,
+ OSUB_ | gc.TUINT8,
+ OSUB_ | gc.TINT16,
+ OSUB_ | gc.TUINT16,
+ OSUB_ | gc.TINT32,
+ OSUB_ | gc.TUINT32,
+ OSUB_ | gc.TPTR32,
+ OSUB_ | gc.TINT64,
+ OSUB_ | gc.TUINT64,
+ OSUB_ | gc.TPTR64:
+ a = s390x.ASUB
+
+ case OSUB_ | gc.TFLOAT32:
+ a = s390x.AFSUBS
+
+ case OSUB_ | gc.TFLOAT64:
+ a = s390x.AFSUB
+
+ case OMINUS_ | gc.TINT8,
+ OMINUS_ | gc.TUINT8,
+ OMINUS_ | gc.TINT16,
+ OMINUS_ | gc.TUINT16,
+ OMINUS_ | gc.TINT32,
+ OMINUS_ | gc.TUINT32,
+ OMINUS_ | gc.TPTR32,
+ OMINUS_ | gc.TINT64,
+ OMINUS_ | gc.TUINT64,
+ OMINUS_ | gc.TPTR64:
+ a = s390x.ANEG
+
+ case OAND_ | gc.TINT8,
+ OAND_ | gc.TUINT8,
+ OAND_ | gc.TINT16,
+ OAND_ | gc.TUINT16,
+ OAND_ | gc.TINT32,
+ OAND_ | gc.TUINT32,
+ OAND_ | gc.TPTR32,
+ OAND_ | gc.TINT64,
+ OAND_ | gc.TUINT64,
+ OAND_ | gc.TPTR64:
+ a = s390x.AAND
+
+ case OOR_ | gc.TINT8,
+ OOR_ | gc.TUINT8,
+ OOR_ | gc.TINT16,
+ OOR_ | gc.TUINT16,
+ OOR_ | gc.TINT32,
+ OOR_ | gc.TUINT32,
+ OOR_ | gc.TPTR32,
+ OOR_ | gc.TINT64,
+ OOR_ | gc.TUINT64,
+ OOR_ | gc.TPTR64:
+ a = s390x.AOR
+
+ case OXOR_ | gc.TINT8,
+ OXOR_ | gc.TUINT8,
+ OXOR_ | gc.TINT16,
+ OXOR_ | gc.TUINT16,
+ OXOR_ | gc.TINT32,
+ OXOR_ | gc.TUINT32,
+ OXOR_ | gc.TPTR32,
+ OXOR_ | gc.TINT64,
+ OXOR_ | gc.TUINT64,
+ OXOR_ | gc.TPTR64:
+ a = s390x.AXOR
+
+ case OLSH_ | gc.TINT8,
+ OLSH_ | gc.TUINT8,
+ OLSH_ | gc.TINT16,
+ OLSH_ | gc.TUINT16,
+ OLSH_ | gc.TINT32,
+ OLSH_ | gc.TUINT32,
+ OLSH_ | gc.TPTR32,
+ OLSH_ | gc.TINT64,
+ OLSH_ | gc.TUINT64,
+ OLSH_ | gc.TPTR64:
+ a = s390x.ASLD
+
+ case ORSH_ | gc.TUINT8,
+ ORSH_ | gc.TUINT16,
+ ORSH_ | gc.TUINT32,
+ ORSH_ | gc.TPTR32,
+ ORSH_ | gc.TUINT64,
+ ORSH_ | gc.TPTR64:
+ a = s390x.ASRD
+
+ case ORSH_ | gc.TINT8,
+ ORSH_ | gc.TINT16,
+ ORSH_ | gc.TINT32,
+ ORSH_ | gc.TINT64:
+ a = s390x.ASRAD
+
+ case OHMUL_ | gc.TINT64:
+ a = s390x.AMULHD
+
+ case OHMUL_ | gc.TUINT64,
+ OHMUL_ | gc.TPTR64:
+ a = s390x.AMULHDU
+
+ case OMUL_ | gc.TINT8,
+ OMUL_ | gc.TINT16,
+ OMUL_ | gc.TINT32,
+ OMUL_ | gc.TINT64:
+ a = s390x.AMULLD
+
+ case OMUL_ | gc.TUINT8,
+ OMUL_ | gc.TUINT16,
+ OMUL_ | gc.TUINT32,
+ OMUL_ | gc.TPTR32,
+ // don't use word multiply, the high 32-bit are undefined.
+ OMUL_ | gc.TUINT64,
+ OMUL_ | gc.TPTR64:
+ // for 64-bit multiplies, signedness doesn't matter.
+ a = s390x.AMULLD
+
+ case OMUL_ | gc.TFLOAT32:
+ a = s390x.AFMULS
+
+ case OMUL_ | gc.TFLOAT64:
+ a = s390x.AFMUL
+
+ case ODIV_ | gc.TINT8,
+ ODIV_ | gc.TINT16,
+ ODIV_ | gc.TINT32,
+ ODIV_ | gc.TINT64:
+ a = s390x.ADIVD
+
+ case ODIV_ | gc.TUINT8,
+ ODIV_ | gc.TUINT16,
+ ODIV_ | gc.TUINT32,
+ ODIV_ | gc.TPTR32,
+ ODIV_ | gc.TUINT64,
+ ODIV_ | gc.TPTR64:
+ a = s390x.ADIVDU
+
+ case ODIV_ | gc.TFLOAT32:
+ a = s390x.AFDIVS
+
+ case ODIV_ | gc.TFLOAT64:
+ a = s390x.AFDIV
+
+ case OSQRT_ | gc.TFLOAT64:
+ a = s390x.AFSQRT
+
+ case OLROT_ | gc.TUINT32,
+ OLROT_ | gc.TPTR32,
+ OLROT_ | gc.TINT32:
+ a = s390x.ARLL
+
+ case OLROT_ | gc.TUINT64,
+ OLROT_ | gc.TPTR64,
+ OLROT_ | gc.TINT64:
+ a = s390x.ARLLG
+ }
+
+ return a
+}
+
+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 obj.As, 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) {
+ return false
+ }
+ v := n.Int64()
+ switch as {
+ default:
+ return false
+
+ // operations that can cope with a 32-bit immediate
+ // TODO(mundaym): logical operations can work on high bits
+ case s390x.AADD,
+ s390x.AADDC,
+ s390x.ASUB,
+ s390x.AMULLW,
+ s390x.AAND,
+ s390x.AOR,
+ s390x.AXOR,
+ s390x.ASLD,
+ s390x.ASLW,
+ s390x.ASRAW,
+ s390x.ASRAD,
+ s390x.ASRW,
+ s390x.ASRD,
+ s390x.AMOVB,
+ s390x.AMOVBZ,
+ s390x.AMOVH,
+ s390x.AMOVHZ,
+ s390x.AMOVW,
+ s390x.AMOVWZ,
+ s390x.AMOVD:
+ if int64(int32(v)) != v {
+ return false
+ }
+
+ // for comparisons avoid immediates unless they can
+ // fit into a int8/uint8
+ // this favours combined compare and branch instructions
+ case s390x.ACMP:
+ if int64(int8(v)) != v {
+ return false
+ }
+ case s390x.ACMPU:
+ if int64(uint8(v)) != v {
+ return false
+ }
+ }
+
+ 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]
+ // check that the offset fits into a 12-bit displacement
+ if n1.Xoffset < 0 || n1.Xoffset >= (1<<12)-8 {
+ sudoclean()
+ return false
+ }
+ 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.Fatalf("can't happen")
+ }
+ gins(s390x.AMOVD, &n1, reg)
+ gc.Cgen_checknil(reg)
+ n1.Xoffset = -(oary[i] + 1)
+ }
+
+ a.Type = obj.TYPE_NONE
+ a.Index = 0
+ // check that the offset fits into a 12-bit displacement
+ if n1.Xoffset < 0 || n1.Xoffset >= (1<<12)-8 {
+ tmp := n1
+ tmp.Op = gc.OREGISTER
+ tmp.Type = gc.Types[gc.Tptr]
+ tmp.Xoffset = 0
+ gc.Cgen_checknil(&tmp)
+ ginscon(s390x.AADD, n1.Xoffset, &tmp)
+ n1.Xoffset = 0
+ }
+ gc.Naddr(a, &n1)
+ return true
+ }
+
+ return false
+}
diff --git a/src/cmd/compile/internal/s390x/peep.go b/src/cmd/compile/internal/s390x/peep.go
new file mode 100644
index 0000000000..cd6a8c5d8c
--- /dev/null
+++ b/src/cmd/compile/internal/s390x/peep.go
@@ -0,0 +1,1664 @@
+// 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 s390x
+
+import (
+ "cmd/compile/internal/gc"
+ "cmd/internal/obj"
+ "cmd/internal/obj/s390x"
+ "fmt"
+)
+
+type usage int
+
+const (
+ _None usage = iota // no usage found
+ _Read // only read from
+ _ReadWriteSame // both read from and written to in a single operand
+ _Write // only written to
+ _ReadWriteDiff // both read from and written to in different operands
+)
+
+var gactive uint32
+
+func peep(firstp *obj.Prog) {
+ g := gc.Flowstart(firstp, nil)
+ if g == nil {
+ return
+ }
+ gactive = 0
+
+ run := func(name string, pass func(r *gc.Flow) int) int {
+ n := pass(g.Start)
+ if gc.Debug['P'] != 0 {
+ fmt.Println(name, ":", n)
+ }
+ if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
+ gc.Dumpit(name, g.Start, 0)
+ }
+ return n
+ }
+
+ for {
+ n := 0
+ n += run("constant propagation", constantPropagation)
+ n += run("copy propagation", copyPropagation)
+ n += run("cast propagation", castPropagation)
+ n += run("remove load-hit-stores", removeLoadHitStores)
+ n += run("dead code elimination", deadCodeElimination)
+ if n == 0 {
+ break
+ }
+ }
+ run("fuse op moves", fuseOpMoves)
+ run("fuse clears", fuseClear)
+ run("load pipelining", loadPipelining)
+ run("fuse compare branch", fuseCompareBranch)
+ run("simplify ops", simplifyOps)
+ run("dead code elimination", deadCodeElimination)
+
+ // TODO(mundaym): load/store multiple aren't currently handled by copyu
+ // so this pass must be last.
+ run("fuse multiple", fuseMultiple)
+
+ gc.Flowend(g)
+}
+
+func pushback(r0 *gc.Flow) {
+ var r *gc.Flow
+
+ var b *gc.Flow
+ p0 := 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 !(isReg(&p.From) || isConst(&p.From)) || !isReg(&p.To) {
+ break
+ }
+ if copyu(p, &p0.To, nil) != _None || copyu(p0, &p.To, nil) != _None {
+ 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 := b; ; r = r.Link {
+ fmt.Printf("\t%v\n", r.Prog)
+ if r == r0 {
+ break
+ }
+ }
+ }
+
+ t := *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
+ p0.From3 = p.From3
+ p0.Reg = p.Reg
+ p0.RegTo2 = p.RegTo2
+ if r == b {
+ break
+ }
+ }
+
+ p0 = r.Prog
+ p0.As = t.As
+ p0.Lineno = t.Lineno
+ p0.From = t.From
+ p0.To = t.To
+ p0.From3 = t.From3
+ p0.Reg = t.Reg
+ p0.RegTo2 = t.RegTo2
+
+ if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
+ fmt.Printf("\tafter\n")
+ for r := b; ; r = r.Link {
+ fmt.Printf("\t%v\n", r.Prog)
+ if r == r0 {
+ break
+ }
+ }
+ }
+}
+
+// excise replaces the given instruction with a NOP and clears
+// its operands.
+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++
+}
+
+// isZero returns true if a is either the constant 0 or the register
+// REGZERO.
+func isZero(a *obj.Addr) bool {
+ if a.Type == obj.TYPE_CONST && a.Offset == 0 {
+ return true
+ }
+ if a.Type == obj.TYPE_REG && a.Reg == s390x.REGZERO {
+ return true
+ }
+ return false
+}
+
+// isReg returns true if a is a general purpose or floating point
+// register (GPR or FPR).
+//
+// TODO(mundaym): currently this excludes REGZER0, but not other
+// special registers.
+func isReg(a *obj.Addr) bool {
+ return a.Type == obj.TYPE_REG &&
+ s390x.REG_R0 <= a.Reg &&
+ a.Reg <= s390x.REG_F15 &&
+ a.Reg != s390x.REGZERO
+}
+
+// isGPR returns true if a is a general purpose register (GPR).
+// REGZERO is treated as a GPR.
+func isGPR(a *obj.Addr) bool {
+ return a.Type == obj.TYPE_REG &&
+ s390x.REG_R0 <= a.Reg &&
+ a.Reg <= s390x.REG_R15
+}
+
+// isFPR returns true if a is a floating point register (FPR).
+func isFPR(a *obj.Addr) bool {
+ return a.Type == obj.TYPE_REG &&
+ s390x.REG_F0 <= a.Reg &&
+ a.Reg <= s390x.REG_F15
+}
+
+// isConst returns true if a refers to a constant (integer or
+// floating point, not string currently).
+func isConst(a *obj.Addr) bool {
+ return a.Type == obj.TYPE_CONST || a.Type == obj.TYPE_FCONST
+}
+
+// isBDMem returns true if a refers to a memory location addressable by a
+// base register (B) and a displacement (D), such as:
+// x+8(R1)
+// and
+// 0(R10)
+// It returns false if the address contains an index register (X) such as:
+// 16(R1)(R2*1)
+// or if a relocation is required.
+func isBDMem(a *obj.Addr) bool {
+ return a.Type == obj.TYPE_MEM &&
+ a.Index == 0 &&
+ (a.Name == obj.NAME_NONE || a.Name == obj.NAME_AUTO || a.Name == obj.NAME_PARAM)
+}
+
+// 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. subprop returns true if it modified any instructions.
+func subprop(r0 *gc.Flow) bool {
+ p := r0.Prog
+ v1 := &p.From
+ if !isReg(v1) {
+ return false
+ }
+ v2 := &p.To
+ if !isReg(v2) {
+ return false
+ }
+ cast := false
+ switch p.As {
+ case s390x.AMOVW, s390x.AMOVWZ,
+ s390x.AMOVH, s390x.AMOVHZ,
+ s390x.AMOVB, s390x.AMOVBZ:
+ cast = true
+ }
+ for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) {
+ if gc.Uniqs(r) == nil {
+ break
+ }
+ p = r.Prog
+ switch copyu(p, v1, nil) {
+ case _Write, _ReadWriteDiff:
+ if p.As == obj.ACALL {
+ return false
+ }
+ if (!cast || p.As == r0.Prog.As) && p.To.Type == v1.Type && p.To.Reg == v1.Reg {
+ copysub(&p.To, v1, v2)
+ for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) {
+ p = r.Prog
+ copysub(&p.From, v1, v2)
+ copysub1(p, v1, v2)
+ copysub(&p.To, v1, v2)
+ }
+ v1.Reg, v2.Reg = v2.Reg, v1.Reg
+ return true
+ }
+ if cast {
+ return false
+ }
+ case _ReadWriteSame:
+ if cast {
+ return false
+ }
+ }
+ if copyu(p, v2, nil) != _None {
+ return false
+ }
+ }
+ 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(r *gc.Flow) bool {
+ p := r.Prog
+
+ canSub := false
+ switch p.As {
+ case s390x.AFMOVS, s390x.AFMOVD, s390x.AMOVD:
+ canSub = true
+ default:
+ for rr := gc.Uniqp(r); rr != nil; rr = gc.Uniqp(rr) {
+ if gc.Uniqs(rr) == nil {
+ break
+ }
+ switch copyu(rr.Prog, &p.From, nil) {
+ case _Read, _None:
+ continue
+ }
+ // write
+ if rr.Prog.As == p.As {
+ canSub = true
+ }
+ break
+ }
+ }
+ if !canSub {
+ return false
+ }
+ if copyas(&p.From, &p.To) {
+ return true
+ }
+
+ gactive++
+ return copy1(&p.From, &p.To, r.S1, 0)
+}
+
+// copy1 replaces uses of v2 with v1 starting at r and returns true if
+// all uses were rewritten.
+func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) bool {
+ if uint32(r.Active) == gactive {
+ return true
+ }
+ r.Active = int32(gactive)
+ for ; r != nil; r = r.S1 {
+ p := r.Prog
+ if f == 0 && gc.Uniqp(r) == nil {
+ // Multiple predecessors; conservatively
+ // assume v1 was set on other path
+ f = 1
+ }
+ t := copyu(p, v2, nil)
+ switch t {
+ case _ReadWriteSame:
+ return false
+ case _Write:
+ return true
+ case _Read, _ReadWriteDiff:
+ if f != 0 {
+ return false
+ }
+ if copyu(p, v2, v1) != 0 {
+ return false
+ }
+ if t == _ReadWriteDiff {
+ return true
+ }
+ }
+ if f == 0 {
+ switch copyu(p, v1, nil) {
+ case _ReadWriteSame, _ReadWriteDiff, _Write:
+ f = 1
+ }
+ }
+ 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:
+// _Read if v only used
+// _ReadWriteSame if v is set and used in one address (read-alter-rewrite;
+// can't substitute)
+// _Write if v is only set
+// _ReadWriteDiff if v is set in one address and used in another (so addresses
+// can be rewritten independently)
+// _None otherwise (not touched)
+func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) usage {
+ if p.From3Type() != obj.TYPE_NONE && p.From3Type() != obj.TYPE_CONST {
+ // Currently we never generate a From3 with anything other than a constant in it.
+ 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(p.As))
+ return _ReadWriteSame
+
+ case // read p.From, write p.To
+ s390x.AMOVH,
+ s390x.AMOVHZ,
+ s390x.AMOVB,
+ s390x.AMOVBZ,
+ s390x.AMOVW,
+ s390x.AMOVWZ,
+ s390x.AMOVD,
+ s390x.ANEG,
+ s390x.AADDME,
+ s390x.AADDZE,
+ s390x.ASUBME,
+ s390x.ASUBZE,
+ s390x.AFMOVS,
+ s390x.AFMOVD,
+ s390x.ALEDBR,
+ s390x.AFNEG,
+ s390x.ALDEBR,
+ s390x.ACLFEBR,
+ s390x.ACLGEBR,
+ s390x.ACLFDBR,
+ s390x.ACLGDBR,
+ s390x.ACFEBRA,
+ s390x.ACGEBRA,
+ s390x.ACFDBRA,
+ s390x.ACGDBRA,
+ s390x.ACELFBR,
+ s390x.ACELGBR,
+ s390x.ACDLFBR,
+ s390x.ACDLGBR,
+ s390x.ACEFBRA,
+ s390x.ACEGBRA,
+ s390x.ACDFBRA,
+ s390x.ACDGBRA,
+ s390x.AFSQRT:
+
+ if s != nil {
+ copysub(&p.From, v, s)
+
+ // Update only indirect uses of v in p.To
+ if !copyas(&p.To, v) {
+ copysub(&p.To, v, s)
+ }
+ return _None
+ }
+
+ 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 _ReadWriteDiff
+ }
+ return _Write
+ }
+
+ if copyau(&p.From, v) {
+ return _Read
+ }
+ if copyau(&p.To, v) {
+ // p.To only indirectly uses v
+ return _Read
+ }
+
+ return _None
+
+ // read p.From, read p.Reg, write p.To
+ case s390x.AADD,
+ s390x.AADDC,
+ s390x.AADDE,
+ s390x.ASUB,
+ s390x.ASLW,
+ s390x.ASRW,
+ s390x.ASRAW,
+ s390x.ASLD,
+ s390x.ASRD,
+ s390x.ASRAD,
+ s390x.ARLL,
+ s390x.ARLLG,
+ s390x.AOR,
+ s390x.AORN,
+ s390x.AAND,
+ s390x.AANDN,
+ s390x.ANAND,
+ s390x.ANOR,
+ s390x.AXOR,
+ s390x.AMULLW,
+ s390x.AMULLD,
+ s390x.AMULHD,
+ s390x.AMULHDU,
+ s390x.ADIVW,
+ s390x.ADIVD,
+ s390x.ADIVWU,
+ s390x.ADIVDU,
+ s390x.AFADDS,
+ s390x.AFADD,
+ s390x.AFSUBS,
+ s390x.AFSUB,
+ s390x.AFMULS,
+ s390x.AFMUL,
+ s390x.AFDIVS,
+ s390x.AFDIV:
+ if s != nil {
+ copysub(&p.From, v, s)
+ copysub1(p, v, s)
+
+ // Update only indirect uses of v in p.To
+ if !copyas(&p.To, v) {
+ copysub(&p.To, v, s)
+ }
+ }
+
+ if copyas(&p.To, v) {
+ if p.Reg == 0 {
+ p.Reg = p.To.Reg
+ }
+ if copyau(&p.From, v) || copyau1(p, v) {
+ return _ReadWriteDiff
+ }
+ return _Write
+ }
+
+ if copyau(&p.From, v) {
+ return _Read
+ }
+ if copyau1(p, v) {
+ return _Read
+ }
+ if copyau(&p.To, v) {
+ return _Read
+ }
+ return _None
+
+ case s390x.ABEQ,
+ s390x.ABGT,
+ s390x.ABGE,
+ s390x.ABLT,
+ s390x.ABLE,
+ s390x.ABNE,
+ s390x.ABVC,
+ s390x.ABVS:
+ return _None
+
+ case obj.ACHECKNIL, // read p.From
+ s390x.ACMP, // read p.From, read p.To
+ s390x.ACMPU,
+ s390x.ACMPW,
+ s390x.ACMPWU,
+ s390x.AFCMPO,
+ s390x.AFCMPU,
+ s390x.ACEBR,
+ s390x.AMVC,
+ s390x.ACLC,
+ s390x.AXC,
+ s390x.AOC,
+ s390x.ANC:
+ if s != nil {
+ copysub(&p.From, v, s)
+ copysub(&p.To, v, s)
+ return _None
+ }
+
+ if copyau(&p.From, v) {
+ return _Read
+ }
+ if copyau(&p.To, v) {
+ return _Read
+ }
+ return _None
+
+ case s390x.ACMPBNE, s390x.ACMPBEQ,
+ s390x.ACMPBLT, s390x.ACMPBLE,
+ s390x.ACMPBGT, s390x.ACMPBGE,
+ s390x.ACMPUBNE, s390x.ACMPUBEQ,
+ s390x.ACMPUBLT, s390x.ACMPUBLE,
+ s390x.ACMPUBGT, s390x.ACMPUBGE:
+ if s != nil {
+ copysub(&p.From, v, s)
+ copysub1(p, v, s)
+ return _None
+ }
+ if copyau(&p.From, v) {
+ return _Read
+ }
+ if copyau1(p, v) {
+ return _Read
+ }
+ return _None
+
+ case s390x.ACLEAR:
+ if s != nil {
+ copysub(&p.To, v, s)
+ return _None
+ }
+ if copyau(&p.To, v) {
+ return _Read
+ }
+ return _None
+
+ // go never generates a branch to a GPR
+ // read p.To
+ case s390x.ABR:
+ if s != nil {
+ copysub(&p.To, v, s)
+ return _None
+ }
+
+ if copyau(&p.To, v) {
+ return _Read
+ }
+ return _None
+
+ case obj.ARET, obj.AUNDEF:
+ if s != nil {
+ return _None
+ }
+
+ // All registers die at this point, so claim
+ // everything is set (and not used).
+ return _Write
+
+ case s390x.ABL:
+ if v.Type == obj.TYPE_REG {
+ if s390x.REGARG != -1 && v.Reg == s390x.REGARG {
+ return _ReadWriteSame
+ }
+ if p.From.Type == obj.TYPE_REG && p.From.Reg == v.Reg {
+ return _ReadWriteSame
+ }
+ if v.Reg == s390x.REGZERO {
+ // Deliberately inserted nops set R0.
+ return _ReadWriteSame
+ }
+ if v.Reg == s390x.REGCTXT {
+ // Context register for closures.
+ // TODO(mundaym): not sure if we need to exclude this.
+ return _ReadWriteSame
+ }
+ }
+ if s != nil {
+ copysub(&p.To, v, s)
+ return _None
+ }
+ if copyau(&p.To, v) {
+ return _ReadWriteDiff
+ }
+ return _Write
+
+ case obj.ATEXT:
+ if v.Type == obj.TYPE_REG {
+ if v.Reg == s390x.REGARG {
+ return _Write
+ }
+ }
+ return _None
+
+ case obj.APCDATA,
+ obj.AFUNCDATA,
+ obj.AVARDEF,
+ obj.AVARKILL,
+ obj.AVARLIVE,
+ obj.AUSEFIELD,
+ obj.ANOP:
+ return _None
+ }
+}
+
+// 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 isReg(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 isReg(v) && v.Reg != 0 {
+ if p.Reg == v.Reg {
+ return true
+ }
+ }
+ return false
+}
+
+// copysub replaces v.Reg with s.Reg if a.Reg and v.Reg are direct
+// references to the same register.
+func copysub(a, v, s *obj.Addr) {
+ if copyau(a, v) {
+ a.Reg = s.Reg
+ }
+}
+
+// copysub1 replaces p.Reg with s.Reg if p.Reg and v.Reg are direct
+// references to the same register.
+func copysub1(p *obj.Prog, v, s *obj.Addr) {
+ if copyau1(p, v) {
+ p.Reg = s.Reg
+ }
+}
+
+func sameaddr(a *obj.Addr, v *obj.Addr) bool {
+ if a.Type != v.Type {
+ return false
+ }
+ if isReg(v) && a.Reg == v.Reg {
+ return true
+ }
+ if v.Type == obj.NAME_AUTO || v.Type == obj.NAME_PARAM {
+ // TODO(mundaym): is the offset enough here? Node?
+ 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 {
+ // TODO(mundaym): the name implies this should check
+ // for TYPE_ADDR with a base register REGSP.
+ return a.Type == obj.TYPE_REG && a.Reg == s390x.REGSP
+}
+
+// isMove returns true if p is a move. Moves may imply
+// sign/zero extension.
+func isMove(p *obj.Prog) bool {
+ switch p.As {
+ case s390x.AMOVD,
+ s390x.AMOVW, s390x.AMOVWZ,
+ s390x.AMOVH, s390x.AMOVHZ,
+ s390x.AMOVB, s390x.AMOVBZ,
+ s390x.AFMOVD, s390x.AFMOVS:
+ return true
+ }
+ return false
+}
+
+// isLoad returns true if p is a move from memory to a register.
+func isLoad(p *obj.Prog) bool {
+ if !isMove(p) {
+ return false
+ }
+ if !(isGPR(&p.To) || isFPR(&p.To)) {
+ return false
+ }
+ if p.From.Type != obj.TYPE_MEM {
+ return false
+ }
+ return true
+}
+
+// isStore returns true if p is a move from a register to memory.
+func isStore(p *obj.Prog) bool {
+ if !isMove(p) {
+ return false
+ }
+ if !(isGPR(&p.From) || isFPR(&p.From) || isConst(&p.From)) {
+ return false
+ }
+ if p.To.Type != obj.TYPE_MEM {
+ return false
+ }
+ return true
+}
+
+// sameStackMem returns true if a and b are both memory operands
+// and address the same location which must reside on the stack.
+func sameStackMem(a, b *obj.Addr) bool {
+ if a.Type != obj.TYPE_MEM ||
+ b.Type != obj.TYPE_MEM ||
+ a.Name != b.Name ||
+ a.Sym != b.Sym ||
+ a.Node != b.Node ||
+ a.Reg != b.Reg ||
+ a.Index != b.Index ||
+ a.Offset != b.Offset {
+ return false
+ }
+ switch a.Name {
+ case obj.NAME_NONE:
+ return a.Reg == s390x.REGSP
+ case obj.NAME_PARAM, obj.NAME_AUTO:
+ // params and autos are always on the stack
+ return true
+ }
+ return false
+}
+
+// removeLoadHitStores trys to remove loads that take place
+// immediately after a store to the same location. Returns
+// true if load-hit-stores were removed.
+//
+// For example:
+// MOVD R1, 0(R15)
+// MOVD 0(R15), R2
+// Would become:
+// MOVD R1, 0(R15)
+// MOVD R1, R2
+func removeLoadHitStores(r *gc.Flow) int {
+ n := 0
+ for ; r != nil; r = r.Link {
+ p := r.Prog
+ if !isStore(p) {
+ continue
+ }
+ for rr := gc.Uniqs(r); rr != nil; rr = gc.Uniqs(rr) {
+ pp := rr.Prog
+ if gc.Uniqp(rr) == nil {
+ break
+ }
+ if pp.As == obj.ANOP {
+ continue
+ }
+ if isLoad(pp) && sameStackMem(&p.To, &pp.From) {
+ if size(p.As) >= size(pp.As) && isGPR(&p.From) == isGPR(&pp.To) {
+ pp.From = p.From
+ }
+ }
+ if !isMove(pp) || isStore(pp) {
+ break
+ }
+ if copyau(&p.From, &pp.To) {
+ break
+ }
+ }
+ }
+ return n
+}
+
+// size returns the width of the given move.
+func size(as obj.As) int {
+ switch as {
+ case s390x.AMOVD, s390x.AFMOVD:
+ return 8
+ case s390x.AMOVW, s390x.AMOVWZ, s390x.AFMOVS:
+ return 4
+ case s390x.AMOVH, s390x.AMOVHZ:
+ return 2
+ case s390x.AMOVB, s390x.AMOVBZ:
+ return 1
+ }
+ return -1
+}
+
+// castPropagation tries to eliminate unecessary casts.
+//
+// For example:
+// MOVHZ R1, R2 // uint16
+// MOVB R2, 0(R15) // int8
+// Can be simplified to:
+// MOVB R1, 0(R15)
+func castPropagation(r *gc.Flow) int {
+ n := 0
+ for ; r != nil; r = r.Link {
+ p := r.Prog
+ if !isMove(p) || !isGPR(&p.To) {
+ continue
+ }
+
+ // r is a move with a destination register
+ var move *gc.Flow
+ for rr := gc.Uniqs(r); rr != nil; rr = gc.Uniqs(rr) {
+ if gc.Uniqp(rr) == nil {
+ // branch target: leave alone
+ break
+ }
+ pp := rr.Prog
+ if isMove(pp) && copyas(&pp.From, &p.To) {
+ if pp.To.Type == obj.TYPE_MEM {
+ if p.From.Type == obj.TYPE_MEM ||
+ p.From.Type == obj.TYPE_ADDR {
+ break
+ }
+ if p.From.Type == obj.TYPE_CONST &&
+ int64(int16(p.From.Offset)) != p.From.Offset {
+ break
+ }
+ }
+ move = rr
+ break
+ }
+ if pp.As == obj.ANOP {
+ continue
+ }
+ break
+ }
+ if move == nil {
+ continue
+ }
+
+ // we have a move that reads from our destination reg, check if any future
+ // instructions also read from the reg
+ mp := move.Prog
+ if !copyas(&mp.From, &mp.To) {
+ safe := false
+ for rr := gc.Uniqs(move); rr != nil; rr = gc.Uniqs(rr) {
+ if gc.Uniqp(rr) == nil {
+ break
+ }
+ switch copyu(rr.Prog, &p.To, nil) {
+ case _None:
+ continue
+ case _Write:
+ safe = true
+ }
+ break
+ }
+ if !safe {
+ continue
+ }
+ }
+
+ // at this point we have something like:
+ // MOV* const/mem/reg, reg
+ // MOV* reg, reg/mem
+ // now check if this is a cast that cannot be forward propagated
+ execute := false
+ if p.As == mp.As || isZero(&p.From) || size(p.As) == size(mp.As) {
+ execute = true
+ } else if isGPR(&p.From) && size(p.As) >= size(mp.As) {
+ execute = true
+ }
+
+ if execute {
+ mp.From = p.From
+ excise(r)
+ n++
+ }
+ }
+ return n
+}
+
+// fuseClear merges memory clear operations.
+//
+// Looks for this pattern (sequence of clears):
+// MOVD R0, n(R15)
+// MOVD R0, n+8(R15)
+// MOVD R0, n+16(R15)
+// Replaces with:
+// CLEAR $24, n(R15)
+func fuseClear(r *gc.Flow) int {
+ n := 0
+ var align int64
+ var clear *obj.Prog
+ for ; r != nil; r = r.Link {
+ // If there is a branch into the instruction stream then
+ // we can't fuse into previous instructions.
+ if gc.Uniqp(r) == nil {
+ clear = nil
+ }
+
+ p := r.Prog
+ if p.As == obj.ANOP {
+ continue
+ }
+ if p.As == s390x.AXC {
+ if p.From.Reg == p.To.Reg && p.From.Offset == p.To.Offset {
+ // TODO(mundaym): merge clears?
+ p.As = s390x.ACLEAR
+ p.From.Offset = p.From3.Offset
+ p.From3 = nil
+ p.From.Type = obj.TYPE_CONST
+ p.From.Reg = 0
+ clear = p
+ } else {
+ clear = nil
+ }
+ continue
+ }
+
+ // Is our source a constant zero?
+ if !isZero(&p.From) {
+ clear = nil
+ continue
+ }
+
+ // Are we moving to memory?
+ if p.To.Type != obj.TYPE_MEM ||
+ p.To.Index != 0 ||
+ p.To.Offset >= 4096 ||
+ !(p.To.Name == obj.NAME_NONE || p.To.Name == obj.NAME_AUTO || p.To.Name == obj.NAME_PARAM) {
+ clear = nil
+ continue
+ }
+
+ size := int64(0)
+ switch p.As {
+ default:
+ clear = nil
+ continue
+ case s390x.AMOVB, s390x.AMOVBZ:
+ size = 1
+ case s390x.AMOVH, s390x.AMOVHZ:
+ size = 2
+ case s390x.AMOVW, s390x.AMOVWZ:
+ size = 4
+ case s390x.AMOVD:
+ size = 8
+ }
+
+ // doubleword aligned clears should be kept doubleword
+ // aligned
+ if (size == 8 && align != 8) || (size != 8 && align == 8) {
+ clear = nil
+ }
+
+ if clear != nil &&
+ clear.To.Reg == p.To.Reg &&
+ clear.To.Name == p.To.Name &&
+ clear.To.Node == p.To.Node &&
+ clear.To.Sym == p.To.Sym {
+
+ min := clear.To.Offset
+ max := clear.To.Offset + clear.From.Offset
+
+ // previous clear is already clearing this region
+ if min <= p.To.Offset && max >= p.To.Offset+size {
+ excise(r)
+ n++
+ continue
+ }
+
+ // merge forwards
+ if max == p.To.Offset {
+ clear.From.Offset += size
+ excise(r)
+ n++
+ continue
+ }
+
+ // merge backwards
+ if min-size == p.To.Offset {
+ clear.From.Offset += size
+ clear.To.Offset -= size
+ excise(r)
+ n++
+ continue
+ }
+ }
+
+ // transform into clear
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = size
+ p.From.Reg = 0
+ p.As = s390x.ACLEAR
+ clear = p
+ align = size
+ }
+ return n
+}
+
+// fuseMultiple merges memory loads and stores into load multiple and
+// store multiple operations.
+//
+// Looks for this pattern (sequence of loads or stores):
+// MOVD R1, 0(R15)
+// MOVD R2, 8(R15)
+// MOVD R3, 16(R15)
+// Replaces with:
+// STMG R1, R3, 0(R15)
+func fuseMultiple(r *gc.Flow) int {
+ n := 0
+ var fused *obj.Prog
+ for ; r != nil; r = r.Link {
+ // If there is a branch into the instruction stream then
+ // we can't fuse into previous instructions.
+ if gc.Uniqp(r) == nil {
+ fused = nil
+ }
+
+ p := r.Prog
+
+ isStore := isGPR(&p.From) && isBDMem(&p.To)
+ isLoad := isGPR(&p.To) && isBDMem(&p.From)
+
+ // are we a candidate?
+ size := int64(0)
+ switch p.As {
+ default:
+ fused = nil
+ continue
+ case obj.ANOP:
+ // skip over nops
+ continue
+ case s390x.AMOVW, s390x.AMOVWZ:
+ size = 4
+ // TODO(mundaym): 32-bit load multiple is currently not supported
+ // as it requires sign/zero extension.
+ if !isStore {
+ fused = nil
+ continue
+ }
+ case s390x.AMOVD:
+ size = 8
+ if !isLoad && !isStore {
+ fused = nil
+ continue
+ }
+ }
+
+ // If we merge two loads/stores with different source/destination Nodes
+ // then we will lose a reference the second Node which means that the
+ // compiler might mark the Node as unused and free its slot on the stack.
+ // TODO(mundaym): allow this by adding a dummy reference to the Node.
+ if fused == nil ||
+ fused.From.Node != p.From.Node ||
+ fused.From.Type != p.From.Type ||
+ fused.To.Node != p.To.Node ||
+ fused.To.Type != p.To.Type {
+ fused = p
+ continue
+ }
+
+ // check two addresses
+ ca := func(a, b *obj.Addr, offset int64) bool {
+ return a.Reg == b.Reg && a.Offset+offset == b.Offset &&
+ a.Sym == b.Sym && a.Name == b.Name
+ }
+
+ switch fused.As {
+ default:
+ fused = p
+ case s390x.AMOVW, s390x.AMOVWZ:
+ if size == 4 && fused.From.Reg+1 == p.From.Reg && ca(&fused.To, &p.To, 4) {
+ fused.As = s390x.ASTMY
+ fused.Reg = p.From.Reg
+ excise(r)
+ n++
+ } else {
+ fused = p
+ }
+ case s390x.AMOVD:
+ if size == 8 && fused.From.Reg+1 == p.From.Reg && ca(&fused.To, &p.To, 8) {
+ fused.As = s390x.ASTMG
+ fused.Reg = p.From.Reg
+ excise(r)
+ n++
+ } else if size == 8 && fused.To.Reg+1 == p.To.Reg && ca(&fused.From, &p.From, 8) {
+ fused.As = s390x.ALMG
+ fused.Reg = fused.To.Reg
+ fused.To.Reg = p.To.Reg
+ excise(r)
+ n++
+ } else {
+ fused = p
+ }
+ case s390x.ASTMG, s390x.ASTMY:
+ if (fused.As == s390x.ASTMY && size != 4) ||
+ (fused.As == s390x.ASTMG && size != 8) {
+ fused = p
+ continue
+ }
+ offset := size * int64(fused.Reg-fused.From.Reg+1)
+ if fused.Reg+1 == p.From.Reg && ca(&fused.To, &p.To, offset) {
+ fused.Reg = p.From.Reg
+ excise(r)
+ n++
+ } else {
+ fused = p
+ }
+ case s390x.ALMG:
+ offset := 8 * int64(fused.To.Reg-fused.Reg+1)
+ if size == 8 && fused.To.Reg+1 == p.To.Reg && ca(&fused.From, &p.From, offset) {
+ fused.To.Reg = p.To.Reg
+ excise(r)
+ n++
+ } else {
+ fused = p
+ }
+ }
+ }
+ return n
+}
+
+// simplifyOps looks for side-effect free ops that can be removed or
+// replaced with moves.
+//
+// For example:
+// XOR $0, R1 => NOP
+// ADD $0, R1, R2 => MOVD R1, R2
+func simplifyOps(r *gc.Flow) int {
+ n := 0
+ for ; r != nil; r = r.Link {
+ p := r.Prog
+
+ // if the target is R0 then this is a required NOP
+ if isGPR(&p.To) && p.To.Reg == s390x.REGZERO {
+ continue
+ }
+
+ switch p.As {
+ case s390x.AADD, s390x.ASUB,
+ s390x.AOR, s390x.AXOR,
+ s390x.ASLW, s390x.ASRW, s390x.ASRAW,
+ s390x.ASLD, s390x.ASRD, s390x.ASRAD,
+ s390x.ARLL, s390x.ARLLG:
+ if isZero(&p.From) && isGPR(&p.To) {
+ if p.Reg == 0 || p.Reg == p.To.Reg {
+ excise(r)
+ n++
+ } else {
+ p.As = s390x.AMOVD
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = p.Reg
+ p.Reg = 0
+ }
+ }
+ case s390x.AMULLW, s390x.AAND:
+ if isZero(&p.From) && isGPR(&p.To) {
+ p.As = s390x.AMOVD
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = s390x.REGZERO
+ p.Reg = 0
+ }
+ }
+ }
+ return n
+}
+
+// fuseOpMoves looks for moves following 2-operand operations and trys to merge them into
+// a 3-operand operation.
+//
+// For example:
+// ADD R1, R2
+// MOVD R2, R3
+// might become
+// ADD R1, R2, R3
+func fuseOpMoves(r *gc.Flow) int {
+ n := 0
+ for ; r != nil; r = r.Link {
+ p := r.Prog
+ switch p.As {
+ case s390x.AADD:
+ case s390x.ASUB:
+ if isConst(&p.From) && int64(int16(p.From.Offset)) != p.From.Offset {
+ continue
+ }
+ case s390x.ASLW,
+ s390x.ASRW,
+ s390x.ASRAW,
+ s390x.ASLD,
+ s390x.ASRD,
+ s390x.ASRAD,
+ s390x.ARLL,
+ s390x.ARLLG:
+ // ok - p.From will be a reg or a constant
+ case s390x.AOR,
+ s390x.AORN,
+ s390x.AAND,
+ s390x.AANDN,
+ s390x.ANAND,
+ s390x.ANOR,
+ s390x.AXOR,
+ s390x.AMULLW,
+ s390x.AMULLD:
+ if isConst(&p.From) {
+ // these instructions can either use 3 register form
+ // or have an immediate but not both
+ continue
+ }
+ default:
+ continue
+ }
+
+ if p.Reg != 0 && p.Reg != p.To.Reg {
+ continue
+ }
+
+ var move *gc.Flow
+ rr := gc.Uniqs(r)
+ for {
+ if rr == nil || gc.Uniqp(rr) == nil || rr == r {
+ break
+ }
+ pp := rr.Prog
+ switch copyu(pp, &p.To, nil) {
+ case _None:
+ rr = gc.Uniqs(rr)
+ continue
+ case _Read:
+ if move == nil && pp.As == s390x.AMOVD && isGPR(&pp.From) && isGPR(&pp.To) {
+ move = rr
+ rr = gc.Uniqs(rr)
+ continue
+ }
+ case _Write:
+ if move == nil {
+ // dead code
+ excise(r)
+ n++
+ } else {
+ for prev := gc.Uniqp(move); prev != r; prev = gc.Uniqp(prev) {
+ if copyu(prev.Prog, &move.Prog.To, nil) != 0 {
+ move = nil
+ break
+ }
+ }
+ if move == nil {
+ break
+ }
+ p.Reg, p.To.Reg = p.To.Reg, move.Prog.To.Reg
+ excise(move)
+ n++
+
+ // clean up
+ if p.From.Reg == p.To.Reg && isCommutative(p.As) {
+ p.From.Reg, p.Reg = p.Reg, 0
+ }
+ if p.To.Reg == p.Reg {
+ p.Reg = 0
+ }
+ // we could try again if p has become a 2-operand op
+ // but in testing nothing extra was extracted
+ }
+ }
+ break
+ }
+ }
+ return n
+}
+
+// isCommutative returns true if the order of input operands
+// does not affect the result. For example:
+// x + y == y + x so ADD is commutative
+// x ^ y == y ^ x so XOR is commutative
+func isCommutative(as obj.As) bool {
+ switch as {
+ case s390x.AADD,
+ s390x.AOR,
+ s390x.AAND,
+ s390x.AXOR,
+ s390x.AMULLW,
+ s390x.AMULLD:
+ return true
+ }
+ return false
+}
+
+// applyCast applies the cast implied by the given move
+// instruction to v and returns the result.
+func applyCast(cast obj.As, v int64) int64 {
+ switch cast {
+ case s390x.AMOVWZ:
+ return int64(uint32(v))
+ case s390x.AMOVHZ:
+ return int64(uint16(v))
+ case s390x.AMOVBZ:
+ return int64(uint8(v))
+ case s390x.AMOVW:
+ return int64(int32(v))
+ case s390x.AMOVH:
+ return int64(int16(v))
+ case s390x.AMOVB:
+ return int64(int8(v))
+ }
+ return v
+}
+
+// constantPropagation removes redundant constant copies.
+func constantPropagation(r *gc.Flow) int {
+ n := 0
+ // find MOV $con,R followed by
+ // another MOV $con,R without
+ // setting R in the interim
+ for ; r != nil; r = r.Link {
+ p := r.Prog
+ if isMove(p) {
+ if !isReg(&p.To) {
+ continue
+ }
+ if !isConst(&p.From) {
+ continue
+ }
+ } else {
+ continue
+ }
+
+ rr := r
+ for {
+ rr = gc.Uniqs(rr)
+ if rr == nil || rr == r {
+ break
+ }
+ if gc.Uniqp(rr) == nil {
+ break
+ }
+
+ pp := rr.Prog
+ t := copyu(pp, &p.To, nil)
+ switch t {
+ case _None:
+ continue
+ case _Read:
+ if !isGPR(&pp.From) || !isMove(pp) {
+ continue
+ }
+ if p.From.Type == obj.TYPE_CONST {
+ v := applyCast(p.As, p.From.Offset)
+ if isGPR(&pp.To) {
+ if int64(int32(v)) == v || ((v>>32)<<32) == v {
+ pp.From.Reg = 0
+ pp.From.Offset = v
+ pp.From.Type = obj.TYPE_CONST
+ n++
+ }
+ } else if int64(int16(v)) == v {
+ pp.From.Reg = 0
+ pp.From.Offset = v
+ pp.From.Type = obj.TYPE_CONST
+ n++
+ }
+ }
+ continue
+ case _Write:
+ if p.As != pp.As || p.From.Type != pp.From.Type {
+ break
+ }
+ if p.From.Type == obj.TYPE_CONST && p.From.Offset == pp.From.Offset {
+ excise(rr)
+ n++
+ continue
+ } else if p.From.Type == obj.TYPE_FCONST {
+ if p.From.Val.(float64) == pp.From.Val.(float64) {
+ excise(rr)
+ n++
+ continue
+ }
+ }
+ }
+ break
+ }
+ }
+ return n
+}
+
+// copyPropagation tries to eliminate register-to-register moves.
+func copyPropagation(r *gc.Flow) int {
+ n := 0
+ for ; r != nil; r = r.Link {
+ p := r.Prog
+ if isMove(p) && isReg(&p.To) {
+ // Convert uses to $0 to uses of R0 and
+ // propagate R0
+ if isGPR(&p.To) && isZero(&p.From) {
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = s390x.REGZERO
+ }
+
+ // Try to eliminate reg->reg moves
+ if isGPR(&p.From) || isFPR(&p.From) {
+ if copyprop(r) || (subprop(r) && copyprop(r)) {
+ excise(r)
+ n++
+ }
+ }
+ }
+ }
+ return n
+}
+
+// loadPipelining pushes any load from memory as early as possible.
+func loadPipelining(r *gc.Flow) int {
+ for ; r != nil; r = r.Link {
+ p := r.Prog
+ if isLoad(p) {
+ pushback(r)
+ }
+ }
+ return 0
+}
+
+// fuseCompareBranch finds comparisons followed by a branch and converts
+// them into a compare-and-branch instruction (which avoid setting the
+// condition code).
+func fuseCompareBranch(r *gc.Flow) int {
+ n := 0
+ for ; r != nil; r = r.Link {
+ p := r.Prog
+ r1 := gc.Uniqs(r)
+ if r1 == nil {
+ continue
+ }
+ p1 := r1.Prog
+
+ var ins obj.As
+ switch p.As {
+ case s390x.ACMP:
+ switch p1.As {
+ case s390x.ABCL, s390x.ABC:
+ continue
+ case s390x.ABEQ:
+ ins = s390x.ACMPBEQ
+ case s390x.ABGE:
+ ins = s390x.ACMPBGE
+ case s390x.ABGT:
+ ins = s390x.ACMPBGT
+ case s390x.ABLE:
+ ins = s390x.ACMPBLE
+ case s390x.ABLT:
+ ins = s390x.ACMPBLT
+ case s390x.ABNE:
+ ins = s390x.ACMPBNE
+ default:
+ continue
+ }
+
+ case s390x.ACMPU:
+ switch p1.As {
+ case s390x.ABCL, s390x.ABC:
+ continue
+ case s390x.ABEQ:
+ ins = s390x.ACMPUBEQ
+ case s390x.ABGE:
+ ins = s390x.ACMPUBGE
+ case s390x.ABGT:
+ ins = s390x.ACMPUBGT
+ case s390x.ABLE:
+ ins = s390x.ACMPUBLE
+ case s390x.ABLT:
+ ins = s390x.ACMPUBLT
+ case s390x.ABNE:
+ ins = s390x.ACMPUBNE
+ default:
+ continue
+ }
+
+ case s390x.ACMPW, s390x.ACMPWU:
+ continue
+
+ default:
+ continue
+ }
+
+ if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
+ fmt.Printf("cnb %v; %v ", p, p1)
+ }
+
+ if p1.To.Sym != nil {
+ continue
+ }
+
+ if p.To.Type == obj.TYPE_REG {
+ p1.As = ins
+ p1.From = p.From
+ p1.Reg = p.To.Reg
+ p1.From3 = nil
+ } else if p.To.Type == obj.TYPE_CONST {
+ switch p.As {
+ case s390x.ACMP, s390x.ACMPW:
+ if (p.To.Offset < -(1 << 7)) || (p.To.Offset >= ((1 << 7) - 1)) {
+ continue
+ }
+ case s390x.ACMPU, s390x.ACMPWU:
+ if p.To.Offset >= (1 << 8) {
+ continue
+ }
+ default:
+ }
+ p1.As = ins
+ p1.From = p.From
+ p1.Reg = 0
+ p1.From3 = new(obj.Addr)
+ *(p1.From3) = p.To
+ } else {
+ continue
+ }
+
+ if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
+ fmt.Printf("%v\n", p1)
+ }
+ excise(r)
+ n++
+ }
+ return n
+}
+
+// deadCodeElimination removes writes to registers which are written
+// to again before they are next read.
+func deadCodeElimination(r *gc.Flow) int {
+ n := 0
+ for ; r != nil; r = r.Link {
+ p := r.Prog
+ // Currently there are no instructions which write to multiple
+ // registers in copyu. This check will need to change if there
+ // ever are.
+ if !(isGPR(&p.To) || isFPR(&p.To)) || copyu(p, &p.To, nil) != _Write {
+ continue
+ }
+ for rr := gc.Uniqs(r); rr != nil; rr = gc.Uniqs(rr) {
+ t := copyu(rr.Prog, &p.To, nil)
+ if t == _None {
+ continue
+ }
+ if t == _Write {
+ excise(r)
+ n++
+ }
+ break
+ }
+ }
+ return n
+}
diff --git a/src/cmd/compile/internal/s390x/prog.go b/src/cmd/compile/internal/s390x/prog.go
new file mode 100644
index 0000000000..306adf85c3
--- /dev/null
+++ b/src/cmd/compile/internal/s390x/prog.go
@@ -0,0 +1,179 @@
+// Copyright 2016 The Go 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 s390x
+
+import (
+ "cmd/compile/internal/gc"
+ "cmd/internal/obj"
+ "cmd/internal/obj/s390x"
+)
+
+// 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.
+var progtable = [s390x.ALAST & obj.AMask]obj.ProgInfo{
+ obj.ATYPE & obj.AMask: {Flags: gc.Pseudo | gc.Skip},
+ obj.ATEXT & obj.AMask: {Flags: gc.Pseudo},
+ obj.AFUNCDATA & obj.AMask: {Flags: gc.Pseudo},
+ obj.APCDATA & obj.AMask: {Flags: gc.Pseudo},
+ obj.AUNDEF & obj.AMask: {Flags: gc.Break},
+ obj.AUSEFIELD & obj.AMask: {Flags: gc.OK},
+ obj.ACHECKNIL & obj.AMask: {Flags: gc.LeftRead},
+ obj.AVARDEF & obj.AMask: {Flags: gc.Pseudo | gc.RightWrite},
+ obj.AVARKILL & obj.AMask: {Flags: gc.Pseudo | gc.RightWrite},
+ obj.AVARLIVE & obj.AMask: {Flags: gc.Pseudo | gc.LeftRead},
+
+ // NOP is an internal no-op that also stands
+ // for USED and SET annotations.
+ obj.ANOP & obj.AMask: {Flags: gc.LeftRead | gc.RightWrite},
+
+ // Integer
+ s390x.AADD & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ s390x.ASUB & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ s390x.ANEG & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ s390x.AAND & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ s390x.AOR & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ s390x.AXOR & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ s390x.AMULLD & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ s390x.AMULLW & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ s390x.AMULHD & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ s390x.AMULHDU & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ s390x.ADIVD & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ s390x.ADIVDU & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ s390x.ASLD & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ s390x.ASRD & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ s390x.ASRAD & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ s390x.ARLL & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ s390x.ARLLG & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ s390x.ACMP & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightRead},
+ s390x.ACMPU & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightRead},
+
+ // Floating point.
+ s390x.AFADD & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ s390x.AFADDS & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ s390x.AFSUB & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ s390x.AFSUBS & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ s390x.AFMUL & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ s390x.AFMULS & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ s390x.AFDIV & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ s390x.AFDIVS & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ s390x.AFCMPU & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightRead},
+ s390x.ACEBR & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightRead},
+ s390x.ALEDBR & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv},
+ s390x.ALDEBR & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv},
+ s390x.AFSQRT & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite},
+
+ // Conversions
+ s390x.ACEFBRA & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv},
+ s390x.ACDFBRA & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv},
+ s390x.ACEGBRA & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv},
+ s390x.ACDGBRA & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv},
+ s390x.ACFEBRA & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv},
+ s390x.ACFDBRA & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv},
+ s390x.ACGEBRA & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv},
+ s390x.ACGDBRA & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv},
+ s390x.ACELFBR & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv},
+ s390x.ACDLFBR & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv},
+ s390x.ACELGBR & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv},
+ s390x.ACDLGBR & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv},
+ s390x.ACLFEBR & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv},
+ s390x.ACLFDBR & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv},
+ s390x.ACLGEBR & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv},
+ s390x.ACLGDBR & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv},
+
+ // Moves
+ s390x.AMOVB & obj.AMask: {Flags: gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+ s390x.AMOVBZ & obj.AMask: {Flags: gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+ s390x.AMOVH & obj.AMask: {Flags: gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+ s390x.AMOVHZ & obj.AMask: {Flags: gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+ s390x.AMOVW & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+ s390x.AMOVWZ & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+ s390x.AMOVD & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
+ s390x.AFMOVS & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+ s390x.AFMOVD & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move},
+
+ // Storage operations
+ s390x.AMVC & obj.AMask: {Flags: gc.LeftRead | gc.LeftAddr | gc.RightWrite | gc.RightAddr},
+ s390x.ACLC & obj.AMask: {Flags: gc.LeftRead | gc.LeftAddr | gc.RightRead | gc.RightAddr},
+ s390x.AXC & obj.AMask: {Flags: gc.LeftRead | gc.LeftAddr | gc.RightWrite | gc.RightAddr},
+ s390x.AOC & obj.AMask: {Flags: gc.LeftRead | gc.LeftAddr | gc.RightWrite | gc.RightAddr},
+ s390x.ANC & obj.AMask: {Flags: gc.LeftRead | gc.LeftAddr | gc.RightWrite | gc.RightAddr},
+
+ // Jumps
+ s390x.ABR & obj.AMask: {Flags: gc.Jump | gc.Break},
+ s390x.ABL & obj.AMask: {Flags: gc.Call},
+ s390x.ABEQ & obj.AMask: {Flags: gc.Cjmp},
+ s390x.ABNE & obj.AMask: {Flags: gc.Cjmp},
+ s390x.ABGE & obj.AMask: {Flags: gc.Cjmp},
+ s390x.ABLT & obj.AMask: {Flags: gc.Cjmp},
+ s390x.ABGT & obj.AMask: {Flags: gc.Cjmp},
+ s390x.ABLE & obj.AMask: {Flags: gc.Cjmp},
+ s390x.ACMPBEQ & obj.AMask: {Flags: gc.Cjmp},
+ s390x.ACMPBNE & obj.AMask: {Flags: gc.Cjmp},
+ s390x.ACMPBGE & obj.AMask: {Flags: gc.Cjmp},
+ s390x.ACMPBLT & obj.AMask: {Flags: gc.Cjmp},
+ s390x.ACMPBGT & obj.AMask: {Flags: gc.Cjmp},
+ s390x.ACMPBLE & obj.AMask: {Flags: gc.Cjmp},
+ s390x.ACMPUBEQ & obj.AMask: {Flags: gc.Cjmp},
+ s390x.ACMPUBNE & obj.AMask: {Flags: gc.Cjmp},
+ s390x.ACMPUBGE & obj.AMask: {Flags: gc.Cjmp},
+ s390x.ACMPUBLT & obj.AMask: {Flags: gc.Cjmp},
+ s390x.ACMPUBGT & obj.AMask: {Flags: gc.Cjmp},
+ s390x.ACMPUBLE & obj.AMask: {Flags: gc.Cjmp},
+
+ // Macros
+ s390x.ACLEAR & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightAddr | gc.RightWrite},
+
+ // Load/store multiple
+ s390x.ASTMG & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightAddr | gc.RightWrite},
+ s390x.ASTMY & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightAddr | gc.RightWrite},
+ s390x.ALMG & obj.AMask: {Flags: gc.SizeQ | gc.LeftAddr | gc.LeftRead | gc.RightWrite},
+ s390x.ALMY & obj.AMask: {Flags: gc.SizeL | gc.LeftAddr | gc.LeftRead | gc.RightWrite},
+
+ obj.ARET & obj.AMask: {Flags: gc.Break},
+}
+
+func proginfo(p *obj.Prog) {
+ info := &p.Info
+ *info = progtable[p.As&obj.AMask]
+ if info.Flags == 0 {
+ gc.Fatalf("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.To.Type == obj.TYPE_MEM || p.To.Type == obj.TYPE_ADDR) && p.To.Reg != 0 {
+ info.Regindex |= 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
+ }
+
+ switch p.As {
+ // load multiple sets a range of registers
+ case s390x.ALMG, s390x.ALMY:
+ for r := p.Reg; r <= p.To.Reg; r++ {
+ info.Regset |= RtoB(int(r))
+ }
+ // store multiple reads a range of registers
+ case s390x.ASTMG, s390x.ASTMY:
+ for r := p.From.Reg; r <= p.Reg; r++ {
+ info.Reguse |= RtoB(int(r))
+ }
+ }
+}
diff --git a/src/cmd/compile/internal/s390x/reg.go b/src/cmd/compile/internal/s390x/reg.go
new file mode 100644
index 0000000000..4cb8a9da05
--- /dev/null
+++ b/src/cmd/compile/internal/s390x/reg.go
@@ -0,0 +1,130 @@
+// 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 s390x
+
+import "cmd/internal/obj/s390x"
+import "cmd/compile/internal/gc"
+
+const (
+ NREGVAR = 32 /* 16 general + 16 floating */
+)
+
+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 {
+ // Exclude registers with fixed functions
+ return RtoB(s390x.REG_R0) |
+ RtoB(s390x.REGSP) |
+ RtoB(s390x.REGG) |
+ RtoB(s390x.REGTMP) |
+ RtoB(s390x.REGTMP2) |
+ RtoB(s390x.REG_LR)
+}
+
+func doregbits(r int) uint64 {
+ return 0
+}
+
+/*
+ * track register variables including external registers:
+ * bit reg
+ * 0 R0
+ * ... ...
+ * 15 R15
+ * 16+0 F0
+ * 16+1 F1
+ * ... ...
+ * 16+15 F15
+ */
+func RtoB(r int) uint64 {
+ if r >= s390x.REG_R0 && r <= s390x.REG_R15 {
+ return 1 << uint(r-s390x.REG_R0)
+ }
+ if r >= s390x.REG_F0 && r <= s390x.REG_F15 {
+ return 1 << uint(16+r-s390x.REG_F0)
+ }
+ return 0
+}
+
+func BtoR(b uint64) int {
+ b &= 0xffff
+ if b == 0 {
+ return 0
+ }
+ return gc.Bitno(b) + s390x.REG_R0
+}
+
+func BtoF(b uint64) int {
+ b >>= 16
+ b &= 0xffff
+ if b == 0 {
+ return 0
+ }
+ return gc.Bitno(b) + s390x.REG_F0
+}
diff --git a/src/cmd/compile/internal/ssa/TODO b/src/cmd/compile/internal/ssa/TODO
index e081856bd3..dad4880994 100644
--- a/src/cmd/compile/internal/ssa/TODO
+++ b/src/cmd/compile/internal/ssa/TODO
@@ -41,8 +41,6 @@ Future/other
------------
- Start another architecture (arm?)
- 64-bit ops on 32-bit machines
-- Investigate type equality. During SSA generation, should we use n.Type or (say) TypeBool?
- Should we get rid of named types in favor of underlying types during SSA generation?
-- Should we introduce a new type equality routine that is less strict than the frontend's?
- Infrastructure for enabling/disabling/configuring passes
- Modify logging for at least pass=1, to be Warnl compatible
diff --git a/src/cmd/compile/internal/ssa/check.go b/src/cmd/compile/internal/ssa/check.go
index 5a17735304..4a10606d3c 100644
--- a/src/cmd/compile/internal/ssa/check.go
+++ b/src/cmd/compile/internal/ssa/check.go
@@ -162,7 +162,7 @@ func checkFunc(f *Func) {
// variable length args)
nArgs := opcodeTable[v.Op].argLen
if nArgs != -1 && int32(len(v.Args)) != nArgs {
- f.Fatalf("value %v has %d args, expected %d", v.LongString(),
+ f.Fatalf("value %s has %d args, expected %d", v.LongString(),
len(v.Args), nArgs)
}
@@ -193,6 +193,8 @@ func checkFunc(f *Func) {
canHaveAuxInt = true
case auxInt64, auxFloat64:
canHaveAuxInt = true
+ case auxInt128:
+ // AuxInt must be zero, so leave canHaveAuxInt set to false.
case auxFloat32:
canHaveAuxInt = true
if !isExactFloat32(v) {
@@ -203,19 +205,25 @@ func checkFunc(f *Func) {
case auxSymOff, auxSymValAndOff:
canHaveAuxInt = true
canHaveAux = true
+ case auxSymInt32:
+ if v.AuxInt != int64(int32(v.AuxInt)) {
+ f.Fatalf("bad int32 AuxInt value for %v", v)
+ }
+ canHaveAuxInt = true
+ canHaveAux = true
default:
f.Fatalf("unknown aux type for %s", v.Op)
}
if !canHaveAux && v.Aux != nil {
- f.Fatalf("value %v has an Aux value %v but shouldn't", v.LongString(), v.Aux)
+ f.Fatalf("value %s has an Aux value %v but shouldn't", v.LongString(), v.Aux)
}
if !canHaveAuxInt && v.AuxInt != 0 {
- f.Fatalf("value %v has an AuxInt value %d but shouldn't", v.LongString(), v.AuxInt)
+ f.Fatalf("value %s has an AuxInt value %d but shouldn't", v.LongString(), v.AuxInt)
}
for _, arg := range v.Args {
if arg == nil {
- f.Fatalf("value %v has nil arg", v.LongString())
+ f.Fatalf("value %s has nil arg", v.LongString())
}
}
@@ -271,7 +279,7 @@ func checkFunc(f *Func) {
for _, v := range b.Values {
for i, a := range v.Args {
if !valueMark[a.ID] {
- f.Fatalf("%v, arg %d of %v, is missing", a, i, v)
+ f.Fatalf("%v, arg %d of %s, is missing", a, i, v.LongString())
}
}
}
@@ -338,7 +346,7 @@ func checkFunc(f *Func) {
// domCheck reports whether x dominates y (including x==y).
func domCheck(f *Func, sdom sparseTree, x, y *Block) bool {
- if !sdom.isAncestorEq(y, f.Entry) {
+ if !sdom.isAncestorEq(f.Entry, y) {
// unreachable - ignore
return true
}
diff --git a/src/cmd/compile/internal/ssa/compile.go b/src/cmd/compile/internal/ssa/compile.go
index d52ae9c6da..bc9c830ee9 100644
--- a/src/cmd/compile/internal/ssa/compile.go
+++ b/src/cmd/compile/internal/ssa/compile.go
@@ -230,9 +230,10 @@ var passes = [...]pass{
{name: "early deadcode", fn: deadcode}, // remove generated dead code to avoid doing pointless work during opt
{name: "short circuit", fn: shortcircuit},
{name: "decompose user", fn: decomposeUser, required: true},
- {name: "opt", fn: opt, required: true}, // TODO: split required rules and optimizing rules
- {name: "zero arg cse", fn: zcse, required: true}, // required to merge OpSB values
- {name: "opt deadcode", fn: deadcode}, // remove any blocks orphaned during opt
+ {name: "opt", fn: opt, required: true}, // TODO: split required rules and optimizing rules
+ {name: "zero arg cse", fn: zcse, required: true}, // required to merge OpSB values
+ {name: "opt deadcode", fn: deadcode, required: true}, // remove any blocks orphaned during opt
+ {name: "generic domtree", fn: domTree},
{name: "generic cse", fn: cse},
{name: "phiopt", fn: phiopt},
{name: "nilcheckelim", fn: nilcheckelim},
@@ -288,6 +289,12 @@ var passOrder = [...]constraint{
{"opt", "nilcheckelim"},
// tighten should happen before lowering to avoid splitting naturally paired instructions such as CMP/SET
{"tighten", "lower"},
+ // cse, phiopt, nilcheckelim, prove and loopbce share idom.
+ {"generic domtree", "generic cse"},
+ {"generic domtree", "phiopt"},
+ {"generic domtree", "nilcheckelim"},
+ {"generic domtree", "prove"},
+ {"generic domtree", "loopbce"},
// tighten will be most effective when as many values have been removed as possible
{"generic deadcode", "tighten"},
{"generic cse", "tighten"},
diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go
index 33357124fc..a60291ea53 100644
--- a/src/cmd/compile/internal/ssa/config.go
+++ b/src/cmd/compile/internal/ssa/config.go
@@ -103,6 +103,7 @@ type Frontend interface {
SplitInterface(LocalSlot) (LocalSlot, LocalSlot)
SplitSlice(LocalSlot) (LocalSlot, LocalSlot, LocalSlot)
SplitComplex(LocalSlot) (LocalSlot, LocalSlot)
+ SplitStruct(LocalSlot, int) LocalSlot
// Line returns a string describing the given line number.
Line(int32) string
@@ -115,6 +116,12 @@ type GCNode interface {
String() string
}
+// GCSym is an interface that *gc.Sym implements.
+// Using *gc.Sym directly would lead to import cycles.
+type GCSym interface {
+ IsRuntimeCall(name string) bool
+}
+
// NewConfig returns a new configuration object for the given architecture.
func NewConfig(arch string, fe Frontend, ctxt *obj.Link, optimize bool) *Config {
c := &Config{arch: arch, fe: fe}
diff --git a/src/cmd/compile/internal/ssa/cse.go b/src/cmd/compile/internal/ssa/cse.go
index 1ec5712be0..d501f75e02 100644
--- a/src/cmd/compile/internal/ssa/cse.go
+++ b/src/cmd/compile/internal/ssa/cse.go
@@ -108,7 +108,7 @@ func cse(f *Func) {
break
}
}
- if !equivalent || !v.Type.Equal(w.Type) {
+ if !equivalent || v.Type.Compare(w.Type) != CMPeq {
// w is not equivalent to v.
// move it to the end and shrink e.
e[j], e[len(e)-1] = e[len(e)-1], e[j]
@@ -131,33 +131,36 @@ func cse(f *Func) {
}
}
- // Compute dominator tree
- idom := dominators(f)
- sdom := newSparseTree(f, idom)
+ // Dominator tree (f.sdom) is computed by the generic domtree pass.
// Compute substitutions we would like to do. We substitute v for w
// if v and w are in the same equivalence class and v dominates w.
rewrite := make([]*Value, f.NumValues())
for _, e := range partition {
- for len(e) > 1 {
- // Find a maximal dominant element in e
- v := e[0]
- for _, w := range e[1:] {
- if sdom.isAncestorEq(w.Block, v.Block) {
- v = w
- }
+ sort.Sort(sortbyentry{e, f.sdom})
+ for i := 0; i < len(e)-1; i++ {
+ // e is sorted by entry value so maximal dominant element should be
+ // found first in the slice
+ v := e[i]
+ if v == nil {
+ continue
}
+ e[i] = nil
// Replace all elements of e which v dominates
- for i := 0; i < len(e); {
- w := e[i]
- if w == v {
- e, e[i] = e[:len(e)-1], e[len(e)-1]
- } else if sdom.isAncestorEq(v.Block, w.Block) {
+ for j := i + 1; j < len(e); j++ {
+ w := e[j]
+ if w == nil {
+ continue
+ }
+ if f.sdom.isAncestorEq(v.Block, w.Block) {
rewrite[w.ID] = v
- e, e[i] = e[:len(e)-1], e[len(e)-1]
+ e[j] = nil
} else {
- i++
+ // since the blocks are assorted in ascending order by entry number
+ // once we know that we don't dominate a block we can't dominate any
+ // 'later' block
+ break
}
}
}
@@ -255,6 +258,14 @@ func cmpVal(v, w *Value, auxIDs auxmap, depth int) Cmp {
return lt2Cmp(v.Block.ID < w.Block.ID)
}
+ switch v.Op {
+ case OpStaticCall, OpAMD64CALLstatic, OpARMCALLstatic:
+ sym := v.Aux.(GCSym)
+ if sym.IsRuntimeCall("newobject") {
+ return lt2Cmp(v.ID < w.ID)
+ }
+ }
+
if tc := v.Type.Compare(w.Type); tc != CMPeq {
return tc
}
@@ -302,3 +313,16 @@ func (sv sortvalues) Less(i, j int) bool {
// Sort by value ID last to keep the sort result deterministic.
return v.ID < w.ID
}
+
+type sortbyentry struct {
+ a []*Value // array of values
+ sdom sparseTree
+}
+
+func (sv sortbyentry) Len() int { return len(sv.a) }
+func (sv sortbyentry) Swap(i, j int) { sv.a[i], sv.a[j] = sv.a[j], sv.a[i] }
+func (sv sortbyentry) Less(i, j int) bool {
+ v := sv.a[i]
+ w := sv.a[j]
+ return sv.sdom.maxdomorder(v.Block) < sv.sdom.maxdomorder(w.Block)
+}
diff --git a/src/cmd/compile/internal/ssa/cse_test.go b/src/cmd/compile/internal/ssa/cse_test.go
index 905939fc32..d5be2b52ec 100644
--- a/src/cmd/compile/internal/ssa/cse_test.go
+++ b/src/cmd/compile/internal/ssa/cse_test.go
@@ -44,6 +44,7 @@ func TestCSEAuxPartitionBug(t *testing.T) {
Exit("rstore")))
CheckFunc(fun.f)
+ domTree(fun.f)
cse(fun.f)
deadcode(fun.f)
CheckFunc(fun.f)
diff --git a/src/cmd/compile/internal/ssa/decompose.go b/src/cmd/compile/internal/ssa/decompose.go
index eab9974106..53116ba593 100644
--- a/src/cmd/compile/internal/ssa/decompose.go
+++ b/src/cmd/compile/internal/ssa/decompose.go
@@ -21,6 +21,7 @@ func decomposeBuiltIn(f *Func) {
// NOTE: the component values we are making are dead at this point.
// We must do the opt pass before any deadcode elimination or we will
// lose the name->value correspondence.
+ var newNames []LocalSlot
for _, name := range f.Names {
t := name.Type
switch {
@@ -32,29 +33,31 @@ func decomposeBuiltIn(f *Func) {
elemType = f.Config.fe.TypeFloat32()
}
rName, iName := f.Config.fe.SplitComplex(name)
- f.Names = append(f.Names, rName, iName)
+ newNames = append(newNames, rName, iName)
for _, v := range f.NamedValues[name] {
r := v.Block.NewValue1(v.Line, OpComplexReal, elemType, v)
i := v.Block.NewValue1(v.Line, OpComplexImag, elemType, v)
f.NamedValues[rName] = append(f.NamedValues[rName], r)
f.NamedValues[iName] = append(f.NamedValues[iName], i)
}
+ delete(f.NamedValues, name)
case t.IsString():
ptrType := f.Config.fe.TypeBytePtr()
lenType := f.Config.fe.TypeInt()
ptrName, lenName := f.Config.fe.SplitString(name)
- f.Names = append(f.Names, ptrName, lenName)
+ newNames = append(newNames, ptrName, lenName)
for _, v := range f.NamedValues[name] {
ptr := v.Block.NewValue1(v.Line, OpStringPtr, ptrType, v)
len := v.Block.NewValue1(v.Line, OpStringLen, lenType, v)
f.NamedValues[ptrName] = append(f.NamedValues[ptrName], ptr)
f.NamedValues[lenName] = append(f.NamedValues[lenName], len)
}
+ delete(f.NamedValues, name)
case t.IsSlice():
ptrType := f.Config.fe.TypeBytePtr()
lenType := f.Config.fe.TypeInt()
ptrName, lenName, capName := f.Config.fe.SplitSlice(name)
- f.Names = append(f.Names, ptrName, lenName, capName)
+ newNames = append(newNames, ptrName, lenName, capName)
for _, v := range f.NamedValues[name] {
ptr := v.Block.NewValue1(v.Line, OpSlicePtr, ptrType, v)
len := v.Block.NewValue1(v.Line, OpSliceLen, lenType, v)
@@ -63,20 +66,25 @@ func decomposeBuiltIn(f *Func) {
f.NamedValues[lenName] = append(f.NamedValues[lenName], len)
f.NamedValues[capName] = append(f.NamedValues[capName], cap)
}
+ delete(f.NamedValues, name)
case t.IsInterface():
ptrType := f.Config.fe.TypeBytePtr()
typeName, dataName := f.Config.fe.SplitInterface(name)
- f.Names = append(f.Names, typeName, dataName)
+ newNames = append(newNames, typeName, dataName)
for _, v := range f.NamedValues[name] {
typ := v.Block.NewValue1(v.Line, OpITab, ptrType, v)
data := v.Block.NewValue1(v.Line, OpIData, ptrType, v)
f.NamedValues[typeName] = append(f.NamedValues[typeName], typ)
f.NamedValues[dataName] = append(f.NamedValues[dataName], data)
}
+ delete(f.NamedValues, name)
case t.Size() > f.Config.IntSize:
- f.Unimplementedf("undecomposed named type %s", t)
+ f.Unimplementedf("undecomposed named type %s %s", name, t)
+ default:
+ newNames = append(newNames, name)
}
}
+ f.Names = newNames
}
func decomposeBuiltInPhi(v *Value) {
@@ -181,25 +189,32 @@ func decomposeUser(f *Func) {
// We must do the opt pass before any deadcode elimination or we will
// lose the name->value correspondence.
i := 0
+ var fnames []LocalSlot
+ var newNames []LocalSlot
for _, name := range f.Names {
t := name.Type
switch {
case t.IsStruct():
n := t.NumFields()
+ fnames = fnames[:0]
+ for i := 0; i < n; i++ {
+ fnames = append(fnames, f.Config.fe.SplitStruct(name, i))
+ }
for _, v := range f.NamedValues[name] {
for i := 0; i < n; i++ {
- fname := LocalSlot{name.N, t.FieldType(i), name.Off + t.FieldOff(i)} // TODO: use actual field name?
x := v.Block.NewValue1I(v.Line, OpStructSelect, t.FieldType(i), int64(i), v)
- f.NamedValues[fname] = append(f.NamedValues[fname], x)
+ f.NamedValues[fnames[i]] = append(f.NamedValues[fnames[i]], x)
}
}
delete(f.NamedValues, name)
+ newNames = append(newNames, fnames...)
default:
f.Names[i] = name
i++
}
}
f.Names = f.Names[:i]
+ f.Names = append(f.Names, newNames...)
}
func decomposeUserPhi(v *Value) {
diff --git a/src/cmd/compile/internal/ssa/dom.go b/src/cmd/compile/internal/ssa/dom.go
index 0fffcdc2af..c0a4bb4188 100644
--- a/src/cmd/compile/internal/ssa/dom.go
+++ b/src/cmd/compile/internal/ssa/dom.go
@@ -5,11 +5,13 @@
package ssa
// mark values
+type markKind uint8
+
const (
- notFound = 0 // block has not been discovered yet
- notExplored = 1 // discovered and in queue, outedges not processed yet
- explored = 2 // discovered and in queue, outedges processed
- done = 3 // all done, in output ordering
+ notFound markKind = 0 // block has not been discovered yet
+ notExplored markKind = 1 // discovered and in queue, outedges not processed yet
+ explored markKind = 2 // discovered and in queue, outedges processed
+ done markKind = 3 // all done, in output ordering
)
// This file contains code to compute the dominator tree
@@ -18,7 +20,10 @@ const (
// postorder computes a postorder traversal ordering for the
// basic blocks in f. Unreachable blocks will not appear.
func postorder(f *Func) []*Block {
- mark := make([]byte, f.NumBlocks())
+ return postorderWithNumbering(f, []int{})
+}
+func postorderWithNumbering(f *Func, ponums []int) []*Block {
+ mark := make([]markKind, f.NumBlocks())
// result ordering
var order []*Block
@@ -34,6 +39,9 @@ func postorder(f *Func) []*Block {
// Children have all been visited. Pop & output block.
s = s[:len(s)-1]
mark[b.ID] = done
+ if len(ponums) > 0 {
+ ponums[b.ID] = len(order)
+ }
order = append(order, b)
case notExplored:
// Children have not been visited yet. Mark as explored
@@ -54,14 +62,14 @@ func postorder(f *Func) []*Block {
type linkedBlocks func(*Block) []*Block
-const nscratchslices = 8
+const nscratchslices = 7
// experimentally, functions with 512 or fewer blocks account
// for 75% of memory (size) allocation for dominator computation
// in make.bash.
const minscratchblocks = 512
-func (cfg *Config) scratchBlocksForDom(maxBlockID int) (a, b, c, d, e, f, g, h []ID) {
+func (cfg *Config) scratchBlocksForDom(maxBlockID int) (a, b, c, d, e, f, g []ID) {
tot := maxBlockID * nscratchslices
scratch := cfg.domblockstore
if len(scratch) < tot {
@@ -88,213 +96,143 @@ func (cfg *Config) scratchBlocksForDom(maxBlockID int) (a, b, c, d, e, f, g, h [
e = scratch[4*maxBlockID : 5*maxBlockID]
f = scratch[5*maxBlockID : 6*maxBlockID]
g = scratch[6*maxBlockID : 7*maxBlockID]
- h = scratch[7*maxBlockID : 8*maxBlockID]
return
}
-// dfs performs a depth first search over the blocks starting at the set of
-// blocks in the entries list (in arbitrary order). dfnum contains a mapping
-// from block id to an int indicating the order the block was reached or
-// notFound if the block was not reached. order contains a mapping from dfnum
-// to block.
-func (f *Func) dfs(entries []*Block, succFn linkedBlocks, dfnum, order, parent []ID) (fromID []*Block) {
- maxBlockID := entries[0].Func.NumBlocks()
-
- fromID = make([]*Block, maxBlockID)
-
- for _, entry := range entries[0].Func.Blocks {
- eid := entry.ID
- if fromID[eid] != nil {
- panic("Colliding entry IDs")
- }
- fromID[eid] = entry
- }
-
- n := ID(0)
- s := make([]*Block, 0, 256)
- for _, entry := range entries {
- if dfnum[entry.ID] != notFound {
- continue // already found from a previous entry
- }
- s = append(s, entry)
- parent[entry.ID] = entry.ID
- for len(s) > 0 {
- node := s[len(s)-1]
- s = s[:len(s)-1]
-
- n++
- for _, w := range succFn(node) {
- // if it has a dfnum, we've already visited it
- if dfnum[w.ID] == notFound {
- s = append(s, w)
- parent[w.ID] = node.ID
- dfnum[w.ID] = notExplored
- }
- }
- dfnum[node.ID] = n
- order[n] = node.ID
- }
- }
-
- return
-}
-
-// dominators computes the dominator tree for f. It returns a slice
-// which maps block ID to the immediate dominator of that block.
-// Unreachable blocks map to nil. The entry block maps to nil.
func dominators(f *Func) []*Block {
preds := func(b *Block) []*Block { return b.Preds }
succs := func(b *Block) []*Block { return b.Succs }
//TODO: benchmark and try to find criteria for swapping between
// dominatorsSimple and dominatorsLT
- return f.dominatorsLT([]*Block{f.Entry}, preds, succs)
+ return f.dominatorsLTOrig(f.Entry, preds, succs)
}
-// postDominators computes the post-dominator tree for f.
-func postDominators(f *Func) []*Block {
- preds := func(b *Block) []*Block { return b.Preds }
- succs := func(b *Block) []*Block { return b.Succs }
-
- if len(f.Blocks) == 0 {
- return nil
- }
-
- // find the exit blocks
- var exits []*Block
- for _, b := range f.Blocks {
- switch b.Kind {
- case BlockExit, BlockRet, BlockRetJmp, BlockCall, BlockCheck:
- exits = append(exits, b)
- }
- }
-
- // infinite loop with no exit
- if exits == nil {
- return make([]*Block, f.NumBlocks())
- }
- return f.dominatorsLT(exits, succs, preds)
-}
-
-// dominatorsLt runs Lengauer-Tarjan to compute a dominator tree starting at
+// dominatorsLTOrig runs Lengauer-Tarjan to compute a dominator tree starting at
// entry and using predFn/succFn to find predecessors/successors to allow
// computing both dominator and post-dominator trees.
-func (f *Func) dominatorsLT(entries []*Block, predFn linkedBlocks, succFn linkedBlocks) []*Block {
- // Based on Lengauer-Tarjan from Modern Compiler Implementation in C -
- // Appel with optimizations from Finding Dominators in Practice -
- // Georgiadis
+func (f *Func) dominatorsLTOrig(entry *Block, predFn linkedBlocks, succFn linkedBlocks) []*Block {
+ // Adapted directly from the original TOPLAS article's "simple" algorithm
- maxBlockID := entries[0].Func.NumBlocks()
+ maxBlockID := entry.Func.NumBlocks()
+ semi, vertex, label, parent, ancestor, bucketHead, bucketLink := f.Config.scratchBlocksForDom(maxBlockID)
- dfnum, vertex, parent, semi, samedom, ancestor, best, bucket := f.Config.scratchBlocksForDom(maxBlockID)
-
- // dfnum := make([]ID, maxBlockID) // conceptually int32, but punning for allocation purposes.
- // vertex := make([]ID, maxBlockID)
- // parent := make([]ID, maxBlockID)
-
- // semi := make([]ID, maxBlockID)
- // samedom := make([]ID, maxBlockID)
- // ancestor := make([]ID, maxBlockID)
- // best := make([]ID, maxBlockID)
- // bucket := make([]ID, maxBlockID)
+ // This version uses integers for most of the computation,
+ // to make the work arrays smaller and pointer-free.
+ // fromID translates from ID to *Block where that is needed.
+ fromID := make([]*Block, maxBlockID)
+ for _, v := range f.Blocks {
+ fromID[v.ID] = v
+ }
+ idom := make([]*Block, maxBlockID)
// Step 1. Carry out a depth first search of the problem graph. Number
// the vertices from 1 to n as they are reached during the search.
- fromID := f.dfs(entries, succFn, dfnum, vertex, parent)
+ n := f.dfsOrig(entry, succFn, semi, vertex, label, parent)
- idom := make([]*Block, maxBlockID)
-
- // Step 2. Compute the semidominators of all vertices by applying
- // Theorem 4. Carry out the computation vertex by vertex in decreasing
- // order by number.
- for i := maxBlockID - 1; i > 0; i-- {
+ for i := n; i >= 2; i-- {
w := vertex[i]
- if w == 0 {
- continue
- }
-
- if dfnum[w] == notFound {
- // skip unreachable node
- continue
- }
-
- // Step 3. Implicitly define the immediate dominator of each
- // vertex by applying Corollary 1. (reordered)
- for v := bucket[w]; v != 0; v = bucket[v] {
- u := eval(v, ancestor, semi, dfnum, best)
- if semi[u] == semi[v] {
- idom[v] = fromID[w] // true dominator
- } else {
- samedom[v] = u // v has same dominator as u
- }
- }
-
- p := parent[w]
- s := p // semidominator
- var sp ID
- // calculate the semidominator of w
+ // step2 in TOPLAS paper
for _, v := range predFn(fromID[w]) {
- if dfnum[v.ID] == notFound {
+ if semi[v.ID] == 0 {
// skip unreachable predecessor
+ // not in original, but we're using existing pred instead of building one.
continue
}
-
- if dfnum[v.ID] <= dfnum[w] {
- sp = v.ID
- } else {
- sp = semi[eval(v.ID, ancestor, semi, dfnum, best)]
- }
-
- if dfnum[sp] < dfnum[s] {
- s = sp
+ u := evalOrig(v.ID, ancestor, semi, label)
+ if semi[u] < semi[w] {
+ semi[w] = semi[u]
}
}
- // link
- ancestor[w] = p
- best[w] = w
+ // add w to bucket[vertex[semi[w]]]
+ // implement bucket as a linked list implemented
+ // in a pair of arrays.
+ vsw := vertex[semi[w]]
+ bucketLink[w] = bucketHead[vsw]
+ bucketHead[vsw] = w
+
+ linkOrig(parent[w], w, ancestor)
- semi[w] = s
- if semi[s] != parent[s] {
- bucket[w] = bucket[s]
- bucket[s] = w
+ // step3 in TOPLAS paper
+ for v := bucketHead[parent[w]]; v != 0; v = bucketLink[v] {
+ u := evalOrig(v, ancestor, semi, label)
+ if semi[u] < semi[v] {
+ idom[v] = fromID[u]
+ } else {
+ idom[v] = fromID[parent[w]]
+ }
}
}
-
- // Final pass of step 3
- for v := bucket[0]; v != 0; v = bucket[v] {
- idom[v] = fromID[bucket[0]]
+ // step 4 in toplas paper
+ for i := ID(2); i <= n; i++ {
+ w := vertex[i]
+ if idom[w].ID != vertex[semi[w]] {
+ idom[w] = idom[idom[w].ID]
+ }
}
- // Step 4. Explicitly define the immediate dominator of each vertex,
- // carrying out the computation vertex by vertex in increasing order by
- // number.
- for i := 1; i < maxBlockID-1; i++ {
- w := vertex[i]
- if w == 0 {
- continue
+ return idom
+}
+
+// dfs performs a depth first search over the blocks starting at entry block
+// (in arbitrary order). This is a de-recursed version of dfs from the
+// original Tarjan-Lengauer TOPLAS article. It's important to return the
+// same values for parent as the original algorithm.
+func (f *Func) dfsOrig(entry *Block, succFn linkedBlocks, semi, vertex, label, parent []ID) ID {
+ n := ID(0)
+ s := make([]*Block, 0, 256)
+ s = append(s, entry)
+
+ for len(s) > 0 {
+ v := s[len(s)-1]
+ s = s[:len(s)-1]
+ // recursing on v
+
+ if semi[v.ID] != 0 {
+ continue // already visited
}
- // w has the same dominator as samedom[w]
- if samedom[w] != 0 {
- idom[w] = idom[samedom[w]]
+ n++
+ semi[v.ID] = n
+ vertex[n] = v.ID
+ label[v.ID] = v.ID
+ // ancestor[v] already zero
+ for _, w := range succFn(v) {
+ // if it has a dfnum, we've already visited it
+ if semi[w.ID] == 0 {
+ // yes, w can be pushed multiple times.
+ s = append(s, w)
+ parent[w.ID] = v.ID // keep overwriting this till it is visited.
+ }
}
}
- return idom
+ return n
}
-// eval function from LT paper with path compression
-func eval(v ID, ancestor []ID, semi []ID, dfnum []ID, best []ID) ID {
- a := ancestor[v]
- if ancestor[a] != 0 {
- bid := eval(a, ancestor, semi, dfnum, best)
- ancestor[v] = ancestor[a]
- if dfnum[semi[bid]] < dfnum[semi[best[v]]] {
- best[v] = bid
+// compressOrig is the "simple" compress function from LT paper
+func compressOrig(v ID, ancestor, semi, label []ID) {
+ if ancestor[ancestor[v]] != 0 {
+ compressOrig(ancestor[v], ancestor, semi, label)
+ if semi[label[ancestor[v]]] < semi[label[v]] {
+ label[v] = label[ancestor[v]]
}
+ ancestor[v] = ancestor[ancestor[v]]
+ }
+}
+
+// evalOrig is the "simple" eval function from LT paper
+func evalOrig(v ID, ancestor, semi, label []ID) ID {
+ if ancestor[v] == 0 {
+ return v
}
- return best[v]
+ compressOrig(v, ancestor, semi, label)
+ return label[v]
+}
+
+func linkOrig(v, w ID, ancestor []ID) {
+ ancestor[w] = v
}
// dominators computes the dominator tree for f. It returns a slice
@@ -364,3 +302,9 @@ func intersect(b, c *Block, postnum []int, idom []*Block) *Block {
}
return b
}
+
+// build immediate dominators.
+func domTree(f *Func) {
+ f.idom = dominators(f)
+ f.sdom = newSparseTree(f, f.idom)
+}
diff --git a/src/cmd/compile/internal/ssa/dom_test.go b/src/cmd/compile/internal/ssa/dom_test.go
index 9741edf331..6ecbe923d4 100644
--- a/src/cmd/compile/internal/ssa/dom_test.go
+++ b/src/cmd/compile/internal/ssa/dom_test.go
@@ -372,32 +372,6 @@ func TestDominatorsMultPred(t *testing.T) {
verifyDominators(t, fun, dominatorsSimple, doms)
}
-func TestPostDominators(t *testing.T) {
- c := testConfig(t)
- fun := Fun(c, "entry",
- Bloc("entry",
- Valu("mem", OpInitMem, TypeMem, 0, nil),
- Valu("p", OpConstBool, TypeBool, 1, nil),
- If("p", "a", "c")),
- Bloc("a",
- If("p", "b", "c")),
- Bloc("b",
- Goto("c")),
- Bloc("c",
- If("p", "b", "exit")),
- Bloc("exit",
- Exit("mem")))
-
- doms := map[string]string{"entry": "c",
- "a": "c",
- "b": "c",
- "c": "exit",
- }
-
- CheckFunc(fun.f)
- verifyDominators(t, fun, postDominators, doms)
-}
-
func TestInfiniteLoop(t *testing.T) {
c := testConfig(t)
// note lack of an exit block
@@ -415,8 +389,184 @@ func TestInfiniteLoop(t *testing.T) {
doms := map[string]string{"a": "entry",
"b": "a"}
verifyDominators(t, fun, dominators, doms)
+}
- // no exit block, so there are no post-dominators
- postDoms := map[string]string{}
- verifyDominators(t, fun, postDominators, postDoms)
+func TestDomTricky(t *testing.T) {
+ doms := map[string]string{
+ "4": "1",
+ "2": "4",
+ "5": "4",
+ "11": "4",
+ "15": "4", // the incorrect answer is "5"
+ "10": "15",
+ "19": "15",
+ }
+
+ if4 := [2]string{"2", "5"}
+ if5 := [2]string{"15", "11"}
+ if15 := [2]string{"19", "10"}
+
+ for i := 0; i < 8; i++ {
+ a := 1 & i
+ b := 1 & i >> 1
+ c := 1 & i >> 2
+
+ fun := Fun(testConfig(t), "1",
+ Bloc("1",
+ Valu("mem", OpInitMem, TypeMem, 0, nil),
+ Valu("p", OpConstBool, TypeBool, 1, nil),
+ Goto("4")),
+ Bloc("2",
+ Goto("11")),
+ Bloc("4",
+ If("p", if4[a], if4[1-a])), // 2, 5
+ Bloc("5",
+ If("p", if5[b], if5[1-b])), //15, 11
+ Bloc("10",
+ Exit("mem")),
+ Bloc("11",
+ Goto("15")),
+ Bloc("15",
+ If("p", if15[c], if15[1-c])), //19, 10
+ Bloc("19",
+ Goto("10")))
+ CheckFunc(fun.f)
+ verifyDominators(t, fun, dominators, doms)
+ verifyDominators(t, fun, dominatorsSimple, doms)
+ }
+}
+
+// generateDominatorMap uses dominatorsSimple to obtain a
+// reference dominator tree for testing faster algorithms.
+func generateDominatorMap(fut fun) map[string]string {
+ blockNames := map[*Block]string{}
+ for n, b := range fut.blocks {
+ blockNames[b] = n
+ }
+ referenceDom := dominatorsSimple(fut.f)
+ doms := make(map[string]string)
+ for _, b := range fut.f.Blocks {
+ if d := referenceDom[b.ID]; d != nil {
+ doms[blockNames[b]] = blockNames[d]
+ }
+ }
+ return doms
+}
+
+func TestDominatorsPostTricky(t *testing.T) {
+ c := testConfig(t)
+ fun := Fun(c, "b1",
+ Bloc("b1",
+ Valu("mem", OpInitMem, TypeMem, 0, nil),
+ Valu("p", OpConstBool, TypeBool, 1, nil),
+ If("p", "b3", "b2")),
+ Bloc("b3",
+ If("p", "b5", "b6")),
+ Bloc("b5",
+ Goto("b7")),
+ Bloc("b7",
+ If("p", "b8", "b11")),
+ Bloc("b8",
+ Goto("b13")),
+ Bloc("b13",
+ If("p", "b14", "b15")),
+ Bloc("b14",
+ Goto("b10")),
+ Bloc("b15",
+ Goto("b16")),
+ Bloc("b16",
+ Goto("b9")),
+ Bloc("b9",
+ Goto("b7")),
+ Bloc("b11",
+ Goto("b12")),
+ Bloc("b12",
+ If("p", "b10", "b8")),
+ Bloc("b10",
+ Goto("b6")),
+ Bloc("b6",
+ Goto("b17")),
+ Bloc("b17",
+ Goto("b18")),
+ Bloc("b18",
+ If("p", "b22", "b19")),
+ Bloc("b22",
+ Goto("b23")),
+ Bloc("b23",
+ If("p", "b21", "b19")),
+ Bloc("b19",
+ If("p", "b24", "b25")),
+ Bloc("b24",
+ Goto("b26")),
+ Bloc("b26",
+ Goto("b25")),
+ Bloc("b25",
+ If("p", "b27", "b29")),
+ Bloc("b27",
+ Goto("b30")),
+ Bloc("b30",
+ Goto("b28")),
+ Bloc("b29",
+ Goto("b31")),
+ Bloc("b31",
+ Goto("b28")),
+ Bloc("b28",
+ If("p", "b32", "b33")),
+ Bloc("b32",
+ Goto("b21")),
+ Bloc("b21",
+ Goto("b47")),
+ Bloc("b47",
+ If("p", "b45", "b46")),
+ Bloc("b45",
+ Goto("b48")),
+ Bloc("b48",
+ Goto("b49")),
+ Bloc("b49",
+ If("p", "b50", "b51")),
+ Bloc("b50",
+ Goto("b52")),
+ Bloc("b52",
+ Goto("b53")),
+ Bloc("b53",
+ Goto("b51")),
+ Bloc("b51",
+ Goto("b54")),
+ Bloc("b54",
+ Goto("b46")),
+ Bloc("b46",
+ Exit("mem")),
+ Bloc("b33",
+ Goto("b34")),
+ Bloc("b34",
+ Goto("b37")),
+ Bloc("b37",
+ If("p", "b35", "b36")),
+ Bloc("b35",
+ Goto("b38")),
+ Bloc("b38",
+ Goto("b39")),
+ Bloc("b39",
+ If("p", "b40", "b41")),
+ Bloc("b40",
+ Goto("b42")),
+ Bloc("b42",
+ Goto("b43")),
+ Bloc("b43",
+ Goto("b41")),
+ Bloc("b41",
+ Goto("b44")),
+ Bloc("b44",
+ Goto("b36")),
+ Bloc("b36",
+ Goto("b20")),
+ Bloc("b20",
+ Goto("b18")),
+ Bloc("b2",
+ Goto("b4")),
+ Bloc("b4",
+ Exit("mem")))
+ CheckFunc(fun.f)
+ doms := generateDominatorMap(fun)
+ verifyDominators(t, fun, dominators, doms)
}
diff --git a/src/cmd/compile/internal/ssa/export_test.go b/src/cmd/compile/internal/ssa/export_test.go
index ce577ef055..0a67de9f05 100644
--- a/src/cmd/compile/internal/ssa/export_test.go
+++ b/src/cmd/compile/internal/ssa/export_test.go
@@ -48,6 +48,9 @@ func (d DummyFrontend) SplitComplex(s LocalSlot) (LocalSlot, LocalSlot) {
}
return LocalSlot{s.N, d.TypeFloat32(), s.Off}, LocalSlot{s.N, d.TypeFloat32(), s.Off + 4}
}
+func (d DummyFrontend) SplitStruct(s LocalSlot, i int) LocalSlot {
+ return LocalSlot{s.N, s.Type.FieldType(i), s.Off + s.Type.FieldOff(i)}
+}
func (DummyFrontend) Line(line int32) string {
return "unknown.go:0"
}
diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go
index 6e47b7f19c..11ff8d3792 100644
--- a/src/cmd/compile/internal/ssa/func.go
+++ b/src/cmd/compile/internal/ssa/func.go
@@ -36,6 +36,9 @@ type Func struct {
freeValues *Value // free Values linked by argstorage[0]. All other fields except ID are 0/nil.
freeBlocks *Block // free Blocks linked by succstorage[0]. All other fields except ID are 0/nil.
+ idom []*Block // precomputed immediate dominators
+ sdom sparseTree // precomputed dominator tree
+
constants map[int64][]*Value // constants cache, keyed by constant value; users must check value's Op and Type
}
@@ -284,7 +287,10 @@ func (b *Block) NewValue2I(line int32, op Op, t Type, auxint int64, arg0, arg1 *
func (b *Block) NewValue3(line int32, op Op, t Type, arg0, arg1, arg2 *Value) *Value {
v := b.Func.newValue(op, t, b, line)
v.AuxInt = 0
- v.Args = []*Value{arg0, arg1, arg2}
+ v.Args = v.argstorage[:3]
+ v.argstorage[0] = arg0
+ v.argstorage[1] = arg1
+ v.argstorage[2] = arg2
arg0.Uses++
arg1.Uses++
arg2.Uses++
@@ -295,7 +301,10 @@ func (b *Block) NewValue3(line int32, op Op, t Type, arg0, arg1, arg2 *Value) *V
func (b *Block) NewValue3I(line int32, op Op, t Type, auxint int64, arg0, arg1, arg2 *Value) *Value {
v := b.Func.newValue(op, t, b, line)
v.AuxInt = auxint
- v.Args = []*Value{arg0, arg1, arg2}
+ v.Args = v.argstorage[:3]
+ v.argstorage[0] = arg0
+ v.argstorage[1] = arg1
+ v.argstorage[2] = arg2
arg0.Uses++
arg1.Uses++
arg2.Uses++
@@ -309,7 +318,7 @@ func (f *Func) constVal(line int32, op Op, t Type, c int64, setAux bool) *Value
}
vv := f.constants[c]
for _, v := range vv {
- if v.Op == op && v.Type.Equal(t) {
+ if v.Op == op && v.Type.Compare(t) == CMPeq {
if setAux && v.AuxInt != c {
panic(fmt.Sprintf("cached const %s should have AuxInt of %d", v.LongString(), c))
}
diff --git a/src/cmd/compile/internal/ssa/gen/AMD64.rules b/src/cmd/compile/internal/ssa/gen/AMD64.rules
index 4ad0f883b0..86123ac5c5 100644
--- a/src/cmd/compile/internal/ssa/gen/AMD64.rules
+++ b/src/cmd/compile/internal/ssa/gen/AMD64.rules
@@ -6,23 +6,23 @@
(Add64 x y) -> (ADDQ x y)
(AddPtr x y) -> (ADDQ x y)
(Add32 x y) -> (ADDL x y)
-(Add16 x y) -> (ADDW x y)
-(Add8 x y) -> (ADDB x y)
+(Add16 x y) -> (ADDL x y)
+(Add8 x y) -> (ADDL x y)
(Add32F x y) -> (ADDSS x y)
(Add64F x y) -> (ADDSD x y)
(Sub64 x y) -> (SUBQ x y)
(SubPtr x y) -> (SUBQ x y)
(Sub32 x y) -> (SUBL x y)
-(Sub16 x y) -> (SUBW x y)
-(Sub8 x y) -> (SUBB x y)
+(Sub16 x y) -> (SUBL x y)
+(Sub8 x y) -> (SUBL x y)
(Sub32F x y) -> (SUBSS x y)
(Sub64F x y) -> (SUBSD x y)
(Mul64 x y) -> (MULQ x y)
(Mul32 x y) -> (MULL x y)
-(Mul16 x y) -> (MULW x y)
-(Mul8 x y) -> (MULB x y)
+(Mul16 x y) -> (MULL x y)
+(Mul8 x y) -> (MULL x y)
(Mul32F x y) -> (MULSS x y)
(Mul64F x y) -> (MULSD x y)
@@ -60,30 +60,30 @@
(And64 x y) -> (ANDQ x y)
(And32 x y) -> (ANDL x y)
-(And16 x y) -> (ANDW x y)
-(And8 x y) -> (ANDB x y)
+(And16 x y) -> (ANDL x y)
+(And8 x y) -> (ANDL x y)
(Or64 x y) -> (ORQ x y)
(Or32 x y) -> (ORL x y)
-(Or16 x y) -> (ORW x y)
-(Or8 x y) -> (ORB x y)
+(Or16 x y) -> (ORL x y)
+(Or8 x y) -> (ORL x y)
(Xor64 x y) -> (XORQ x y)
(Xor32 x y) -> (XORL x y)
-(Xor16 x y) -> (XORW x y)
-(Xor8 x y) -> (XORB x y)
+(Xor16 x y) -> (XORL x y)
+(Xor8 x y) -> (XORL x y)
(Neg64 x) -> (NEGQ x)
(Neg32 x) -> (NEGL x)
-(Neg16 x) -> (NEGW x)
-(Neg8 x) -> (NEGB x)
+(Neg16 x) -> (NEGL x)
+(Neg8 x) -> (NEGL x)
(Neg32F x) -> (PXOR x (MOVSSconst <config.Frontend().TypeFloat32()> [f2i(math.Copysign(0, -1))]))
(Neg64F x) -> (PXOR x (MOVSDconst <config.Frontend().TypeFloat64()> [f2i(math.Copysign(0, -1))]))
(Com64 x) -> (NOTQ x)
(Com32 x) -> (NOTL x)
-(Com16 x) -> (NOTW x)
-(Com8 x) -> (NOTB x)
+(Com16 x) -> (NOTL x)
+(Com8 x) -> (NOTL x)
// CMPQconst 0 below is redundant because BSF sets Z but how to remove?
(Ctz64 <t> x) -> (CMOVQEQconst (BSFQ <t> x) (CMPQconst x [0]) [64])
@@ -169,15 +169,15 @@
(Lsh32x16 <t> x y) -> (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPWconst y [32])))
(Lsh32x8 <t> x y) -> (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPBconst y [32])))
-(Lsh16x64 <t> x y) -> (ANDW (SHLW <t> x y) (SBBLcarrymask <t> (CMPQconst y [16])))
-(Lsh16x32 <t> x y) -> (ANDW (SHLW <t> x y) (SBBLcarrymask <t> (CMPLconst y [16])))
-(Lsh16x16 <t> x y) -> (ANDW (SHLW <t> x y) (SBBLcarrymask <t> (CMPWconst y [16])))
-(Lsh16x8 <t> x y) -> (ANDW (SHLW <t> x y) (SBBLcarrymask <t> (CMPBconst y [16])))
+(Lsh16x64 <t> x y) -> (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPQconst y [32])))
+(Lsh16x32 <t> x y) -> (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPLconst y [32])))
+(Lsh16x16 <t> x y) -> (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPWconst y [32])))
+(Lsh16x8 <t> x y) -> (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPBconst y [32])))
-(Lsh8x64 <t> x y) -> (ANDB (SHLB <t> x y) (SBBLcarrymask <t> (CMPQconst y [8])))
-(Lsh8x32 <t> x y) -> (ANDB (SHLB <t> x y) (SBBLcarrymask <t> (CMPLconst y [8])))
-(Lsh8x16 <t> x y) -> (ANDB (SHLB <t> x y) (SBBLcarrymask <t> (CMPWconst y [8])))
-(Lsh8x8 <t> x y) -> (ANDB (SHLB <t> x y) (SBBLcarrymask <t> (CMPBconst y [8])))
+(Lsh8x64 <t> x y) -> (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPQconst y [32])))
+(Lsh8x32 <t> x y) -> (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPLconst y [32])))
+(Lsh8x16 <t> x y) -> (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPWconst y [32])))
+(Lsh8x8 <t> x y) -> (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPBconst y [32])))
(Lrot64 <t> x [c]) -> (ROLQconst <t> [c&63] x)
(Lrot32 <t> x [c]) -> (ROLLconst <t> [c&31] x)
@@ -194,38 +194,38 @@
(Rsh32Ux16 <t> x y) -> (ANDL (SHRL <t> x y) (SBBLcarrymask <t> (CMPWconst y [32])))
(Rsh32Ux8 <t> x y) -> (ANDL (SHRL <t> x y) (SBBLcarrymask <t> (CMPBconst y [32])))
-(Rsh16Ux64 <t> x y) -> (ANDW (SHRW <t> x y) (SBBLcarrymask <t> (CMPQconst y [16])))
-(Rsh16Ux32 <t> x y) -> (ANDW (SHRW <t> x y) (SBBLcarrymask <t> (CMPLconst y [16])))
-(Rsh16Ux16 <t> x y) -> (ANDW (SHRW <t> x y) (SBBLcarrymask <t> (CMPWconst y [16])))
-(Rsh16Ux8 <t> x y) -> (ANDW (SHRW <t> x y) (SBBLcarrymask <t> (CMPBconst y [16])))
+(Rsh16Ux64 <t> x y) -> (ANDL (SHRW <t> x y) (SBBLcarrymask <t> (CMPQconst y [16])))
+(Rsh16Ux32 <t> x y) -> (ANDL (SHRW <t> x y) (SBBLcarrymask <t> (CMPLconst y [16])))
+(Rsh16Ux16 <t> x y) -> (ANDL (SHRW <t> x y) (SBBLcarrymask <t> (CMPWconst y [16])))
+(Rsh16Ux8 <t> x y) -> (ANDL (SHRW <t> x y) (SBBLcarrymask <t> (CMPBconst y [16])))
-(Rsh8Ux64 <t> x y) -> (ANDB (SHRB <t> x y) (SBBLcarrymask <t> (CMPQconst y [8])))
-(Rsh8Ux32 <t> x y) -> (ANDB (SHRB <t> x y) (SBBLcarrymask <t> (CMPLconst y [8])))
-(Rsh8Ux16 <t> x y) -> (ANDB (SHRB <t> x y) (SBBLcarrymask <t> (CMPWconst y [8])))
-(Rsh8Ux8 <t> x y) -> (ANDB (SHRB <t> x y) (SBBLcarrymask <t> (CMPBconst y [8])))
+(Rsh8Ux64 <t> x y) -> (ANDL (SHRB <t> x y) (SBBLcarrymask <t> (CMPQconst y [8])))
+(Rsh8Ux32 <t> x y) -> (ANDL (SHRB <t> x y) (SBBLcarrymask <t> (CMPLconst y [8])))
+(Rsh8Ux16 <t> x y) -> (ANDL (SHRB <t> x y) (SBBLcarrymask <t> (CMPWconst y [8])))
+(Rsh8Ux8 <t> x y) -> (ANDL (SHRB <t> x y) (SBBLcarrymask <t> (CMPBconst y [8])))
// Signed right shift needs to return 0/-1 if shift amount is >= width of shifted value.
// We implement this by setting the shift value to -1 (all ones) if the shift value is >= width.
// Note: for small shift widths we generate 32 bits of mask even when we don't need it all.
(Rsh64x64 <t> x y) -> (SARQ <t> x (ORQ <y.Type> y (NOTQ <y.Type> (SBBQcarrymask <y.Type> (CMPQconst y [64])))))
(Rsh64x32 <t> x y) -> (SARQ <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPLconst y [64])))))
-(Rsh64x16 <t> x y) -> (SARQ <t> x (ORW <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [64])))))
-(Rsh64x8 <t> x y) -> (SARQ <t> x (ORB <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [64])))))
+(Rsh64x16 <t> x y) -> (SARQ <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [64])))))
+(Rsh64x8 <t> x y) -> (SARQ <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [64])))))
(Rsh32x64 <t> x y) -> (SARL <t> x (ORQ <y.Type> y (NOTQ <y.Type> (SBBQcarrymask <y.Type> (CMPQconst y [32])))))
(Rsh32x32 <t> x y) -> (SARL <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPLconst y [32])))))
-(Rsh32x16 <t> x y) -> (SARL <t> x (ORW <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [32])))))
-(Rsh32x8 <t> x y) -> (SARL <t> x (ORB <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [32])))))
+(Rsh32x16 <t> x y) -> (SARL <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [32])))))
+(Rsh32x8 <t> x y) -> (SARL <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [32])))))
(Rsh16x64 <t> x y) -> (SARW <t> x (ORQ <y.Type> y (NOTQ <y.Type> (SBBQcarrymask <y.Type> (CMPQconst y [16])))))
(Rsh16x32 <t> x y) -> (SARW <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPLconst y [16])))))
-(Rsh16x16 <t> x y) -> (SARW <t> x (ORW <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [16])))))
-(Rsh16x8 <t> x y) -> (SARW <t> x (ORB <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [16])))))
+(Rsh16x16 <t> x y) -> (SARW <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [16])))))
+(Rsh16x8 <t> x y) -> (SARW <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [16])))))
(Rsh8x64 <t> x y) -> (SARB <t> x (ORQ <y.Type> y (NOTQ <y.Type> (SBBQcarrymask <y.Type> (CMPQconst y [8])))))
(Rsh8x32 <t> x y) -> (SARB <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPLconst y [8])))))
-(Rsh8x16 <t> x y) -> (SARB <t> x (ORW <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [8])))))
-(Rsh8x8 <t> x y) -> (SARB <t> x (ORB <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [8])))))
+(Rsh8x16 <t> x y) -> (SARB <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [8])))))
+(Rsh8x8 <t> x y) -> (SARB <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [8])))))
(Less64 x y) -> (SETL (CMPQ x y))
(Less32 x y) -> (SETL (CMPL x y))
@@ -281,6 +281,7 @@
(Eq32 x y) -> (SETEQ (CMPL x y))
(Eq16 x y) -> (SETEQ (CMPW x y))
(Eq8 x y) -> (SETEQ (CMPB x y))
+(EqB x y) -> (SETEQ (CMPB x y))
(EqPtr x y) -> (SETEQ (CMPQ x y))
(Eq64F x y) -> (SETEQF (UCOMISD x y))
(Eq32F x y) -> (SETEQF (UCOMISS x y))
@@ -289,6 +290,7 @@
(Neq32 x y) -> (SETNE (CMPL x y))
(Neq16 x y) -> (SETNE (CMPW x y))
(Neq8 x y) -> (SETNE (CMPB x y))
+(NeqB x y) -> (SETNE (CMPB x y))
(NeqPtr x y) -> (SETNE (CMPQ x y))
(Neq64F x y) -> (SETNEF (UCOMISD x y))
(Neq32F x y) -> (SETNEF (UCOMISS x y))
@@ -366,19 +368,21 @@
(Move [size] dst src mem) && (size > 16*64 || config.noDuffDevice) && size%8 == 0 ->
(REPMOVSQ dst src (MOVQconst [size/8]) mem)
-(Not x) -> (XORBconst [1] x)
+(AndB x y) -> (ANDL x y)
+(OrB x y) -> (ORL x y)
+(Not x) -> (XORLconst [1] x)
(OffPtr [off] ptr) && is32Bit(off) -> (ADDQconst [off] ptr)
(OffPtr [off] ptr) -> (ADDQ (MOVQconst [off]) ptr)
-(Const8 [val]) -> (MOVBconst [val])
-(Const16 [val]) -> (MOVWconst [val])
+(Const8 [val]) -> (MOVLconst [val])
+(Const16 [val]) -> (MOVLconst [val])
(Const32 [val]) -> (MOVLconst [val])
(Const64 [val]) -> (MOVQconst [val])
(Const32F [val]) -> (MOVSSconst [val])
(Const64F [val]) -> (MOVSDconst [val])
(ConstNil) -> (MOVQconst [0])
-(ConstBool [b]) -> (MOVBconst [b])
+(ConstBool [b]) -> (MOVLconst [b])
(Addr {sym} base) -> (LEAQ {sym} base)
@@ -404,22 +408,22 @@
(If cond yes no) -> (NE (TESTB cond cond) yes no)
-(NE (TESTB (SETL cmp)) yes no) -> (LT cmp yes no)
-(NE (TESTB (SETLE cmp)) yes no) -> (LE cmp yes no)
-(NE (TESTB (SETG cmp)) yes no) -> (GT cmp yes no)
-(NE (TESTB (SETGE cmp)) yes no) -> (GE cmp yes no)
-(NE (TESTB (SETEQ cmp)) yes no) -> (EQ cmp yes no)
-(NE (TESTB (SETNE cmp)) yes no) -> (NE cmp yes no)
-(NE (TESTB (SETB cmp)) yes no) -> (ULT cmp yes no)
-(NE (TESTB (SETBE cmp)) yes no) -> (ULE cmp yes no)
-(NE (TESTB (SETA cmp)) yes no) -> (UGT cmp yes no)
-(NE (TESTB (SETAE cmp)) yes no) -> (UGE cmp yes no)
+(NE (TESTB (SETL cmp) (SETL cmp)) yes no) -> (LT cmp yes no)
+(NE (TESTB (SETLE cmp) (SETLE cmp)) yes no) -> (LE cmp yes no)
+(NE (TESTB (SETG cmp) (SETG cmp)) yes no) -> (GT cmp yes no)
+(NE (TESTB (SETGE cmp) (SETGE cmp)) yes no) -> (GE cmp yes no)
+(NE (TESTB (SETEQ cmp) (SETEQ cmp)) yes no) -> (EQ cmp yes no)
+(NE (TESTB (SETNE cmp) (SETNE cmp)) yes no) -> (NE cmp yes no)
+(NE (TESTB (SETB cmp) (SETB cmp)) yes no) -> (ULT cmp yes no)
+(NE (TESTB (SETBE cmp) (SETBE cmp)) yes no) -> (ULE cmp yes no)
+(NE (TESTB (SETA cmp) (SETA cmp)) yes no) -> (UGT cmp yes no)
+(NE (TESTB (SETAE cmp) (SETAE cmp)) yes no) -> (UGE cmp yes no)
// Special case for floating point - LF/LEF not generated
-(NE (TESTB (SETGF cmp)) yes no) -> (UGT cmp yes no)
-(NE (TESTB (SETGEF cmp)) yes no) -> (UGE cmp yes no)
-(NE (TESTB (SETEQF cmp)) yes no) -> (EQF cmp yes no)
-(NE (TESTB (SETNEF cmp)) yes no) -> (NEF cmp yes no)
+(NE (TESTB (SETGF cmp) (SETGF cmp)) yes no) -> (UGT cmp yes no)
+(NE (TESTB (SETGEF cmp) (SETGEF cmp)) yes no) -> (UGE cmp yes no)
+(NE (TESTB (SETEQF cmp) (SETEQF cmp)) yes no) -> (EQF cmp yes no)
+(NE (TESTB (SETNEF cmp) (SETNEF cmp)) yes no) -> (NEF cmp yes no)
// Disabled because it interferes with the pattern match above and makes worse code.
// (SETNEF x) -> (ORQ (SETNE <config.Frontend().TypeInt8()> x) (SETNAN <config.Frontend().TypeInt8()> x))
@@ -439,44 +443,22 @@
(ADDQ (MOVQconst [c]) x) && is32Bit(c) -> (ADDQconst [c] x)
(ADDL x (MOVLconst [c])) -> (ADDLconst [c] x)
(ADDL (MOVLconst [c]) x) -> (ADDLconst [c] x)
-(ADDW x (MOVWconst [c])) -> (ADDWconst [c] x)
-(ADDW (MOVWconst [c]) x) -> (ADDWconst [c] x)
-(ADDB x (MOVBconst [c])) -> (ADDBconst [c] x)
-(ADDB (MOVBconst [c]) x) -> (ADDBconst [c] x)
(SUBQ x (MOVQconst [c])) && is32Bit(c) -> (SUBQconst x [c])
(SUBQ (MOVQconst [c]) x) && is32Bit(c) -> (NEGQ (SUBQconst <v.Type> x [c]))
(SUBL x (MOVLconst [c])) -> (SUBLconst x [c])
(SUBL (MOVLconst [c]) x) -> (NEGL (SUBLconst <v.Type> x [c]))
-(SUBW x (MOVWconst [c])) -> (SUBWconst x [c])
-(SUBW (MOVWconst [c]) x) -> (NEGW (SUBWconst <v.Type> x [c]))
-(SUBB x (MOVBconst [c])) -> (SUBBconst x [c])
-(SUBB (MOVBconst [c]) x) -> (NEGB (SUBBconst <v.Type> x [c]))
(MULQ x (MOVQconst [c])) && is32Bit(c) -> (MULQconst [c] x)
(MULQ (MOVQconst [c]) x) && is32Bit(c) -> (MULQconst [c] x)
(MULL x (MOVLconst [c])) -> (MULLconst [c] x)
(MULL (MOVLconst [c]) x) -> (MULLconst [c] x)
-(MULW x (MOVWconst [c])) -> (MULWconst [c] x)
-(MULW (MOVWconst [c]) x) -> (MULWconst [c] x)
-(MULB x (MOVBconst [c])) -> (MULBconst [c] x)
-(MULB (MOVBconst [c]) x) -> (MULBconst [c] x)
(ANDQ x (MOVQconst [c])) && is32Bit(c) -> (ANDQconst [c] x)
(ANDQ (MOVQconst [c]) x) && is32Bit(c) -> (ANDQconst [c] x)
(ANDL x (MOVLconst [c])) -> (ANDLconst [c] x)
(ANDL (MOVLconst [c]) x) -> (ANDLconst [c] x)
-(ANDW x (MOVLconst [c])) -> (ANDWconst [c] x)
-(ANDW (MOVLconst [c]) x) -> (ANDWconst [c] x)
-(ANDW x (MOVWconst [c])) -> (ANDWconst [c] x)
-(ANDW (MOVWconst [c]) x) -> (ANDWconst [c] x)
-(ANDB x (MOVLconst [c])) -> (ANDBconst [c] x)
-(ANDB (MOVLconst [c]) x) -> (ANDBconst [c] x)
-(ANDB x (MOVBconst [c])) -> (ANDBconst [c] x)
-(ANDB (MOVBconst [c]) x) -> (ANDBconst [c] x)
-(ANDBconst [c] (ANDBconst [d] x)) -> (ANDBconst [c & d] x)
-(ANDWconst [c] (ANDWconst [d] x)) -> (ANDWconst [c & d] x)
(ANDLconst [c] (ANDLconst [d] x)) -> (ANDLconst [c & d] x)
(ANDQconst [c] (ANDQconst [d] x)) -> (ANDQconst [c & d] x)
@@ -484,108 +466,69 @@
(ORQ (MOVQconst [c]) x) && is32Bit(c) -> (ORQconst [c] x)
(ORL x (MOVLconst [c])) -> (ORLconst [c] x)
(ORL (MOVLconst [c]) x) -> (ORLconst [c] x)
-(ORW x (MOVWconst [c])) -> (ORWconst [c] x)
-(ORW (MOVWconst [c]) x) -> (ORWconst [c] x)
-(ORB x (MOVBconst [c])) -> (ORBconst [c] x)
-(ORB (MOVBconst [c]) x) -> (ORBconst [c] x)
(XORQ x (MOVQconst [c])) && is32Bit(c) -> (XORQconst [c] x)
(XORQ (MOVQconst [c]) x) && is32Bit(c) -> (XORQconst [c] x)
(XORL x (MOVLconst [c])) -> (XORLconst [c] x)
(XORL (MOVLconst [c]) x) -> (XORLconst [c] x)
-(XORW x (MOVWconst [c])) -> (XORWconst [c] x)
-(XORW (MOVWconst [c]) x) -> (XORWconst [c] x)
-(XORB x (MOVBconst [c])) -> (XORBconst [c] x)
-(XORB (MOVBconst [c]) x) -> (XORBconst [c] x)
(SHLQ x (MOVQconst [c])) -> (SHLQconst [c&63] x)
(SHLQ x (MOVLconst [c])) -> (SHLQconst [c&63] x)
-(SHLQ x (MOVWconst [c])) -> (SHLQconst [c&63] x)
-(SHLQ x (MOVBconst [c])) -> (SHLQconst [c&63] x)
(SHLL x (MOVQconst [c])) -> (SHLLconst [c&31] x)
(SHLL x (MOVLconst [c])) -> (SHLLconst [c&31] x)
-(SHLL x (MOVWconst [c])) -> (SHLLconst [c&31] x)
-(SHLL x (MOVBconst [c])) -> (SHLLconst [c&31] x)
-
-(SHLW x (MOVQconst [c])) -> (SHLWconst [c&31] x)
-(SHLW x (MOVLconst [c])) -> (SHLWconst [c&31] x)
-(SHLW x (MOVWconst [c])) -> (SHLWconst [c&31] x)
-(SHLW x (MOVBconst [c])) -> (SHLWconst [c&31] x)
-
-(SHLB x (MOVQconst [c])) -> (SHLBconst [c&31] x)
-(SHLB x (MOVLconst [c])) -> (SHLBconst [c&31] x)
-(SHLB x (MOVWconst [c])) -> (SHLBconst [c&31] x)
-(SHLB x (MOVBconst [c])) -> (SHLBconst [c&31] x)
(SHRQ x (MOVQconst [c])) -> (SHRQconst [c&63] x)
(SHRQ x (MOVLconst [c])) -> (SHRQconst [c&63] x)
-(SHRQ x (MOVWconst [c])) -> (SHRQconst [c&63] x)
-(SHRQ x (MOVBconst [c])) -> (SHRQconst [c&63] x)
(SHRL x (MOVQconst [c])) -> (SHRLconst [c&31] x)
(SHRL x (MOVLconst [c])) -> (SHRLconst [c&31] x)
-(SHRL x (MOVWconst [c])) -> (SHRLconst [c&31] x)
-(SHRL x (MOVBconst [c])) -> (SHRLconst [c&31] x)
(SHRW x (MOVQconst [c])) -> (SHRWconst [c&31] x)
(SHRW x (MOVLconst [c])) -> (SHRWconst [c&31] x)
-(SHRW x (MOVWconst [c])) -> (SHRWconst [c&31] x)
-(SHRW x (MOVBconst [c])) -> (SHRWconst [c&31] x)
(SHRB x (MOVQconst [c])) -> (SHRBconst [c&31] x)
(SHRB x (MOVLconst [c])) -> (SHRBconst [c&31] x)
-(SHRB x (MOVWconst [c])) -> (SHRBconst [c&31] x)
-(SHRB x (MOVBconst [c])) -> (SHRBconst [c&31] x)
(SARQ x (MOVQconst [c])) -> (SARQconst [c&63] x)
(SARQ x (MOVLconst [c])) -> (SARQconst [c&63] x)
-(SARQ x (MOVWconst [c])) -> (SARQconst [c&63] x)
-(SARQ x (MOVBconst [c])) -> (SARQconst [c&63] x)
(SARL x (MOVQconst [c])) -> (SARLconst [c&31] x)
(SARL x (MOVLconst [c])) -> (SARLconst [c&31] x)
-(SARL x (MOVWconst [c])) -> (SARLconst [c&31] x)
-(SARL x (MOVBconst [c])) -> (SARLconst [c&31] x)
(SARW x (MOVQconst [c])) -> (SARWconst [c&31] x)
(SARW x (MOVLconst [c])) -> (SARWconst [c&31] x)
-(SARW x (MOVWconst [c])) -> (SARWconst [c&31] x)
-(SARW x (MOVBconst [c])) -> (SARWconst [c&31] x)
(SARB x (MOVQconst [c])) -> (SARBconst [c&31] x)
(SARB x (MOVLconst [c])) -> (SARBconst [c&31] x)
-(SARB x (MOVWconst [c])) -> (SARBconst [c&31] x)
-(SARB x (MOVBconst [c])) -> (SARBconst [c&31] x)
-(SARB x (ANDBconst [31] y)) -> (SARB x y)
-(SARW x (ANDWconst [31] y)) -> (SARW x y)
(SARL x (ANDLconst [31] y)) -> (SARL x y)
(SARQ x (ANDQconst [63] y)) -> (SARQ x y)
-(SHLB x (ANDBconst [31] y)) -> (SHLB x y)
-(SHLW x (ANDWconst [31] y)) -> (SHLW x y)
(SHLL x (ANDLconst [31] y)) -> (SHLL x y)
(SHLQ x (ANDQconst [63] y)) -> (SHLQ x y)
-(SHRB x (ANDBconst [31] y)) -> (SHRB x y)
-(SHRW x (ANDWconst [31] y)) -> (SHRW x y)
(SHRL x (ANDLconst [31] y)) -> (SHRL x y)
(SHRQ x (ANDQconst [63] y)) -> (SHRQ x y)
// Note: the word and byte shifts keep the low 5 bits (not the low 4 or 3 bits)
// because the x86 instructions are defined to use all 5 bits of the shift even
// for the small shifts. I don't think we'll ever generate a weird shift (e.g.
-// (SHLW x (MOVWconst [24])), but just in case.
+// (SHRW x (MOVLconst [24])), but just in case.
(CMPQ x (MOVQconst [c])) && is32Bit(c) -> (CMPQconst x [c])
(CMPQ (MOVQconst [c]) x) && is32Bit(c) -> (InvertFlags (CMPQconst x [c]))
(CMPL x (MOVLconst [c])) -> (CMPLconst x [c])
(CMPL (MOVLconst [c]) x) -> (InvertFlags (CMPLconst x [c]))
-(CMPW x (MOVWconst [c])) -> (CMPWconst x [c])
-(CMPW (MOVWconst [c]) x) -> (InvertFlags (CMPWconst x [c]))
-(CMPB x (MOVBconst [c])) -> (CMPBconst x [c])
-(CMPB (MOVBconst [c]) x) -> (InvertFlags (CMPBconst x [c]))
+(CMPW x (MOVLconst [c])) -> (CMPWconst x [int64(int16(c))])
+(CMPW (MOVLconst [c]) x) -> (InvertFlags (CMPWconst x [int64(int16(c))]))
+(CMPB x (MOVLconst [c])) -> (CMPBconst x [int64(int8(c))])
+(CMPB (MOVLconst [c]) x) -> (InvertFlags (CMPBconst x [int64(int8(c))]))
+
+// Using MOVBQZX instead of ANDQ is cheaper.
+(ANDQconst [0xFF] x) -> (MOVBQZX x)
+(ANDQconst [0xFFFF] x) -> (MOVWQZX x)
+(ANDQconst [0xFFFFFFFF] x) -> (MOVLQZX x)
// strength reduction
// Assumes that the following costs from https://gmplib.org/~tege/x86-timing.pdf:
@@ -684,18 +627,18 @@
// Make sure we don't combine these ops if the load has another use.
// This prevents a single load from being split into multiple loads
// which then might return different values. See test/atomicload.go.
-(MOVBQSX x:(MOVBload [off] {sym} ptr mem)) && x.Uses == 1 -> @x.Block (MOVBQSXload <v.Type> [off] {sym} ptr mem)
-(MOVBQZX x:(MOVBload [off] {sym} ptr mem)) && x.Uses == 1 -> @x.Block (MOVBload <v.Type> [off] {sym} ptr mem)
-(MOVWQSX x:(MOVWload [off] {sym} ptr mem)) && x.Uses == 1 -> @x.Block (MOVWQSXload <v.Type> [off] {sym} ptr mem)
-(MOVWQZX x:(MOVWload [off] {sym} ptr mem)) && x.Uses == 1 -> @x.Block (MOVWload <v.Type> [off] {sym} ptr mem)
-(MOVLQSX x:(MOVLload [off] {sym} ptr mem)) && x.Uses == 1 -> @x.Block (MOVLQSXload <v.Type> [off] {sym} ptr mem)
-(MOVLQZX x:(MOVLload [off] {sym} ptr mem)) && x.Uses == 1 -> @x.Block (MOVLload <v.Type> [off] {sym} ptr mem)
+(MOVBQSX x:(MOVBload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBQSXload <v.Type> [off] {sym} ptr mem)
+(MOVBQZX x:(MOVBload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBload <v.Type> [off] {sym} ptr mem)
+(MOVWQSX x:(MOVWload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWQSXload <v.Type> [off] {sym} ptr mem)
+(MOVWQZX x:(MOVWload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWload <v.Type> [off] {sym} ptr mem)
+(MOVLQSX x:(MOVLload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVLQSXload <v.Type> [off] {sym} ptr mem)
+(MOVLQZX x:(MOVLload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVLload <v.Type> [off] {sym} ptr mem)
-(MOVBQZX x:(MOVBloadidx1 [off] {sym} ptr idx mem)) && x.Uses == 1 -> @x.Block (MOVBloadidx1 <v.Type> [off] {sym} ptr idx mem)
-(MOVWQZX x:(MOVWloadidx1 [off] {sym} ptr idx mem)) && x.Uses == 1 -> @x.Block (MOVWloadidx1 <v.Type> [off] {sym} ptr idx mem)
-(MOVWQZX x:(MOVWloadidx2 [off] {sym} ptr idx mem)) && x.Uses == 1 -> @x.Block (MOVWloadidx2 <v.Type> [off] {sym} ptr idx mem)
-(MOVLQZX x:(MOVLloadidx1 [off] {sym} ptr idx mem)) && x.Uses == 1 -> @x.Block (MOVLloadidx1 <v.Type> [off] {sym} ptr idx mem)
-(MOVLQZX x:(MOVLloadidx4 [off] {sym} ptr idx mem)) && x.Uses == 1 -> @x.Block (MOVLloadidx4 <v.Type> [off] {sym} ptr idx mem)
+(MOVBQZX x:(MOVBloadidx1 [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBloadidx1 <v.Type> [off] {sym} ptr idx mem)
+(MOVWQZX x:(MOVWloadidx1 [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWloadidx1 <v.Type> [off] {sym} ptr idx mem)
+(MOVWQZX x:(MOVWloadidx2 [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWloadidx2 <v.Type> [off] {sym} ptr idx mem)
+(MOVLQZX x:(MOVLloadidx1 [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVLloadidx1 <v.Type> [off] {sym} ptr idx mem)
+(MOVLQZX x:(MOVLloadidx4 [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVLloadidx4 <v.Type> [off] {sym} ptr idx mem)
// replace load from same location as preceding store with copy
(MOVBload [off] {sym} ptr (MOVBstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
@@ -704,12 +647,12 @@
(MOVQload [off] {sym} ptr (MOVQstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
// Fold extensions and ANDs together.
-(MOVBQZX (ANDBconst [c] x)) -> (ANDQconst [c & 0xff] x)
-(MOVWQZX (ANDWconst [c] x)) -> (ANDQconst [c & 0xffff] x)
-(MOVLQZX (ANDLconst [c] x)) && c & 0x80000000 == 0 -> (ANDQconst [c & 0x7fffffff] x)
-(MOVBQSX (ANDBconst [c] x)) && c & 0x80 == 0 -> (ANDQconst [c & 0x7f] x)
-(MOVWQSX (ANDWconst [c] x)) && c & 0x8000 == 0 -> (ANDQconst [c & 0x7fff] x)
-(MOVLQSX (ANDLconst [c] x)) && c & 0x80000000 == 0 -> (ANDQconst [c & 0x7fffffff] x)
+(MOVBQZX (ANDLconst [c] x)) -> (ANDLconst [c & 0xff] x)
+(MOVWQZX (ANDLconst [c] x)) -> (ANDLconst [c & 0xffff] x)
+(MOVLQZX (ANDLconst [c] x)) -> (ANDLconst [c] x)
+(MOVBQSX (ANDLconst [c] x)) && c & 0x80 == 0 -> (ANDLconst [c & 0x7f] x)
+(MOVWQSX (ANDLconst [c] x)) && c & 0x8000 == 0 -> (ANDLconst [c & 0x7fff] x)
+(MOVLQSX (ANDLconst [c] x)) && c & 0x80000000 == 0 -> (ANDLconst [c & 0x7fffffff] x)
// Don't extend before storing
(MOVLstore [off] {sym} ptr (MOVLQSX x) mem) -> (MOVLstore [off] {sym} ptr x mem)
@@ -745,9 +688,9 @@
(MOVQstoreconst [makeValAndOff(c,off)] {sym} ptr mem)
(MOVLstore [off] {sym} ptr (MOVLconst [c]) mem) && validOff(off) ->
(MOVLstoreconst [makeValAndOff(int64(int32(c)),off)] {sym} ptr mem)
-(MOVWstore [off] {sym} ptr (MOVWconst [c]) mem) && validOff(off) ->
+(MOVWstore [off] {sym} ptr (MOVLconst [c]) mem) && validOff(off) ->
(MOVWstoreconst [makeValAndOff(int64(int16(c)),off)] {sym} ptr mem)
-(MOVBstore [off] {sym} ptr (MOVBconst [c]) mem) && validOff(off) ->
+(MOVBstore [off] {sym} ptr (MOVLconst [c]) mem) && validOff(off) ->
(MOVBstoreconst [makeValAndOff(int64(int8(c)),off)] {sym} ptr mem)
// Fold address offsets into constant stores.
@@ -1081,22 +1024,27 @@
(CMPLconst (MOVLconst [x]) [y]) && int32(x)<int32(y) && uint32(x)>uint32(y) -> (FlagLT_UGT)
(CMPLconst (MOVLconst [x]) [y]) && int32(x)>int32(y) && uint32(x)<uint32(y) -> (FlagGT_ULT)
(CMPLconst (MOVLconst [x]) [y]) && int32(x)>int32(y) && uint32(x)>uint32(y) -> (FlagGT_UGT)
-(CMPWconst (MOVWconst [x]) [y]) && int16(x)==int16(y) -> (FlagEQ)
-(CMPWconst (MOVWconst [x]) [y]) && int16(x)<int16(y) && uint16(x)<uint16(y) -> (FlagLT_ULT)
-(CMPWconst (MOVWconst [x]) [y]) && int16(x)<int16(y) && uint16(x)>uint16(y) -> (FlagLT_UGT)
-(CMPWconst (MOVWconst [x]) [y]) && int16(x)>int16(y) && uint16(x)<uint16(y) -> (FlagGT_ULT)
-(CMPWconst (MOVWconst [x]) [y]) && int16(x)>int16(y) && uint16(x)>uint16(y) -> (FlagGT_UGT)
-(CMPBconst (MOVBconst [x]) [y]) && int8(x)==int8(y) -> (FlagEQ)
-(CMPBconst (MOVBconst [x]) [y]) && int8(x)<int8(y) && uint8(x)<uint8(y) -> (FlagLT_ULT)
-(CMPBconst (MOVBconst [x]) [y]) && int8(x)<int8(y) && uint8(x)>uint8(y) -> (FlagLT_UGT)
-(CMPBconst (MOVBconst [x]) [y]) && int8(x)>int8(y) && uint8(x)<uint8(y) -> (FlagGT_ULT)
-(CMPBconst (MOVBconst [x]) [y]) && int8(x)>int8(y) && uint8(x)>uint8(y) -> (FlagGT_UGT)
+(CMPWconst (MOVLconst [x]) [y]) && int16(x)==int16(y) -> (FlagEQ)
+(CMPWconst (MOVLconst [x]) [y]) && int16(x)<int16(y) && uint16(x)<uint16(y) -> (FlagLT_ULT)
+(CMPWconst (MOVLconst [x]) [y]) && int16(x)<int16(y) && uint16(x)>uint16(y) -> (FlagLT_UGT)
+(CMPWconst (MOVLconst [x]) [y]) && int16(x)>int16(y) && uint16(x)<uint16(y) -> (FlagGT_ULT)
+(CMPWconst (MOVLconst [x]) [y]) && int16(x)>int16(y) && uint16(x)>uint16(y) -> (FlagGT_UGT)
+(CMPBconst (MOVLconst [x]) [y]) && int8(x)==int8(y) -> (FlagEQ)
+(CMPBconst (MOVLconst [x]) [y]) && int8(x)<int8(y) && uint8(x)<uint8(y) -> (FlagLT_ULT)
+(CMPBconst (MOVLconst [x]) [y]) && int8(x)<int8(y) && uint8(x)>uint8(y) -> (FlagLT_UGT)
+(CMPBconst (MOVLconst [x]) [y]) && int8(x)>int8(y) && uint8(x)<uint8(y) -> (FlagGT_ULT)
+(CMPBconst (MOVLconst [x]) [y]) && int8(x)>int8(y) && uint8(x)>uint8(y) -> (FlagGT_UGT)
// Other known comparisons.
+(CMPQconst (MOVBQZX _) [c]) && 0xFF < c -> (FlagLT_ULT)
+(CMPQconst (MOVWQZX _) [c]) && 0xFFFF < c -> (FlagLT_ULT)
+(CMPQconst (MOVLQZX _) [c]) && 0xFFFFFFFF < c -> (FlagLT_ULT)
+(CMPLconst (SHRLconst _ [c]) [n]) && 0 <= n && 0 < c && c <= 32 && (1<<uint64(32-c)) <= uint64(n) -> (FlagLT_ULT)
+(CMPQconst (SHRQconst _ [c]) [n]) && 0 <= n && 0 < c && c <= 64 && (1<<uint64(64-c)) <= uint64(n) -> (FlagLT_ULT)
(CMPQconst (ANDQconst _ [m]) [n]) && 0 <= m && m < n -> (FlagLT_ULT)
(CMPLconst (ANDLconst _ [m]) [n]) && 0 <= int32(m) && int32(m) < int32(n) -> (FlagLT_ULT)
-(CMPWconst (ANDWconst _ [m]) [n]) && 0 <= int16(m) && int16(m) < int16(n) -> (FlagLT_ULT)
-(CMPBconst (ANDBconst _ [m]) [n]) && 0 <= int8(m) && int8(m) < int8(n) -> (FlagLT_ULT)
+(CMPWconst (ANDLconst _ [m]) [n]) && 0 <= int16(m) && int16(m) < int16(n) -> (FlagLT_ULT)
+(CMPBconst (ANDLconst _ [m]) [n]) && 0 <= int8(m) && int8(m) < int8(n) -> (FlagLT_ULT)
// TODO: DIVxU also.
// Absorb flag constants into SBB ops.
@@ -1173,175 +1121,140 @@
(UGE (FlagGT_UGT) yes no) -> (First nil yes no)
// Absorb flag constants into SETxx ops.
-(SETEQ (FlagEQ)) -> (MOVBconst [1])
-(SETEQ (FlagLT_ULT)) -> (MOVBconst [0])
-(SETEQ (FlagLT_UGT)) -> (MOVBconst [0])
-(SETEQ (FlagGT_ULT)) -> (MOVBconst [0])
-(SETEQ (FlagGT_UGT)) -> (MOVBconst [0])
+(SETEQ (FlagEQ)) -> (MOVLconst [1])
+(SETEQ (FlagLT_ULT)) -> (MOVLconst [0])
+(SETEQ (FlagLT_UGT)) -> (MOVLconst [0])
+(SETEQ (FlagGT_ULT)) -> (MOVLconst [0])
+(SETEQ (FlagGT_UGT)) -> (MOVLconst [0])
-(SETNE (FlagEQ)) -> (MOVBconst [0])
-(SETNE (FlagLT_ULT)) -> (MOVBconst [1])
-(SETNE (FlagLT_UGT)) -> (MOVBconst [1])
-(SETNE (FlagGT_ULT)) -> (MOVBconst [1])
-(SETNE (FlagGT_UGT)) -> (MOVBconst [1])
+(SETNE (FlagEQ)) -> (MOVLconst [0])
+(SETNE (FlagLT_ULT)) -> (MOVLconst [1])
+(SETNE (FlagLT_UGT)) -> (MOVLconst [1])
+(SETNE (FlagGT_ULT)) -> (MOVLconst [1])
+(SETNE (FlagGT_UGT)) -> (MOVLconst [1])
-(SETL (FlagEQ)) -> (MOVBconst [0])
-(SETL (FlagLT_ULT)) -> (MOVBconst [1])
-(SETL (FlagLT_UGT)) -> (MOVBconst [1])
-(SETL (FlagGT_ULT)) -> (MOVBconst [0])
-(SETL (FlagGT_UGT)) -> (MOVBconst [0])
+(SETL (FlagEQ)) -> (MOVLconst [0])
+(SETL (FlagLT_ULT)) -> (MOVLconst [1])
+(SETL (FlagLT_UGT)) -> (MOVLconst [1])
+(SETL (FlagGT_ULT)) -> (MOVLconst [0])
+(SETL (FlagGT_UGT)) -> (MOVLconst [0])
-(SETLE (FlagEQ)) -> (MOVBconst [1])
-(SETLE (FlagLT_ULT)) -> (MOVBconst [1])
-(SETLE (FlagLT_UGT)) -> (MOVBconst [1])
-(SETLE (FlagGT_ULT)) -> (MOVBconst [0])
-(SETLE (FlagGT_UGT)) -> (MOVBconst [0])
+(SETLE (FlagEQ)) -> (MOVLconst [1])
+(SETLE (FlagLT_ULT)) -> (MOVLconst [1])
+(SETLE (FlagLT_UGT)) -> (MOVLconst [1])
+(SETLE (FlagGT_ULT)) -> (MOVLconst [0])
+(SETLE (FlagGT_UGT)) -> (MOVLconst [0])
-(SETG (FlagEQ)) -> (MOVBconst [0])
-(SETG (FlagLT_ULT)) -> (MOVBconst [0])
-(SETG (FlagLT_UGT)) -> (MOVBconst [0])
-(SETG (FlagGT_ULT)) -> (MOVBconst [1])
-(SETG (FlagGT_UGT)) -> (MOVBconst [1])
+(SETG (FlagEQ)) -> (MOVLconst [0])
+(SETG (FlagLT_ULT)) -> (MOVLconst [0])
+(SETG (FlagLT_UGT)) -> (MOVLconst [0])
+(SETG (FlagGT_ULT)) -> (MOVLconst [1])
+(SETG (FlagGT_UGT)) -> (MOVLconst [1])
-(SETGE (FlagEQ)) -> (MOVBconst [1])
-(SETGE (FlagLT_ULT)) -> (MOVBconst [0])
-(SETGE (FlagLT_UGT)) -> (MOVBconst [0])
-(SETGE (FlagGT_ULT)) -> (MOVBconst [1])
-(SETGE (FlagGT_UGT)) -> (MOVBconst [1])
+(SETGE (FlagEQ)) -> (MOVLconst [1])
+(SETGE (FlagLT_ULT)) -> (MOVLconst [0])
+(SETGE (FlagLT_UGT)) -> (MOVLconst [0])
+(SETGE (FlagGT_ULT)) -> (MOVLconst [1])
+(SETGE (FlagGT_UGT)) -> (MOVLconst [1])
-(SETB (FlagEQ)) -> (MOVBconst [0])
-(SETB (FlagLT_ULT)) -> (MOVBconst [1])
-(SETB (FlagLT_UGT)) -> (MOVBconst [0])
-(SETB (FlagGT_ULT)) -> (MOVBconst [1])
-(SETB (FlagGT_UGT)) -> (MOVBconst [0])
+(SETB (FlagEQ)) -> (MOVLconst [0])
+(SETB (FlagLT_ULT)) -> (MOVLconst [1])
+(SETB (FlagLT_UGT)) -> (MOVLconst [0])
+(SETB (FlagGT_ULT)) -> (MOVLconst [1])
+(SETB (FlagGT_UGT)) -> (MOVLconst [0])
-(SETBE (FlagEQ)) -> (MOVBconst [1])
-(SETBE (FlagLT_ULT)) -> (MOVBconst [1])
-(SETBE (FlagLT_UGT)) -> (MOVBconst [0])
-(SETBE (FlagGT_ULT)) -> (MOVBconst [1])
-(SETBE (FlagGT_UGT)) -> (MOVBconst [0])
+(SETBE (FlagEQ)) -> (MOVLconst [1])
+(SETBE (FlagLT_ULT)) -> (MOVLconst [1])
+(SETBE (FlagLT_UGT)) -> (MOVLconst [0])
+(SETBE (FlagGT_ULT)) -> (MOVLconst [1])
+(SETBE (FlagGT_UGT)) -> (MOVLconst [0])
-(SETA (FlagEQ)) -> (MOVBconst [0])
-(SETA (FlagLT_ULT)) -> (MOVBconst [0])
-(SETA (FlagLT_UGT)) -> (MOVBconst [1])
-(SETA (FlagGT_ULT)) -> (MOVBconst [0])
-(SETA (FlagGT_UGT)) -> (MOVBconst [1])
+(SETA (FlagEQ)) -> (MOVLconst [0])
+(SETA (FlagLT_ULT)) -> (MOVLconst [0])
+(SETA (FlagLT_UGT)) -> (MOVLconst [1])
+(SETA (FlagGT_ULT)) -> (MOVLconst [0])
+(SETA (FlagGT_UGT)) -> (MOVLconst [1])
-(SETAE (FlagEQ)) -> (MOVBconst [1])
-(SETAE (FlagLT_ULT)) -> (MOVBconst [0])
-(SETAE (FlagLT_UGT)) -> (MOVBconst [1])
-(SETAE (FlagGT_ULT)) -> (MOVBconst [0])
-(SETAE (FlagGT_UGT)) -> (MOVBconst [1])
+(SETAE (FlagEQ)) -> (MOVLconst [1])
+(SETAE (FlagLT_ULT)) -> (MOVLconst [0])
+(SETAE (FlagLT_UGT)) -> (MOVLconst [1])
+(SETAE (FlagGT_ULT)) -> (MOVLconst [0])
+(SETAE (FlagGT_UGT)) -> (MOVLconst [1])
// Remove redundant *const ops
(ADDQconst [0] x) -> x
(ADDLconst [c] x) && int32(c)==0 -> x
-(ADDWconst [c] x) && int16(c)==0 -> x
-(ADDBconst [c] x) && int8(c)==0 -> x
(SUBQconst [0] x) -> x
(SUBLconst [c] x) && int32(c) == 0 -> x
-(SUBWconst [c] x) && int16(c) == 0 -> x
-(SUBBconst [c] x) && int8(c) == 0 -> x
(ANDQconst [0] _) -> (MOVQconst [0])
(ANDLconst [c] _) && int32(c)==0 -> (MOVLconst [0])
-(ANDWconst [c] _) && int16(c)==0 -> (MOVWconst [0])
-(ANDBconst [c] _) && int8(c)==0 -> (MOVBconst [0])
(ANDQconst [-1] x) -> x
(ANDLconst [c] x) && int32(c)==-1 -> x
-(ANDWconst [c] x) && int16(c)==-1 -> x
-(ANDBconst [c] x) && int8(c)==-1 -> x
(ORQconst [0] x) -> x
(ORLconst [c] x) && int32(c)==0 -> x
-(ORWconst [c] x) && int16(c)==0 -> x
-(ORBconst [c] x) && int8(c)==0 -> x
(ORQconst [-1] _) -> (MOVQconst [-1])
(ORLconst [c] _) && int32(c)==-1 -> (MOVLconst [-1])
-(ORWconst [c] _) && int16(c)==-1 -> (MOVWconst [-1])
-(ORBconst [c] _) && int8(c)==-1 -> (MOVBconst [-1])
(XORQconst [0] x) -> x
(XORLconst [c] x) && int32(c)==0 -> x
-(XORWconst [c] x) && int16(c)==0 -> x
-(XORBconst [c] x) && int8(c)==0 -> x
+// TODO: since we got rid of the W/B versions, we might miss
+// things like (ANDLconst [0x100] x) which were formerly
+// (ANDBconst [0] x). Probably doesn't happen very often.
+// If we cared, we might do:
+// (ANDLconst <t> [c] x) && t.Size()==1 && int8(x)==0 -> (MOVLconst [0])
+
+// Convert constant subtracts to constant adds
+(SUBQconst [c] x) && c != -(1<<31) -> (ADDQconst [-c] x)
+(SUBLconst [c] x) -> (ADDLconst [int64(int32(-c))] x)
// generic constant folding
// TODO: more of this
(ADDQconst [c] (MOVQconst [d])) -> (MOVQconst [c+d])
(ADDLconst [c] (MOVLconst [d])) -> (MOVLconst [int64(int32(c+d))])
-(ADDWconst [c] (MOVWconst [d])) -> (MOVWconst [int64(int16(c+d))])
-(ADDBconst [c] (MOVBconst [d])) -> (MOVBconst [int64(int8(c+d))])
(ADDQconst [c] (ADDQconst [d] x)) && is32Bit(c+d) -> (ADDQconst [c+d] x)
(ADDLconst [c] (ADDLconst [d] x)) -> (ADDLconst [int64(int32(c+d))] x)
-(ADDWconst [c] (ADDWconst [d] x)) -> (ADDWconst [int64(int16(c+d))] x)
-(ADDBconst [c] (ADDBconst [d] x)) -> (ADDBconst [int64(int8(c+d))] x)
(SUBQconst (MOVQconst [d]) [c]) -> (MOVQconst [d-c])
(SUBLconst (MOVLconst [d]) [c]) -> (MOVLconst [int64(int32(d-c))])
-(SUBWconst (MOVWconst [d]) [c]) -> (MOVWconst [int64(int16(d-c))])
-(SUBBconst (MOVBconst [d]) [c]) -> (MOVBconst [int64(int8(d-c))])
(SUBQconst (SUBQconst x [d]) [c]) && is32Bit(-c-d) -> (ADDQconst [-c-d] x)
(SUBLconst (SUBLconst x [d]) [c]) -> (ADDLconst [int64(int32(-c-d))] x)
-(SUBWconst (SUBWconst x [d]) [c]) -> (ADDWconst [int64(int16(-c-d))] x)
-(SUBBconst (SUBBconst x [d]) [c]) -> (ADDBconst [int64(int8(-c-d))] x)
(SARQconst [c] (MOVQconst [d])) -> (MOVQconst [d>>uint64(c)])
(SARLconst [c] (MOVQconst [d])) -> (MOVQconst [d>>uint64(c)])
(SARWconst [c] (MOVQconst [d])) -> (MOVQconst [d>>uint64(c)])
(SARBconst [c] (MOVQconst [d])) -> (MOVQconst [d>>uint64(c)])
(NEGQ (MOVQconst [c])) -> (MOVQconst [-c])
(NEGL (MOVLconst [c])) -> (MOVLconst [int64(int32(-c))])
-(NEGW (MOVWconst [c])) -> (MOVWconst [int64(int16(-c))])
-(NEGB (MOVBconst [c])) -> (MOVBconst [int64(int8(-c))])
(MULQconst [c] (MOVQconst [d])) -> (MOVQconst [c*d])
(MULLconst [c] (MOVLconst [d])) -> (MOVLconst [int64(int32(c*d))])
-(MULWconst [c] (MOVWconst [d])) -> (MOVWconst [int64(int16(c*d))])
-(MULBconst [c] (MOVBconst [d])) -> (MOVBconst [int64(int8(c*d))])
(ANDQconst [c] (MOVQconst [d])) -> (MOVQconst [c&d])
(ANDLconst [c] (MOVLconst [d])) -> (MOVLconst [c&d])
-(ANDWconst [c] (MOVWconst [d])) -> (MOVWconst [c&d])
-(ANDBconst [c] (MOVBconst [d])) -> (MOVBconst [c&d])
(ORQconst [c] (MOVQconst [d])) -> (MOVQconst [c|d])
(ORLconst [c] (MOVLconst [d])) -> (MOVLconst [c|d])
-(ORWconst [c] (MOVWconst [d])) -> (MOVWconst [c|d])
-(ORBconst [c] (MOVBconst [d])) -> (MOVBconst [c|d])
(XORQconst [c] (MOVQconst [d])) -> (MOVQconst [c^d])
(XORLconst [c] (MOVLconst [d])) -> (MOVLconst [c^d])
-(XORWconst [c] (MOVWconst [d])) -> (MOVWconst [c^d])
-(XORBconst [c] (MOVBconst [d])) -> (MOVBconst [c^d])
(NOTQ (MOVQconst [c])) -> (MOVQconst [^c])
(NOTL (MOVLconst [c])) -> (MOVLconst [^c])
-(NOTW (MOVWconst [c])) -> (MOVWconst [^c])
-(NOTB (MOVBconst [c])) -> (MOVBconst [^c])
// generic simplifications
// TODO: more of this
(ADDQ x (NEGQ y)) -> (SUBQ x y)
(ADDL x (NEGL y)) -> (SUBL x y)
-(ADDW x (NEGW y)) -> (SUBW x y)
-(ADDB x (NEGB y)) -> (SUBB x y)
(SUBQ x x) -> (MOVQconst [0])
(SUBL x x) -> (MOVLconst [0])
-(SUBW x x) -> (MOVWconst [0])
-(SUBB x x) -> (MOVBconst [0])
(ANDQ x x) -> x
(ANDL x x) -> x
-(ANDW x x) -> x
-(ANDB x x) -> x
(ORQ x x) -> x
(ORL x x) -> x
-(ORW x x) -> x
-(ORB x x) -> x
(XORQ x x) -> (MOVQconst [0])
(XORL x x) -> (MOVLconst [0])
-(XORW x x) -> (MOVWconst [0])
-(XORB x x) -> (MOVBconst [0])
// checking AND against 0.
(CMPQconst (ANDQ x y) [0]) -> (TESTQ x y)
(CMPLconst (ANDL x y) [0]) -> (TESTL x y)
-(CMPWconst (ANDW x y) [0]) -> (TESTW x y)
-(CMPBconst (ANDB x y) [0]) -> (TESTB x y)
+(CMPWconst (ANDL x y) [0]) -> (TESTW x y)
+(CMPBconst (ANDL x y) [0]) -> (TESTB x y)
(CMPQconst (ANDQconst [c] x) [0]) -> (TESTQconst [c] x)
(CMPLconst (ANDLconst [c] x) [0]) -> (TESTLconst [c] x)
-(CMPWconst (ANDWconst [c] x) [0]) -> (TESTWconst [c] x)
-(CMPBconst (ANDBconst [c] x) [0]) -> (TESTBconst [c] x)
+(CMPWconst (ANDLconst [c] x) [0]) -> (TESTWconst [int64(int16(c))] x)
+(CMPBconst (ANDLconst [c] x) [0]) -> (TESTBconst [int64(int8(c))] x)
// TEST %reg,%reg is shorter than CMP
(CMPQconst x [0]) -> (TESTQ x x)
@@ -1352,40 +1265,296 @@
// Combining byte loads into larger (unaligned) loads.
// There are many ways these combinations could occur. This is
// designed to match the way encoding/binary.LittleEndian does it.
-(ORW x0:(MOVBload [i] {s} p mem)
- (SHLWconst [8] x1:(MOVBload [i+1] {s} p mem))) && mergePoint(b,x0,x1) != nil -> @mergePoint(b,x0,x1) (MOVWload [i] {s} p mem)
+(ORL x0:(MOVBload [i] {s} p mem)
+ s0:(SHLLconst [8] x1:(MOVBload [i+1] {s} p mem)))
+ && x0.Uses == 1
+ && x1.Uses == 1
+ && s0.Uses == 1
+ && mergePoint(b,x0,x1) != nil
+ && clobber(x0)
+ && clobber(x1)
+ && clobber(s0)
+ -> @mergePoint(b,x0,x1) (MOVWload [i] {s} p mem)
+
+(ORL o0:(ORL o1:(ORL
+ x0:(MOVBload [i] {s} p mem)
+ s0:(SHLLconst [8] x1:(MOVBload [i+1] {s} p mem)))
+ s1:(SHLLconst [16] x2:(MOVBload [i+2] {s} p mem)))
+ s2:(SHLLconst [24] x3:(MOVBload [i+3] {s} p mem)))
+ && x0.Uses == 1
+ && x1.Uses == 1
+ && x2.Uses == 1
+ && x3.Uses == 1
+ && s0.Uses == 1
+ && s1.Uses == 1
+ && s2.Uses == 1
+ && o0.Uses == 1
+ && o1.Uses == 1
+ && mergePoint(b,x0,x1,x2,x3) != nil
+ && clobber(x0)
+ && clobber(x1)
+ && clobber(x2)
+ && clobber(x3)
+ && clobber(s0)
+ && clobber(s1)
+ && clobber(s2)
+ && clobber(o0)
+ && clobber(o1)
+ -> @mergePoint(b,x0,x1,x2,x3) (MOVLload [i] {s} p mem)
+
+(ORQ o0:(ORQ o1:(ORQ o2:(ORQ o3:(ORQ o4:(ORQ o5:(ORQ
+ x0:(MOVBload [i] {s} p mem)
+ s0:(SHLQconst [8] x1:(MOVBload [i+1] {s} p mem)))
+ s1:(SHLQconst [16] x2:(MOVBload [i+2] {s} p mem)))
+ s2:(SHLQconst [24] x3:(MOVBload [i+3] {s} p mem)))
+ s3:(SHLQconst [32] x4:(MOVBload [i+4] {s} p mem)))
+ s4:(SHLQconst [40] x5:(MOVBload [i+5] {s} p mem)))
+ s5:(SHLQconst [48] x6:(MOVBload [i+6] {s} p mem)))
+ s6:(SHLQconst [56] x7:(MOVBload [i+7] {s} p mem)))
+ && x0.Uses == 1
+ && x1.Uses == 1
+ && x2.Uses == 1
+ && x3.Uses == 1
+ && x4.Uses == 1
+ && x5.Uses == 1
+ && x6.Uses == 1
+ && x7.Uses == 1
+ && s0.Uses == 1
+ && s1.Uses == 1
+ && s2.Uses == 1
+ && s3.Uses == 1
+ && s4.Uses == 1
+ && s5.Uses == 1
+ && s6.Uses == 1
+ && o0.Uses == 1
+ && o1.Uses == 1
+ && o2.Uses == 1
+ && o3.Uses == 1
+ && o4.Uses == 1
+ && o5.Uses == 1
+ && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil
+ && clobber(x0)
+ && clobber(x1)
+ && clobber(x2)
+ && clobber(x3)
+ && clobber(x4)
+ && clobber(x5)
+ && clobber(x6)
+ && clobber(x7)
+ && clobber(s0)
+ && clobber(s1)
+ && clobber(s2)
+ && clobber(s3)
+ && clobber(s4)
+ && clobber(s5)
+ && clobber(s6)
+ && clobber(o0)
+ && clobber(o1)
+ && clobber(o2)
+ && clobber(o3)
+ && clobber(o4)
+ && clobber(o5)
+ -> @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (MOVQload [i] {s} p mem)
+
+(ORL x0:(MOVBloadidx1 [i] {s} p idx mem)
+ s0:(SHLLconst [8] x1:(MOVBloadidx1 [i+1] {s} p idx mem)))
+ && x0.Uses == 1
+ && x1.Uses == 1
+ && s0.Uses == 1
+ && mergePoint(b,x0,x1) != nil
+ && clobber(x0)
+ && clobber(x1)
+ && clobber(s0)
+ -> @mergePoint(b,x0,x1) (MOVWloadidx1 <v.Type> [i] {s} p idx mem)
+
+(ORL o0:(ORL o1:(ORL
+ x0:(MOVBloadidx1 [i] {s} p idx mem)
+ s0:(SHLLconst [8] x1:(MOVBloadidx1 [i+1] {s} p idx mem)))
+ s1:(SHLLconst [16] x2:(MOVBloadidx1 [i+2] {s} p idx mem)))
+ s2:(SHLLconst [24] x3:(MOVBloadidx1 [i+3] {s} p idx mem)))
+ && x0.Uses == 1
+ && x1.Uses == 1
+ && x2.Uses == 1
+ && x3.Uses == 1
+ && s0.Uses == 1
+ && s1.Uses == 1
+ && s2.Uses == 1
+ && o0.Uses == 1
+ && o1.Uses == 1
+ && mergePoint(b,x0,x1,x2,x3) != nil
+ && clobber(x0)
+ && clobber(x1)
+ && clobber(x2)
+ && clobber(x3)
+ && clobber(s0)
+ && clobber(s1)
+ && clobber(s2)
+ && clobber(o0)
+ && clobber(o1)
+ -> @mergePoint(b,x0,x1,x2,x3) (MOVLloadidx1 <v.Type> [i] {s} p idx mem)
+
+(ORQ o0:(ORQ o1:(ORQ o2:(ORQ o3:(ORQ o4:(ORQ o5:(ORQ
+ x0:(MOVBloadidx1 [i] {s} p idx mem)
+ s0:(SHLQconst [8] x1:(MOVBloadidx1 [i+1] {s} p idx mem)))
+ s1:(SHLQconst [16] x2:(MOVBloadidx1 [i+2] {s} p idx mem)))
+ s2:(SHLQconst [24] x3:(MOVBloadidx1 [i+3] {s} p idx mem)))
+ s3:(SHLQconst [32] x4:(MOVBloadidx1 [i+4] {s} p idx mem)))
+ s4:(SHLQconst [40] x5:(MOVBloadidx1 [i+5] {s} p idx mem)))
+ s5:(SHLQconst [48] x6:(MOVBloadidx1 [i+6] {s} p idx mem)))
+ s6:(SHLQconst [56] x7:(MOVBloadidx1 [i+7] {s} p idx mem)))
+ && x0.Uses == 1
+ && x1.Uses == 1
+ && x2.Uses == 1
+ && x3.Uses == 1
+ && x4.Uses == 1
+ && x5.Uses == 1
+ && x6.Uses == 1
+ && x7.Uses == 1
+ && s0.Uses == 1
+ && s1.Uses == 1
+ && s2.Uses == 1
+ && s3.Uses == 1
+ && s4.Uses == 1
+ && s5.Uses == 1
+ && s6.Uses == 1
+ && o0.Uses == 1
+ && o1.Uses == 1
+ && o2.Uses == 1
+ && o3.Uses == 1
+ && o4.Uses == 1
+ && o5.Uses == 1
+ && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil
+ && clobber(x0)
+ && clobber(x1)
+ && clobber(x2)
+ && clobber(x3)
+ && clobber(x4)
+ && clobber(x5)
+ && clobber(x6)
+ && clobber(x7)
+ && clobber(s0)
+ && clobber(s1)
+ && clobber(s2)
+ && clobber(s3)
+ && clobber(s4)
+ && clobber(s5)
+ && clobber(s6)
+ && clobber(o0)
+ && clobber(o1)
+ && clobber(o2)
+ && clobber(o3)
+ && clobber(o4)
+ && clobber(o5)
+ -> @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (MOVQloadidx1 <v.Type> [i] {s} p idx mem)
+
+// Combine constant stores into larger (unaligned) stores.
+(MOVBstoreconst [c] {s} p x:(MOVBstoreconst [a] {s} p mem))
+ && x.Uses == 1
+ && ValAndOff(a).Off() + 1 == ValAndOff(c).Off()
+ && clobber(x)
+ -> (MOVWstoreconst [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p mem)
+(MOVWstoreconst [c] {s} p x:(MOVWstoreconst [a] {s} p mem))
+ && x.Uses == 1
+ && ValAndOff(a).Off() + 2 == ValAndOff(c).Off()
+ && clobber(x)
+ -> (MOVLstoreconst [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p mem)
+(MOVLstoreconst [c] {s} p x:(MOVLstoreconst [a] {s} p mem))
+ && x.Uses == 1
+ && ValAndOff(a).Off() + 4 == ValAndOff(c).Off()
+ && clobber(x)
+ -> (MOVQstore [ValAndOff(a).Off()] {s} p (MOVQconst [ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32]) mem)
-(ORL (ORL (ORL
- x0:(MOVBload [i] {s} p mem)
- (SHLLconst [8] x1:(MOVBload [i+1] {s} p mem)))
- (SHLLconst [16] x2:(MOVBload [i+2] {s} p mem)))
- (SHLLconst [24] x3:(MOVBload [i+3] {s} p mem))) && mergePoint(b,x0,x1,x2,x3) != nil -> @mergePoint(b,x0,x1,x2,x3) (MOVLload [i] {s} p mem)
+(MOVBstoreconstidx1 [c] {s} p i x:(MOVBstoreconstidx1 [a] {s} p i mem))
+ && x.Uses == 1
+ && ValAndOff(a).Off() + 1 == ValAndOff(c).Off()
+ && clobber(x)
+ -> (MOVWstoreconstidx1 [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p i mem)
+(MOVWstoreconstidx1 [c] {s} p i x:(MOVWstoreconstidx1 [a] {s} p i mem))
+ && x.Uses == 1
+ && ValAndOff(a).Off() + 2 == ValAndOff(c).Off()
+ && clobber(x)
+ -> (MOVLstoreconstidx1 [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p i mem)
+(MOVLstoreconstidx1 [c] {s} p i x:(MOVLstoreconstidx1 [a] {s} p i mem))
+ && x.Uses == 1
+ && ValAndOff(a).Off() + 4 == ValAndOff(c).Off()
+ && clobber(x)
+ -> (MOVQstoreidx1 [ValAndOff(a).Off()] {s} p i (MOVQconst [ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32]) mem)
-(ORQ (ORQ (ORQ (ORQ (ORQ (ORQ (ORQ
- x0:(MOVBload [i] {s} p mem)
- (SHLQconst [8] x1:(MOVBload [i+1] {s} p mem)))
- (SHLQconst [16] x2:(MOVBload [i+2] {s} p mem)))
- (SHLQconst [24] x3:(MOVBload [i+3] {s} p mem)))
- (SHLQconst [32] x4:(MOVBload [i+4] {s} p mem)))
- (SHLQconst [40] x5:(MOVBload [i+5] {s} p mem)))
- (SHLQconst [48] x6:(MOVBload [i+6] {s} p mem)))
- (SHLQconst [56] x7:(MOVBload [i+7] {s} p mem))) && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil -> @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (MOVQload [i] {s} p mem)
+(MOVWstoreconstidx2 [c] {s} p i x:(MOVWstoreconstidx2 [a] {s} p i mem))
+ && x.Uses == 1
+ && ValAndOff(a).Off() + 2 == ValAndOff(c).Off()
+ && clobber(x)
+ -> (MOVLstoreconstidx1 [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p (SHLQconst <i.Type> [1] i) mem)
+(MOVLstoreconstidx4 [c] {s} p i x:(MOVLstoreconstidx4 [a] {s} p i mem))
+ && x.Uses == 1
+ && ValAndOff(a).Off() + 4 == ValAndOff(c).Off()
+ && clobber(x)
+ -> (MOVQstoreidx1 [ValAndOff(a).Off()] {s} p (SHLQconst <i.Type> [2] i) (MOVQconst [ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32]) mem)
-(ORW x0:(MOVBloadidx1 [i] {s} p idx mem)
- (SHLWconst [8] x1:(MOVBloadidx1 [i+1] {s} p idx mem))) && mergePoint(b,x0,x1) != nil -> @mergePoint(b,x0,x1) (MOVWloadidx1 <v.Type> [i] {s} p idx mem)
+// Combine stores into larger (unaligned) stores.
+(MOVBstore [i] {s} p (SHRQconst [8] w) x:(MOVBstore [i-1] {s} p w mem))
+ && x.Uses == 1
+ && clobber(x)
+ -> (MOVWstore [i-1] {s} p w mem)
+(MOVBstore [i] {s} p (SHRQconst [j] w) x:(MOVBstore [i-1] {s} p w0:(SHRQconst [j-8] w) mem))
+ && x.Uses == 1
+ && clobber(x)
+ -> (MOVWstore [i-1] {s} p w0 mem)
+(MOVWstore [i] {s} p (SHRQconst [16] w) x:(MOVWstore [i-2] {s} p w mem))
+ && x.Uses == 1
+ && clobber(x)
+ -> (MOVLstore [i-2] {s} p w mem)
+(MOVWstore [i] {s} p (SHRQconst [j] w) x:(MOVWstore [i-2] {s} p w0:(SHRQconst [j-16] w) mem))
+ && x.Uses == 1
+ && clobber(x)
+ -> (MOVLstore [i-2] {s} p w0 mem)
+(MOVLstore [i] {s} p (SHRQconst [32] w) x:(MOVLstore [i-4] {s} p w mem))
+ && x.Uses == 1
+ && clobber(x)
+ -> (MOVQstore [i-4] {s} p w mem)
+(MOVLstore [i] {s} p (SHRQconst [j] w) x:(MOVLstore [i-4] {s} p w0:(SHRQconst [j-32] w) mem))
+ && x.Uses == 1
+ && clobber(x)
+ -> (MOVQstore [i-4] {s} p w0 mem)
-(ORL (ORL (ORL
- x0:(MOVBloadidx1 [i] {s} p idx mem)
- (SHLLconst [8] x1:(MOVBloadidx1 [i+1] {s} p idx mem)))
- (SHLLconst [16] x2:(MOVBloadidx1 [i+2] {s} p idx mem)))
- (SHLLconst [24] x3:(MOVBloadidx1 [i+3] {s} p idx mem))) && mergePoint(b,x0,x1,x2,x3) != nil -> @mergePoint(b,x0,x1,x2,x3) (MOVLloadidx1 <v.Type> [i] {s} p idx mem)
+(MOVBstoreidx1 [i] {s} p idx (SHRQconst [8] w) x:(MOVBstoreidx1 [i-1] {s} p idx w mem))
+ && x.Uses == 1
+ && clobber(x)
+ -> (MOVWstoreidx1 [i-1] {s} p idx w mem)
+(MOVBstoreidx1 [i] {s} p idx (SHRQconst [j] w) x:(MOVBstoreidx1 [i-1] {s} p idx w0:(SHRQconst [j-8] w) mem))
+ && x.Uses == 1
+ && clobber(x)
+ -> (MOVWstoreidx1 [i-1] {s} p idx w0 mem)
+(MOVWstoreidx1 [i] {s} p idx (SHRQconst [16] w) x:(MOVWstoreidx1 [i-2] {s} p idx w mem))
+ && x.Uses == 1
+ && clobber(x)
+ -> (MOVLstoreidx1 [i-2] {s} p idx w mem)
+(MOVWstoreidx1 [i] {s} p idx (SHRQconst [j] w) x:(MOVWstoreidx1 [i-2] {s} p idx w0:(SHRQconst [j-16] w) mem))
+ && x.Uses == 1
+ && clobber(x)
+ -> (MOVLstoreidx1 [i-2] {s} p idx w0 mem)
+(MOVLstoreidx1 [i] {s} p idx (SHRQconst [32] w) x:(MOVLstoreidx1 [i-4] {s} p idx w mem))
+ && x.Uses == 1
+ && clobber(x)
+ -> (MOVQstoreidx1 [i-4] {s} p idx w mem)
+(MOVLstoreidx1 [i] {s} p idx (SHRQconst [j] w) x:(MOVLstoreidx1 [i-4] {s} p idx w0:(SHRQconst [j-32] w) mem))
+ && x.Uses == 1
+ && clobber(x)
+ -> (MOVQstoreidx1 [i-4] {s} p idx w0 mem)
-(ORQ (ORQ (ORQ (ORQ (ORQ (ORQ (ORQ
- x0:(MOVBloadidx1 [i] {s} p idx mem)
- (SHLQconst [8] x1:(MOVBloadidx1 [i+1] {s} p idx mem)))
- (SHLQconst [16] x2:(MOVBloadidx1 [i+2] {s} p idx mem)))
- (SHLQconst [24] x3:(MOVBloadidx1 [i+3] {s} p idx mem)))
- (SHLQconst [32] x4:(MOVBloadidx1 [i+4] {s} p idx mem)))
- (SHLQconst [40] x5:(MOVBloadidx1 [i+5] {s} p idx mem)))
- (SHLQconst [48] x6:(MOVBloadidx1 [i+6] {s} p idx mem)))
- (SHLQconst [56] x7:(MOVBloadidx1 [i+7] {s} p idx mem))) && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil -> @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (MOVQloadidx1 <v.Type> [i] {s} p idx mem)
+(MOVWstoreidx2 [i] {s} p idx (SHRQconst [16] w) x:(MOVWstoreidx2 [i-2] {s} p idx w mem))
+ && x.Uses == 1
+ && clobber(x)
+ -> (MOVLstoreidx1 [i-2] {s} p (SHLQconst <idx.Type> [1] idx) w mem)
+(MOVWstoreidx2 [i] {s} p idx (SHRQconst [j] w) x:(MOVWstoreidx2 [i-2] {s} p idx w0:(SHRQconst [j-16] w) mem))
+ && x.Uses == 1
+ && clobber(x)
+ -> (MOVLstoreidx1 [i-2] {s} p (SHLQconst <idx.Type> [1] idx) w0 mem)
+(MOVLstoreidx4 [i] {s} p idx (SHRQconst [32] w) x:(MOVLstoreidx4 [i-4] {s} p idx w mem))
+ && x.Uses == 1
+ && clobber(x)
+ -> (MOVQstoreidx1 [i-4] {s} p (SHLQconst <idx.Type> [2] idx) w mem)
+(MOVLstoreidx4 [i] {s} p idx (SHRQconst [j] w) x:(MOVLstoreidx4 [i-4] {s} p idx w0:(SHRQconst [j-32] w) mem))
+ && x.Uses == 1
+ && clobber(x)
+ -> (MOVQstoreidx1 [i-4] {s} p (SHLQconst <idx.Type> [2] idx) w0 mem)
diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
index b1698c0cf1..b684b9ccdf 100644
--- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
@@ -111,12 +111,14 @@ func init() {
// Common regInfo
var (
gp01 = regInfo{inputs: []regMask{}, outputs: gponly}
- gp11 = regInfo{inputs: []regMask{gpsp}, outputs: gponly, clobbers: flags}
+ gp11 = regInfo{inputs: []regMask{gp}, outputs: gponly, clobbers: flags}
+ gp11sp = regInfo{inputs: []regMask{gpsp}, outputs: gponly, clobbers: flags}
gp11nf = regInfo{inputs: []regMask{gpsp}, outputs: gponly} // nf: no flags clobbered
gp11sb = regInfo{inputs: []regMask{gpspsb}, outputs: gponly}
- gp21 = regInfo{inputs: []regMask{gpsp, gpsp}, outputs: gponly, clobbers: flags}
+ gp21 = regInfo{inputs: []regMask{gp, gp}, outputs: gponly, clobbers: flags}
+ gp21sp = regInfo{inputs: []regMask{gpsp, gp}, outputs: gponly, clobbers: flags}
gp21sb = regInfo{inputs: []regMask{gpspsb, gpsp}, outputs: gponly}
- gp21shift = regInfo{inputs: []regMask{gpsp, cx}, outputs: []regMask{gp &^ cx}, clobbers: flags}
+ gp21shift = regInfo{inputs: []regMask{gp, cx}, outputs: []regMask{gp}, clobbers: flags}
gp11div = regInfo{inputs: []regMask{ax, gpsp &^ dx}, outputs: []regMask{ax},
clobbers: dx | flags}
gp11hmul = regInfo{inputs: []regMask{ax, gpsp}, outputs: []regMask{dx},
@@ -128,8 +130,8 @@ func init() {
gp1flags = regInfo{inputs: []regMask{gpsp}, outputs: flagsonly}
flagsgp = regInfo{inputs: flagsonly, outputs: gponly}
- // for CMOVconst -- uses AX to hold constant temporary. AX input is moved before temp.
- gp1flagsgp = regInfo{inputs: []regMask{gp, flags}, clobbers: ax | flags, outputs: []regMask{gp &^ ax}}
+ // for CMOVconst -- uses AX to hold constant temporary.
+ gp1flagsgp = regInfo{inputs: []regMask{gp &^ ax, flags}, clobbers: ax | flags, outputs: []regMask{gp &^ ax}}
readflags = regInfo{inputs: flagsonly, outputs: gponly}
flagsgpax = regInfo{inputs: flagsonly, clobbers: ax | flags, outputs: []regMask{gp &^ ax}}
@@ -186,32 +188,20 @@ func init() {
{name: "MOVSDstoreidx8", argLength: 4, reg: fpstoreidx, asm: "MOVSD", aux: "SymOff"}, // fp64 indexed by 8i store
// binary ops
- {name: "ADDQ", argLength: 2, reg: gp21, asm: "ADDQ", commutative: true, resultInArg0: true}, // arg0 + arg1
- {name: "ADDL", argLength: 2, reg: gp21, asm: "ADDL", commutative: true, resultInArg0: true}, // arg0 + arg1
- {name: "ADDW", argLength: 2, reg: gp21, asm: "ADDL", commutative: true, resultInArg0: true}, // arg0 + arg1
- {name: "ADDB", argLength: 2, reg: gp21, asm: "ADDL", commutative: true, resultInArg0: true}, // arg0 + arg1
- {name: "ADDQconst", argLength: 1, reg: gp11, asm: "ADDQ", aux: "Int64", resultInArg0: true, typ: "UInt64"}, // arg0 + auxint
- {name: "ADDLconst", argLength: 1, reg: gp11, asm: "ADDL", aux: "Int32", resultInArg0: true}, // arg0 + auxint
- {name: "ADDWconst", argLength: 1, reg: gp11, asm: "ADDL", aux: "Int16", resultInArg0: true}, // arg0 + auxint
- {name: "ADDBconst", argLength: 1, reg: gp11, asm: "ADDL", aux: "Int8", resultInArg0: true}, // arg0 + auxint
+ {name: "ADDQ", argLength: 2, reg: gp21sp, asm: "ADDQ", commutative: true}, // arg0 + arg1
+ {name: "ADDL", argLength: 2, reg: gp21sp, asm: "ADDL", commutative: true}, // arg0 + arg1
+ {name: "ADDQconst", argLength: 1, reg: gp11sp, asm: "ADDQ", aux: "Int64", typ: "UInt64"}, // arg0 + auxint
+ {name: "ADDLconst", argLength: 1, reg: gp11sp, asm: "ADDL", aux: "Int32"}, // arg0 + auxint
{name: "SUBQ", argLength: 2, reg: gp21, asm: "SUBQ", resultInArg0: true}, // arg0 - arg1
{name: "SUBL", argLength: 2, reg: gp21, asm: "SUBL", resultInArg0: true}, // arg0 - arg1
- {name: "SUBW", argLength: 2, reg: gp21, asm: "SUBL", resultInArg0: true}, // arg0 - arg1
- {name: "SUBB", argLength: 2, reg: gp21, asm: "SUBL", resultInArg0: true}, // arg0 - arg1
{name: "SUBQconst", argLength: 1, reg: gp11, asm: "SUBQ", aux: "Int64", resultInArg0: true}, // arg0 - auxint
{name: "SUBLconst", argLength: 1, reg: gp11, asm: "SUBL", aux: "Int32", resultInArg0: true}, // arg0 - auxint
- {name: "SUBWconst", argLength: 1, reg: gp11, asm: "SUBL", aux: "Int16", resultInArg0: true}, // arg0 - auxint
- {name: "SUBBconst", argLength: 1, reg: gp11, asm: "SUBL", aux: "Int8", resultInArg0: true}, // arg0 - auxint
{name: "MULQ", argLength: 2, reg: gp21, asm: "IMULQ", commutative: true, resultInArg0: true}, // arg0 * arg1
{name: "MULL", argLength: 2, reg: gp21, asm: "IMULL", commutative: true, resultInArg0: true}, // arg0 * arg1
- {name: "MULW", argLength: 2, reg: gp21, asm: "IMULW", commutative: true, resultInArg0: true}, // arg0 * arg1
- {name: "MULB", argLength: 2, reg: gp21, asm: "IMULW", commutative: true, resultInArg0: true}, // arg0 * arg1
{name: "MULQconst", argLength: 1, reg: gp11, asm: "IMULQ", aux: "Int64", resultInArg0: true}, // arg0 * auxint
{name: "MULLconst", argLength: 1, reg: gp11, asm: "IMULL", aux: "Int32", resultInArg0: true}, // arg0 * auxint
- {name: "MULWconst", argLength: 1, reg: gp11, asm: "IMULW", aux: "Int16", resultInArg0: true}, // arg0 * auxint
- {name: "MULBconst", argLength: 1, reg: gp11, asm: "IMULW", aux: "Int8", resultInArg0: true}, // arg0 * auxint
{name: "HMULQ", argLength: 2, reg: gp11hmul, asm: "IMULQ"}, // (arg0 * arg1) >> width
{name: "HMULL", argLength: 2, reg: gp11hmul, asm: "IMULL"}, // (arg0 * arg1) >> width
@@ -240,30 +230,18 @@ func init() {
{name: "ANDQ", argLength: 2, reg: gp21, asm: "ANDQ", commutative: true, resultInArg0: true}, // arg0 & arg1
{name: "ANDL", argLength: 2, reg: gp21, asm: "ANDL", commutative: true, resultInArg0: true}, // arg0 & arg1
- {name: "ANDW", argLength: 2, reg: gp21, asm: "ANDL", commutative: true, resultInArg0: true}, // arg0 & arg1
- {name: "ANDB", argLength: 2, reg: gp21, asm: "ANDL", commutative: true, resultInArg0: true}, // arg0 & arg1
{name: "ANDQconst", argLength: 1, reg: gp11, asm: "ANDQ", aux: "Int64", resultInArg0: true}, // arg0 & auxint
{name: "ANDLconst", argLength: 1, reg: gp11, asm: "ANDL", aux: "Int32", resultInArg0: true}, // arg0 & auxint
- {name: "ANDWconst", argLength: 1, reg: gp11, asm: "ANDL", aux: "Int16", resultInArg0: true}, // arg0 & auxint
- {name: "ANDBconst", argLength: 1, reg: gp11, asm: "ANDL", aux: "Int8", resultInArg0: true}, // arg0 & auxint
{name: "ORQ", argLength: 2, reg: gp21, asm: "ORQ", commutative: true, resultInArg0: true}, // arg0 | arg1
{name: "ORL", argLength: 2, reg: gp21, asm: "ORL", commutative: true, resultInArg0: true}, // arg0 | arg1
- {name: "ORW", argLength: 2, reg: gp21, asm: "ORL", commutative: true, resultInArg0: true}, // arg0 | arg1
- {name: "ORB", argLength: 2, reg: gp21, asm: "ORL", commutative: true, resultInArg0: true}, // arg0 | arg1
{name: "ORQconst", argLength: 1, reg: gp11, asm: "ORQ", aux: "Int64", resultInArg0: true}, // arg0 | auxint
{name: "ORLconst", argLength: 1, reg: gp11, asm: "ORL", aux: "Int32", resultInArg0: true}, // arg0 | auxint
- {name: "ORWconst", argLength: 1, reg: gp11, asm: "ORL", aux: "Int16", resultInArg0: true}, // arg0 | auxint
- {name: "ORBconst", argLength: 1, reg: gp11, asm: "ORL", aux: "Int8", resultInArg0: true}, // arg0 | auxint
{name: "XORQ", argLength: 2, reg: gp21, asm: "XORQ", commutative: true, resultInArg0: true}, // arg0 ^ arg1
{name: "XORL", argLength: 2, reg: gp21, asm: "XORL", commutative: true, resultInArg0: true}, // arg0 ^ arg1
- {name: "XORW", argLength: 2, reg: gp21, asm: "XORL", commutative: true, resultInArg0: true}, // arg0 ^ arg1
- {name: "XORB", argLength: 2, reg: gp21, asm: "XORL", commutative: true, resultInArg0: true}, // arg0 ^ arg1
{name: "XORQconst", argLength: 1, reg: gp11, asm: "XORQ", aux: "Int64", resultInArg0: true}, // arg0 ^ auxint
{name: "XORLconst", argLength: 1, reg: gp11, asm: "XORL", aux: "Int32", resultInArg0: true}, // arg0 ^ auxint
- {name: "XORWconst", argLength: 1, reg: gp11, asm: "XORL", aux: "Int16", resultInArg0: true}, // arg0 ^ auxint
- {name: "XORBconst", argLength: 1, reg: gp11, asm: "XORL", aux: "Int8", resultInArg0: true}, // arg0 ^ auxint
{name: "CMPQ", argLength: 2, reg: gp2flags, asm: "CMPQ", typ: "Flags"}, // arg0 compare to arg1
{name: "CMPL", argLength: 2, reg: gp2flags, asm: "CMPL", typ: "Flags"}, // arg0 compare to arg1
@@ -288,12 +266,8 @@ func init() {
{name: "SHLQ", argLength: 2, reg: gp21shift, asm: "SHLQ", resultInArg0: true}, // arg0 << arg1, shift amount is mod 64
{name: "SHLL", argLength: 2, reg: gp21shift, asm: "SHLL", resultInArg0: true}, // arg0 << arg1, shift amount is mod 32
- {name: "SHLW", argLength: 2, reg: gp21shift, asm: "SHLL", resultInArg0: true}, // arg0 << arg1, shift amount is mod 32
- {name: "SHLB", argLength: 2, reg: gp21shift, asm: "SHLL", resultInArg0: true}, // arg0 << arg1, shift amount is mod 32
{name: "SHLQconst", argLength: 1, reg: gp11, asm: "SHLQ", aux: "Int64", resultInArg0: true}, // arg0 << auxint, shift amount 0-63
{name: "SHLLconst", argLength: 1, reg: gp11, asm: "SHLL", aux: "Int32", resultInArg0: true}, // arg0 << auxint, shift amount 0-31
- {name: "SHLWconst", argLength: 1, reg: gp11, asm: "SHLL", aux: "Int16", resultInArg0: true}, // arg0 << auxint, shift amount 0-31
- {name: "SHLBconst", argLength: 1, reg: gp11, asm: "SHLL", aux: "Int8", resultInArg0: true}, // arg0 << auxint, shift amount 0-31
// Note: x86 is weird, the 16 and 8 byte shifts still use all 5 bits of shift amount!
{name: "SHRQ", argLength: 2, reg: gp21shift, asm: "SHRQ", resultInArg0: true}, // unsigned arg0 >> arg1, shift amount is mod 64
@@ -322,13 +296,9 @@ func init() {
// unary ops
{name: "NEGQ", argLength: 1, reg: gp11, asm: "NEGQ", resultInArg0: true}, // -arg0
{name: "NEGL", argLength: 1, reg: gp11, asm: "NEGL", resultInArg0: true}, // -arg0
- {name: "NEGW", argLength: 1, reg: gp11, asm: "NEGL", resultInArg0: true}, // -arg0
- {name: "NEGB", argLength: 1, reg: gp11, asm: "NEGL", resultInArg0: true}, // -arg0
{name: "NOTQ", argLength: 1, reg: gp11, asm: "NOTQ", resultInArg0: true}, // ^arg0
{name: "NOTL", argLength: 1, reg: gp11, asm: "NOTL", resultInArg0: true}, // ^arg0
- {name: "NOTW", argLength: 1, reg: gp11, asm: "NOTL", resultInArg0: true}, // ^arg0
- {name: "NOTB", argLength: 1, reg: gp11, asm: "NOTL", resultInArg0: true}, // ^arg0
{name: "BSFQ", argLength: 1, reg: gp11, asm: "BSFQ"}, // arg0 # of low-order zeroes ; undef if zero
{name: "BSFL", argLength: 1, reg: gp11, asm: "BSFL"}, // arg0 # of low-order zeroes ; undef if zero
@@ -383,8 +353,6 @@ func init() {
{name: "MOVLQSX", argLength: 1, reg: gp11nf, asm: "MOVLQSX"}, // sign extend arg0 from int32 to int64
{name: "MOVLQZX", argLength: 1, reg: gp11nf, asm: "MOVLQZX"}, // zero extend arg0 from int32 to int64
- {name: "MOVBconst", reg: gp01, asm: "MOVB", typ: "UInt8", aux: "Int8", rematerializeable: true}, // 8 low bits of auxint
- {name: "MOVWconst", reg: gp01, asm: "MOVW", typ: "UInt16", aux: "Int16", rematerializeable: true}, // 16 low bits of auxint
{name: "MOVLconst", reg: gp01, asm: "MOVL", typ: "UInt32", aux: "Int32", rematerializeable: true}, // 32 low bits of auxint
{name: "MOVQconst", reg: gp01, asm: "MOVQ", typ: "UInt64", aux: "Int64", rematerializeable: true}, // auxint
@@ -471,7 +439,7 @@ func init() {
clobbers: buildReg("DI FLAGS"),
},
},
- {name: "MOVOconst", reg: regInfo{nil, 0, []regMask{fp}}, typ: "Int128", rematerializeable: true},
+ {name: "MOVOconst", reg: regInfo{nil, 0, []regMask{fp}}, typ: "Int128", aux: "Int128", rematerializeable: true},
// arg0 = address of memory to zero
// arg1 = # of 8-byte words to zero
diff --git a/src/cmd/compile/internal/ssa/gen/ARMOps.go b/src/cmd/compile/internal/ssa/gen/ARMOps.go
index a4f7b17e87..23e8f63471 100644
--- a/src/cmd/compile/internal/ssa/gen/ARMOps.go
+++ b/src/cmd/compile/internal/ssa/gen/ARMOps.go
@@ -25,13 +25,13 @@ func init() {
{name: "CMP", argLength: 2, reg: gp2flags, asm: "CMP", typ: "Flags"}, // arg0 compare to arg1
- {name: "MOVWload", argLength: 2, reg: gpload, asm: "MOVW"}, // load from arg0 + auxInt + aux. arg1=mem.
- {name: "MOVWstore", argLength: 3, reg: gpstore, asm: "MOVW"}, // store 4 bytes of arg1 to arg0 + auxInt + aux. arg2=mem.
+ {name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW"}, // load from arg0 + auxInt + aux. arg1=mem.
+ {name: "MOVWstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVW"}, // store 4 bytes of arg1 to arg0 + auxInt + aux. arg2=mem.
{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff"}, // call static function aux.(*gc.Sym). arg0=mem, auxint=argsize, returns mem
// pseudo-ops
- {name: "LessThan", argLength: 2, reg: flagsgp}, // bool, 1 flags encode x<y 0 otherwise.
+ {name: "LessThan", argLength: 1, reg: flagsgp}, // bool, 1 flags encode x<y 0 otherwise.
}
blocks := []blockData{
diff --git a/src/cmd/compile/internal/ssa/gen/generic.rules b/src/cmd/compile/internal/ssa/gen/generic.rules
index b56e3f1b2d..b33037f100 100644
--- a/src/cmd/compile/internal/ssa/gen/generic.rules
+++ b/src/cmd/compile/internal/ssa/gen/generic.rules
@@ -81,13 +81,13 @@
(Rsh64Ux64 (Const64 [c]) (Const64 [d])) -> (Const64 [int64(uint64(c) >> uint64(d))])
(Lsh32x64 (Const32 [c]) (Const64 [d])) -> (Const32 [int64(int32(c) << uint64(d))])
(Rsh32x64 (Const32 [c]) (Const64 [d])) -> (Const32 [int64(int32(c) >> uint64(d))])
-(Rsh32Ux64 (Const32 [c]) (Const64 [d])) -> (Const32 [int64(uint32(c) >> uint64(d))])
+(Rsh32Ux64 (Const32 [c]) (Const64 [d])) -> (Const32 [int64(int32(uint32(c) >> uint64(d)))])
(Lsh16x64 (Const16 [c]) (Const64 [d])) -> (Const16 [int64(int16(c) << uint64(d))])
(Rsh16x64 (Const16 [c]) (Const64 [d])) -> (Const16 [int64(int16(c) >> uint64(d))])
-(Rsh16Ux64 (Const16 [c]) (Const64 [d])) -> (Const16 [int64(uint16(c) >> uint64(d))])
+(Rsh16Ux64 (Const16 [c]) (Const64 [d])) -> (Const16 [int64(int16(uint16(c) >> uint64(d)))])
(Lsh8x64 (Const8 [c]) (Const64 [d])) -> (Const8 [int64(int8(c) << uint64(d))])
(Rsh8x64 (Const8 [c]) (Const64 [d])) -> (Const8 [int64(int8(c) >> uint64(d))])
-(Rsh8Ux64 (Const8 [c]) (Const64 [d])) -> (Const8 [int64(uint8(c) >> uint64(d))])
+(Rsh8Ux64 (Const8 [c]) (Const64 [d])) -> (Const8 [int64(int8(uint8(c) >> uint64(d)))])
(Lsh64x64 (Const64 [0]) _) -> (Const64 [0])
(Rsh64x64 (Const64 [0]) _) -> (Const64 [0])
@@ -114,7 +114,7 @@
(Lsh16x16 (Rsh16Ux16 (Lsh16x16 x (Const16 [c1])) (Const16 [c2])) (Const16 [c3])) && uint16(c1) >= uint16(c2) && uint16(c3) >= uint16(c2) -> (Lsh16x16 x (Const16 <config.fe.TypeUInt16()> [int64(int16(c1-c2+c3))]))
(Lsh8x8 (Rsh8Ux8 (Lsh8x8 x (Const8 [c1])) (Const8 [c2])) (Const8 [c3])) && uint8(c1) >= uint8(c2) && uint8(c3) >= uint8(c2) -> (Lsh8x8 x (Const8 <config.fe.TypeUInt8()> [int64(int8(c1-c2+c3))]))
-// Fold IsInBounds when the range of the index cannot exceed the limt.
+// Fold IsInBounds when the range of the index cannot exceed the limit.
(IsInBounds (ZeroExt8to32 _) (Const32 [c])) && (1 << 8) <= c -> (ConstBool [1])
(IsInBounds (ZeroExt8to64 _) (Const64 [c])) && (1 << 8) <= c -> (ConstBool [1])
(IsInBounds (ZeroExt16to32 _) (Const32 [c])) && (1 << 16) <= c -> (ConstBool [1])
@@ -141,17 +141,17 @@
(Eq32 x x) -> (ConstBool [1])
(Eq16 x x) -> (ConstBool [1])
(Eq8 x x) -> (ConstBool [1])
-(Eq8 (ConstBool [c]) (ConstBool [d])) -> (ConstBool [b2i(c == d)])
-(Eq8 (ConstBool [0]) x) -> (Not x)
-(Eq8 (ConstBool [1]) x) -> x
+(EqB (ConstBool [c]) (ConstBool [d])) -> (ConstBool [b2i(c == d)])
+(EqB (ConstBool [0]) x) -> (Not x)
+(EqB (ConstBool [1]) x) -> x
(Neq64 x x) -> (ConstBool [0])
(Neq32 x x) -> (ConstBool [0])
(Neq16 x x) -> (ConstBool [0])
(Neq8 x x) -> (ConstBool [0])
-(Neq8 (ConstBool [c]) (ConstBool [d])) -> (ConstBool [b2i(c != d)])
-(Neq8 (ConstBool [0]) x) -> x
-(Neq8 (ConstBool [1]) x) -> (Not x)
+(NeqB (ConstBool [c]) (ConstBool [d])) -> (ConstBool [b2i(c != d)])
+(NeqB (ConstBool [0]) x) -> x
+(NeqB (ConstBool [1]) x) -> (Not x)
(Eq64 (Const64 <t> [c]) (Add64 (Const64 <t> [d]) x)) -> (Eq64 (Const64 <t> [c-d]) x)
(Eq32 (Const32 <t> [c]) (Add32 (Const32 <t> [d]) x)) -> (Eq32 (Const32 <t> [int64(int32(c-d))]) x)
@@ -168,13 +168,11 @@
(Eq32 x (Const32 <t> [c])) && x.Op != OpConst32 -> (Eq32 (Const32 <t> [c]) x)
(Eq16 x (Const16 <t> [c])) && x.Op != OpConst16 -> (Eq16 (Const16 <t> [c]) x)
(Eq8 x (Const8 <t> [c])) && x.Op != OpConst8 -> (Eq8 (Const8 <t> [c]) x)
-(Eq8 x (ConstBool <t> [c])) && x.Op != OpConstBool -> (Eq8 (ConstBool <t> [c]) x)
(Neq64 x (Const64 <t> [c])) && x.Op != OpConst64 -> (Neq64 (Const64 <t> [c]) x)
(Neq32 x (Const32 <t> [c])) && x.Op != OpConst32 -> (Neq32 (Const32 <t> [c]) x)
(Neq16 x (Const16 <t> [c])) && x.Op != OpConst16 -> (Neq16 (Const16 <t> [c]) x)
(Neq8 x (Const8 <t> [c])) && x.Op != OpConst8 -> (Neq8 (Const8 <t> [c]) x)
-(Neq8 x (ConstBool <t> [c])) && x.Op != OpConstBool -> (Neq8 (ConstBool <t> [c]) x)
// AddPtr is not canonicalized because nilcheck ptr checks the first argument to be non-nil.
(Add64 x (Const64 <t> [c])) && x.Op != OpConst64 -> (Add64 (Const64 <t> [c]) x)
@@ -414,6 +412,55 @@
(Neg32 (Sub32 x y)) -> (Sub32 y x)
(Neg64 (Sub64 x y)) -> (Sub64 y x)
+(And64 x (And64 x y)) -> (And64 x y)
+(And32 x (And32 x y)) -> (And32 x y)
+(And16 x (And16 x y)) -> (And16 x y)
+(And8 x (And8 x y)) -> (And8 x y)
+(And64 x (And64 y x)) -> (And64 x y)
+(And32 x (And32 y x)) -> (And32 x y)
+(And16 x (And16 y x)) -> (And16 x y)
+(And8 x (And8 y x)) -> (And8 x y)
+(And64 (And64 x y) x) -> (And64 x y)
+(And32 (And32 x y) x) -> (And32 x y)
+(And16 (And16 x y) x) -> (And16 x y)
+(And8 (And8 x y) x) -> (And8 x y)
+(And64 (And64 x y) y) -> (And64 x y)
+(And32 (And32 x y) y) -> (And32 x y)
+(And16 (And16 x y) y) -> (And16 x y)
+(And8 (And8 x y) y) -> (And8 x y)
+(Or64 x (Or64 x y)) -> (Or64 x y)
+(Or32 x (Or32 x y)) -> (Or32 x y)
+(Or16 x (Or16 x y)) -> (Or16 x y)
+(Or8 x (Or8 x y)) -> (Or8 x y)
+(Or64 x (Or64 y x)) -> (Or64 x y)
+(Or32 x (Or32 y x)) -> (Or32 x y)
+(Or16 x (Or16 y x)) -> (Or16 x y)
+(Or8 x (Or8 y x)) -> (Or8 x y)
+(Or64 (Or64 x y) x) -> (Or64 x y)
+(Or32 (Or32 x y) x) -> (Or32 x y)
+(Or16 (Or16 x y) x) -> (Or16 x y)
+(Or8 (Or8 x y) x) -> (Or8 x y)
+(Or64 (Or64 x y) y) -> (Or64 x y)
+(Or32 (Or32 x y) y) -> (Or32 x y)
+(Or16 (Or16 x y) y) -> (Or16 x y)
+(Or8 (Or8 x y) y) -> (Or8 x y)
+(Xor64 x (Xor64 x y)) -> y
+(Xor32 x (Xor32 x y)) -> y
+(Xor16 x (Xor16 x y)) -> y
+(Xor8 x (Xor8 x y)) -> y
+(Xor64 x (Xor64 y x)) -> y
+(Xor32 x (Xor32 y x)) -> y
+(Xor16 x (Xor16 y x)) -> y
+(Xor8 x (Xor8 y x)) -> y
+(Xor64 (Xor64 x y) x) -> y
+(Xor32 (Xor32 x y) x) -> y
+(Xor16 (Xor16 x y) x) -> y
+(Xor8 (Xor8 x y) x) -> y
+(Xor64 (Xor64 x y) y) -> x
+(Xor32 (Xor32 x y) y) -> x
+(Xor16 (Xor16 x y) y) -> x
+(Xor8 (Xor8 x y) y) -> x
+
(Trunc64to8 (And64 (Const64 [y]) x)) && y&0xFF == 0xFF -> (Trunc64to8 x)
(Trunc64to16 (And64 (Const64 [y]) x)) && y&0xFFFF == 0xFFFF -> (Trunc64to16 x)
(Trunc64to32 (And64 (Const64 [y]) x)) && y&0xFFFFFFFF == 0xFFFFFFFF -> (Trunc64to32 x)
diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go
index e6a0e8355b..88ae8b189d 100644
--- a/src/cmd/compile/internal/ssa/gen/genericOps.go
+++ b/src/cmd/compile/internal/ssa/gen/genericOps.go
@@ -237,9 +237,14 @@ var genericOps = []opData{
{name: "Geq32F", argLength: 2},
{name: "Geq64F", argLength: 2},
- // 1-input ops
- {name: "Not", argLength: 1}, // !arg0, boolean
+ // boolean ops
+ {name: "AndB", argLength: 2}, // arg0 && arg1 (not shortcircuited)
+ {name: "OrB", argLength: 2}, // arg0 || arg1 (not shortcircuited)
+ {name: "EqB", argLength: 2}, // arg0 == arg1
+ {name: "NeqB", argLength: 2}, // arg0 != arg1
+ {name: "Not", argLength: 1}, // !arg0, boolean
+ // 1-input ops
{name: "Neg8", argLength: 1}, // -arg0
{name: "Neg16", argLength: 1},
{name: "Neg32", argLength: 1},
diff --git a/src/cmd/compile/internal/ssa/gen/main.go b/src/cmd/compile/internal/ssa/gen/main.go
index db3c43d3a3..2aec4a324b 100644
--- a/src/cmd/compile/internal/ssa/gen/main.go
+++ b/src/cmd/compile/internal/ssa/gen/main.go
@@ -39,7 +39,7 @@ type opData struct {
rematerializeable bool
argLength int32 // number of arguments, if -1, then this operation has a variable number of arguments
commutative bool // this operation is commutative (e.g. addition)
- resultInArg0 bool // prefer v and v.Args[0] to be allocated to the same register
+ resultInArg0 bool // v and v.Args[0] must be allocated to the same register
}
type blockData struct {
@@ -155,6 +155,12 @@ func genOp() {
}
if v.resultInArg0 {
fmt.Fprintln(w, "resultInArg0: true,")
+ if v.reg.inputs[0] != v.reg.outputs[0] {
+ log.Fatalf("input[0] and output registers must be equal for %s", v.name)
+ }
+ if v.commutative && v.reg.inputs[1] != v.reg.outputs[0] {
+ log.Fatalf("input[1] and output registers must be equal for %s", v.name)
+ }
}
if a.name == "generic" {
fmt.Fprintln(w, "generic:true,")
diff --git a/src/cmd/compile/internal/ssa/gen/rulegen.go b/src/cmd/compile/internal/ssa/gen/rulegen.go
index b40f480f3e..5f7d1cf984 100644
--- a/src/cmd/compile/internal/ssa/gen/rulegen.go
+++ b/src/cmd/compile/internal/ssa/gen/rulegen.go
@@ -52,12 +52,12 @@ var (
)
type Rule struct {
- rule string
- lineno int
+ rule string
+ loc string // file name & line number
}
func (r Rule) String() string {
- return fmt.Sprintf("rule %q at line %d", r.rule, r.lineno)
+ return fmt.Sprintf("rule %q at %s", r.rule, r.loc)
}
// parse returns the matching part of the rule, additional conditions, and the result.
@@ -91,6 +91,7 @@ func genRules(arch arch) {
scanner := bufio.NewScanner(text)
rule := ""
var lineno int
+ var ruleLineno int // line number of "->"
for scanner.Scan() {
lineno++
line := scanner.Text()
@@ -107,6 +108,9 @@ func genRules(arch arch) {
if !strings.Contains(rule, "->") {
continue
}
+ if ruleLineno == 0 {
+ ruleLineno = lineno
+ }
if strings.HasSuffix(rule, "->") {
continue
}
@@ -117,18 +121,20 @@ func genRules(arch arch) {
if op[len(op)-1] == ')' {
op = op[:len(op)-1] // rule has only opcode, e.g. (ConstNil) -> ...
}
+ loc := fmt.Sprintf("%s.rules:%d", arch.name, ruleLineno)
if isBlock(op, arch) {
- blockrules[op] = append(blockrules[op], Rule{rule: rule, lineno: lineno})
+ blockrules[op] = append(blockrules[op], Rule{rule: rule, loc: loc})
} else {
- oprules[op] = append(oprules[op], Rule{rule: rule, lineno: lineno})
+ oprules[op] = append(oprules[op], Rule{rule: rule, loc: loc})
}
rule = ""
+ ruleLineno = 0
}
if err := scanner.Err(); err != nil {
log.Fatalf("scanner failed: %v\n", err)
}
if unbalanced(rule) {
- log.Fatalf("unbalanced rule at line %d: %v\n", lineno, rule)
+ log.Fatalf("%s.rules:%d: unbalanced rule: %v\n", arch.name, lineno, rule)
}
// Order all the ops.
@@ -174,15 +180,15 @@ func genRules(arch arch) {
fmt.Fprintf(w, "// result: %s\n", result)
fmt.Fprintf(w, "for {\n")
- genMatch(w, arch, match)
+ genMatch(w, arch, match, rule.loc)
if cond != "" {
fmt.Fprintf(w, "if !(%s) {\nbreak\n}\n", cond)
}
- genResult(w, arch, result)
+ genResult(w, arch, result, rule.loc)
if *genLog {
- fmt.Fprintf(w, "fmt.Println(\"rewrite %s.rules:%d\")\n", arch.name, rule.lineno)
+ fmt.Fprintf(w, "fmt.Println(\"rewrite %s\")\n", rule.loc)
}
fmt.Fprintf(w, "return true\n")
@@ -217,7 +223,7 @@ func genRules(arch arch) {
if s[1] != "nil" {
fmt.Fprintf(w, "v := b.Control\n")
if strings.Contains(s[1], "(") {
- genMatch0(w, arch, s[1], "v", map[string]struct{}{}, false)
+ genMatch0(w, arch, s[1], "v", map[string]struct{}{}, false, rule.loc)
} else {
fmt.Fprintf(w, "%s := b.Control\n", s[1])
}
@@ -266,7 +272,7 @@ func genRules(arch arch) {
if t[1] == "nil" {
fmt.Fprintf(w, "b.SetControl(nil)\n")
} else {
- fmt.Fprintf(w, "b.SetControl(%s)\n", genResult0(w, arch, t[1], new(int), false, false))
+ fmt.Fprintf(w, "b.SetControl(%s)\n", genResult0(w, arch, t[1], new(int), false, false, rule.loc))
}
if len(newsuccs) < len(succs) {
fmt.Fprintf(w, "b.Succs = b.Succs[:%d]\n", len(newsuccs))
@@ -289,7 +295,7 @@ func genRules(arch arch) {
}
if *genLog {
- fmt.Fprintf(w, "fmt.Println(\"rewrite %s.rules:%d\")\n", arch.name, rule.lineno)
+ fmt.Fprintf(w, "fmt.Println(\"rewrite %s\")\n", rule.loc)
}
fmt.Fprintf(w, "return true\n")
@@ -315,11 +321,11 @@ func genRules(arch arch) {
}
}
-func genMatch(w io.Writer, arch arch, match string) {
- genMatch0(w, arch, match, "v", map[string]struct{}{}, true)
+func genMatch(w io.Writer, arch arch, match string, loc string) {
+ genMatch0(w, arch, match, "v", map[string]struct{}{}, true, loc)
}
-func genMatch0(w io.Writer, arch arch, match, v string, m map[string]struct{}, top bool) {
+func genMatch0(w io.Writer, arch arch, match, v string, m map[string]struct{}, top bool, loc string) {
if match[0] != '(' || match[len(match)-1] != ')' {
panic("non-compound expr in genMatch0: " + match)
}
@@ -328,6 +334,24 @@ func genMatch0(w io.Writer, arch arch, match, v string, m map[string]struct{}, t
// contained in () or {}.
s := split(match[1 : len(match)-1]) // remove parens, then split
+ // Find op record
+ var op opData
+ for _, x := range genericOps {
+ if x.name == s[0] {
+ op = x
+ break
+ }
+ }
+ for _, x := range arch.ops {
+ if x.name == s[0] {
+ op = x
+ break
+ }
+ }
+ if op.name == "" {
+ log.Fatalf("%s: unknown op %s", loc, s[0])
+ }
+
// check op
if !top {
fmt.Fprintf(w, "if %s.Op != %s {\nbreak\n}\n", v, opName(s[0], arch))
@@ -354,6 +378,11 @@ func genMatch0(w io.Writer, arch arch, match, v string, m map[string]struct{}, t
}
} else if a[0] == '[' {
// auxint restriction
+ switch op.aux {
+ case "Bool", "Int8", "Int16", "Int32", "Int64", "Int128", "Float32", "Float64", "SymOff", "SymValAndOff", "SymInt32":
+ default:
+ log.Fatalf("%s: op %s %s can't have auxint", loc, op.name, op.aux)
+ }
x := a[1 : len(a)-1] // remove []
if !isVariable(x) {
// code
@@ -368,7 +397,12 @@ func genMatch0(w io.Writer, arch arch, match, v string, m map[string]struct{}, t
}
}
} else if a[0] == '{' {
- // auxint restriction
+ // aux restriction
+ switch op.aux {
+ case "String", "Sym", "SymOff", "SymValAndOff", "SymInt32":
+ default:
+ log.Fatalf("%s: op %s %s can't have aux", loc, op.name, op.aux)
+ }
x := a[1 : len(a)-1] // remove {}
if !isVariable(x) {
// code
@@ -412,30 +446,18 @@ func genMatch0(w io.Writer, arch arch, match, v string, m map[string]struct{}, t
argname = fmt.Sprintf("%s_%d", v, argnum)
}
fmt.Fprintf(w, "%s := %s.Args[%d]\n", argname, v, argnum)
- genMatch0(w, arch, a, argname, m, false)
+ genMatch0(w, arch, a, argname, m, false, loc)
argnum++
}
}
-
- variableLength := false
- for _, op := range genericOps {
- if op.name == s[0] && op.argLength == -1 {
- variableLength = true
- break
- }
- }
- for _, op := range arch.ops {
- if op.name == s[0] && op.argLength == -1 {
- variableLength = true
- break
- }
- }
- if variableLength {
+ if op.argLength == -1 {
fmt.Fprintf(w, "if len(%s.Args) != %d {\nbreak\n}\n", v, argnum)
+ } else if int(op.argLength) != argnum {
+ log.Fatalf("%s: op %s should have %d args, has %d", loc, op.name, op.argLength, argnum)
}
}
-func genResult(w io.Writer, arch arch, result string) {
+func genResult(w io.Writer, arch arch, result string, loc string) {
move := false
if result[0] == '@' {
// parse @block directive
@@ -444,9 +466,9 @@ func genResult(w io.Writer, arch arch, result string) {
result = s[1]
move = true
}
- genResult0(w, arch, result, new(int), true, move)
+ genResult0(w, arch, result, new(int), true, move, loc)
}
-func genResult0(w io.Writer, arch arch, result string, alloc *int, top, move bool) string {
+func genResult0(w io.Writer, arch arch, result string, alloc *int, top, move bool, loc string) string {
// TODO: when generating a constant result, use f.constVal to avoid
// introducing copies just to clean them up again.
if result[0] != '(' {
@@ -464,6 +486,24 @@ func genResult0(w io.Writer, arch arch, result string, alloc *int, top, move boo
s := split(result[1 : len(result)-1]) // remove parens, then split
+ // Find op record
+ var op opData
+ for _, x := range genericOps {
+ if x.name == s[0] {
+ op = x
+ break
+ }
+ }
+ for _, x := range arch.ops {
+ if x.name == s[0] {
+ op = x
+ break
+ }
+ }
+ if op.name == "" {
+ log.Fatalf("%s: unknown op %s", loc, s[0])
+ }
+
// Find the type of the variable.
var opType string
var typeOverride bool
@@ -512,23 +552,38 @@ func genResult0(w io.Writer, arch arch, result string, alloc *int, top, move boo
fmt.Fprintf(w, "v.AddArg(%s)\n", v)
}
}
+ argnum := 0
for _, a := range s[1:] {
if a[0] == '<' {
// type restriction, handled above
} else if a[0] == '[' {
// auxint restriction
+ switch op.aux {
+ case "Bool", "Int8", "Int16", "Int32", "Int64", "Int128", "Float32", "Float64", "SymOff", "SymValAndOff", "SymInt32":
+ default:
+ log.Fatalf("%s: op %s %s can't have auxint", loc, op.name, op.aux)
+ }
x := a[1 : len(a)-1] // remove []
fmt.Fprintf(w, "%s.AuxInt = %s\n", v, x)
} else if a[0] == '{' {
// aux restriction
+ switch op.aux {
+ case "String", "Sym", "SymOff", "SymValAndOff", "SymInt32":
+ default:
+ log.Fatalf("%s: op %s %s can't have aux", loc, op.name, op.aux)
+ }
x := a[1 : len(a)-1] // remove {}
fmt.Fprintf(w, "%s.Aux = %s\n", v, x)
} else {
// regular argument (sexpr or variable)
- x := genResult0(w, arch, a, alloc, false, move)
+ x := genResult0(w, arch, a, alloc, false, move, loc)
fmt.Fprintf(w, "%s.AddArg(%s)\n", v, x)
+ argnum++
}
}
+ if op.argLength != -1 && int(op.argLength) != argnum {
+ log.Fatalf("%s: op %s should have %d args, has %d", loc, op.name, op.argLength, argnum)
+ }
return v
}
diff --git a/src/cmd/compile/internal/ssa/id.go b/src/cmd/compile/internal/ssa/id.go
index 367e687abf..725279e9fd 100644
--- a/src/cmd/compile/internal/ssa/id.go
+++ b/src/cmd/compile/internal/ssa/id.go
@@ -11,7 +11,7 @@ type idAlloc struct {
last ID
}
-// get allocates an ID and returns it.
+// get allocates an ID and returns it. IDs are always > 0.
func (a *idAlloc) get() ID {
x := a.last
x++
diff --git a/src/cmd/compile/internal/ssa/likelyadjust.go b/src/cmd/compile/internal/ssa/likelyadjust.go
index 76251bdd14..2f52c4c6e6 100644
--- a/src/cmd/compile/internal/ssa/likelyadjust.go
+++ b/src/cmd/compile/internal/ssa/likelyadjust.go
@@ -11,11 +11,24 @@ import (
type loop struct {
header *Block // The header node of this (reducible) loop
outer *loop // loop containing this loop
- // Next two fields not currently used, but cheap to maintain,
- // and aid in computation of inner-ness and list of blocks.
- nBlocks int32 // Number of blocks in this loop but not within inner loops
- isInner bool // True if never discovered to contain a loop
- containsCall bool // if any block in this loop or any loop it contains is a BlockCall or BlockDefer
+
+ // By default, children exits, and depth are not initialized.
+ children []*loop // loops nested directly within this loop. Initialized by assembleChildren().
+ exits []*Block // exits records blocks reached by exits from this loop. Initialized by findExits().
+
+ // Loops aren't that common, so rather than force regalloc to keep
+ // a map or slice for its data, just put it here.
+ spills []*Value
+ scratch int32
+
+ // Next three fields used by regalloc and/or
+ // aid in computation of inner-ness and list of blocks.
+ nBlocks int32 // Number of blocks in this loop but not within inner loops
+ depth int16 // Nesting depth of the loop; 1 is outermost. Initialized by calculateDepths().
+ isInner bool // True if never discovered to contain a loop
+
+ // register allocation uses this.
+ containsCall bool // if any block in this loop or any loop it contains is a BlockCall or BlockDefer
}
// outerinner records that outer contains inner
@@ -48,6 +61,9 @@ type loopnest struct {
po []*Block
sdom sparseTree
loops []*loop
+
+ // Record which of the lazily initialized fields have actually been initialized.
+ initializedChildren, initializedDepth, initializedExits bool
}
func min8(a, b int8) int8 {
@@ -295,6 +311,35 @@ func loopnestfor(f *Func) *loopnest {
innermost.nBlocks++
}
}
+
+ ln := &loopnest{f: f, b2l: b2l, po: po, sdom: sdom, loops: loops}
+
+ // Curious about the loopiness? "-d=ssa/likelyadjust/stats"
+ if f.pass.stats > 0 && len(loops) > 0 {
+ ln.assembleChildren()
+ ln.calculateDepths()
+ ln.findExits()
+
+ // Note stats for non-innermost loops are slightly flawed because
+ // they don't account for inner loop exits that span multiple levels.
+
+ for _, l := range loops {
+ x := len(l.exits)
+ cf := 0
+ if !l.containsCall {
+ cf = 1
+ }
+ inner := 0
+ if l.isInner {
+ inner++
+ }
+
+ f.logStat("loopstats:",
+ l.depth, "depth", x, "exits",
+ inner, "is_inner", cf, "is_callfree", l.nBlocks, "n_blocks")
+ }
+ }
+
if f.pass.debug > 1 && len(loops) > 0 {
fmt.Printf("Loops in %s:\n", f.Name)
for _, l := range loops {
@@ -314,5 +359,90 @@ func loopnestfor(f *Func) *loopnest {
}
fmt.Print("\n")
}
- return &loopnest{f, b2l, po, sdom, loops}
+ return ln
+}
+
+// assembleChildren initializes the children field of each
+// loop in the nest. Loop A is a child of loop B if A is
+// directly nested within B (based on the reducible-loops
+// detection above)
+func (ln *loopnest) assembleChildren() {
+ if ln.initializedChildren {
+ return
+ }
+ for _, l := range ln.loops {
+ if l.outer != nil {
+ l.outer.children = append(l.outer.children, l)
+ }
+ }
+ ln.initializedChildren = true
+}
+
+// calculateDepths uses the children field of loops
+// to determine the nesting depth (outer=1) of each
+// loop. This is helpful for finding exit edges.
+func (ln *loopnest) calculateDepths() {
+ if ln.initializedDepth {
+ return
+ }
+ ln.assembleChildren()
+ for _, l := range ln.loops {
+ if l.outer == nil {
+ l.setDepth(1)
+ }
+ }
+ ln.initializedDepth = true
+}
+
+// findExits uses loop depth information to find the
+// exits from a loop.
+func (ln *loopnest) findExits() {
+ if ln.initializedExits {
+ return
+ }
+ ln.calculateDepths()
+ b2l := ln.b2l
+ for _, b := range ln.po {
+ l := b2l[b.ID]
+ if l != nil && len(b.Succs) == 2 {
+ sl := b2l[b.Succs[0].ID]
+ if recordIfExit(l, sl, b.Succs[0]) {
+ continue
+ }
+ sl = b2l[b.Succs[1].ID]
+ if recordIfExit(l, sl, b.Succs[1]) {
+ continue
+ }
+ }
+ }
+ ln.initializedExits = true
+}
+
+// recordIfExit checks sl (the loop containing b) to see if it
+// is outside of loop l, and if so, records b as an exit block
+// from l and returns true.
+func recordIfExit(l, sl *loop, b *Block) bool {
+ if sl != l {
+ if sl == nil || sl.depth <= l.depth {
+ l.exits = append(l.exits, b)
+ return true
+ }
+ // sl is not nil, and is deeper than l
+ // it's possible for this to be a goto into an irreducible loop made from gotos.
+ for sl.depth > l.depth {
+ sl = sl.outer
+ }
+ if sl != l {
+ l.exits = append(l.exits, b)
+ return true
+ }
+ }
+ return false
+}
+
+func (l *loop) setDepth(d int16) {
+ l.depth = d
+ for _, c := range l.children {
+ c.setDepth(d + 1)
+ }
}
diff --git a/src/cmd/compile/internal/ssa/loopbce.go b/src/cmd/compile/internal/ssa/loopbce.go
index 17486ac49f..9bd2d3f0de 100644
--- a/src/cmd/compile/internal/ssa/loopbce.go
+++ b/src/cmd/compile/internal/ssa/loopbce.go
@@ -31,7 +31,7 @@ type indVar struct {
//
//
// TODO: handle 32 bit operations
-func findIndVar(f *Func, sdom sparseTree) []indVar {
+func findIndVar(f *Func) []indVar {
var iv []indVar
nextb:
@@ -110,7 +110,7 @@ nextb:
// Second condition: b.Succs[entry] dominates nxt so that
// nxt is computed when inc < max, meaning nxt <= max.
- if !sdom.isAncestorEq(b.Succs[entry], nxt.Block) {
+ if !f.sdom.isAncestorEq(b.Succs[entry], nxt.Block) {
// inc+ind can only be reached through the branch that enters the loop.
continue
}
@@ -160,20 +160,18 @@ nextb:
// loopbce performs loop based bounds check elimination.
func loopbce(f *Func) {
- idom := dominators(f)
- sdom := newSparseTree(f, idom)
- ivList := findIndVar(f, sdom)
+ ivList := findIndVar(f)
m := make(map[*Value]indVar)
for _, iv := range ivList {
m[iv.ind] = iv
}
- removeBoundsChecks(f, sdom, m)
+ removeBoundsChecks(f, m)
}
// removesBoundsChecks remove IsInBounds and IsSliceInBounds based on the induction variables.
-func removeBoundsChecks(f *Func, sdom sparseTree, m map[*Value]indVar) {
+func removeBoundsChecks(f *Func, m map[*Value]indVar) {
for _, b := range f.Blocks {
if b.Kind != BlockIf {
continue
@@ -202,7 +200,7 @@ func removeBoundsChecks(f *Func, sdom sparseTree, m map[*Value]indVar) {
goto skip1
}
- if iv, has := m[ind]; has && sdom.isAncestorEq(iv.entry, b) && isNonNegative(iv.min) {
+ if iv, has := m[ind]; has && f.sdom.isAncestorEq(iv.entry, b) && isNonNegative(iv.min) {
if v.Args[1] == iv.max {
if f.pass.debug > 0 {
f.Config.Warnl(b.Line, "Found redundant %s", v.Op)
@@ -229,7 +227,7 @@ func removeBoundsChecks(f *Func, sdom sparseTree, m map[*Value]indVar) {
goto skip2
}
- if iv, has := m[ind]; has && sdom.isAncestorEq(iv.entry, b) && isNonNegative(iv.min) {
+ if iv, has := m[ind]; has && f.sdom.isAncestorEq(iv.entry, b) && isNonNegative(iv.min) {
if v.Args[1].Op == OpSliceCap && iv.max.Op == OpSliceLen && v.Args[1].Args[0] == iv.max.Args[0] {
if f.pass.debug > 0 {
f.Config.Warnl(b.Line, "Found redundant %s (len promoted to cap)", v.Op)
@@ -240,6 +238,37 @@ func removeBoundsChecks(f *Func, sdom sparseTree, m map[*Value]indVar) {
}
skip2:
+ // Simplify
+ // (IsInBounds (Add64 ind) (Const64 [c])) where 0 <= min <= ind < max <= (Const64 [c])
+ // (IsSliceInBounds ind (Const64 [c])) where 0 <= min <= ind < max <= (Const64 [c])
+ if v.Op == OpIsInBounds || v.Op == OpIsSliceInBounds {
+ ind, add := dropAdd64(v.Args[0])
+ if ind.Op != OpPhi {
+ goto skip3
+ }
+
+ // ind + add >= 0 <-> min + add >= 0 <-> min >= -add
+ if iv, has := m[ind]; has && f.sdom.isAncestorEq(iv.entry, b) && isGreaterOrEqualThan(iv.min, -add) {
+ if !v.Args[1].isGenericIntConst() || !iv.max.isGenericIntConst() {
+ goto skip3
+ }
+
+ limit := v.Args[1].AuxInt
+ if v.Op == OpIsSliceInBounds {
+ // If limit++ overflows signed integer then 0 <= max && max <= limit will be false.
+ limit++
+ }
+
+ if max := iv.max.AuxInt + add; 0 <= max && max <= limit { // handle overflow
+ if f.pass.debug > 0 {
+ f.Config.Warnl(b.Line, "Found redundant (%s ind %d), ind < %d", v.Op, v.Args[1].AuxInt, iv.max.AuxInt+add)
+ }
+ goto simplify
+ }
+ }
+ }
+ skip3:
+
continue
simplify:
@@ -258,3 +287,13 @@ func dropAdd64(v *Value) (*Value, int64) {
}
return v, 0
}
+
+func isGreaterOrEqualThan(v *Value, c int64) bool {
+ if c == 0 {
+ return isNonNegative(v)
+ }
+ if v.isGenericIntConst() && v.AuxInt >= c {
+ return true
+ }
+ return false
+}
diff --git a/src/cmd/compile/internal/ssa/nilcheck.go b/src/cmd/compile/internal/ssa/nilcheck.go
index 881e3b2eff..62eb0c8ea6 100644
--- a/src/cmd/compile/internal/ssa/nilcheck.go
+++ b/src/cmd/compile/internal/ssa/nilcheck.go
@@ -4,14 +4,12 @@
package ssa
-// TODO: return value from newobject/newarray is non-nil.
-
// nilcheckelim eliminates unnecessary nil checks.
func nilcheckelim(f *Func) {
// A nil check is redundant if the same nil check was successful in a
// dominating block. The efficacy of this pass depends heavily on the
// efficacy of the cse pass.
- idom := dominators(f)
+ idom := f.idom
domTree := make([][]*Block, f.NumBlocks())
// Create a block ID -> [dominees] mapping
diff --git a/src/cmd/compile/internal/ssa/nilcheck_test.go b/src/cmd/compile/internal/ssa/nilcheck_test.go
index d1f38b6951..af6cbe864a 100644
--- a/src/cmd/compile/internal/ssa/nilcheck_test.go
+++ b/src/cmd/compile/internal/ssa/nilcheck_test.go
@@ -49,6 +49,7 @@ func benchmarkNilCheckDeep(b *testing.B, depth int) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
+ domTree(fun.f)
nilcheckelim(fun.f)
}
}
@@ -83,6 +84,7 @@ func TestNilcheckSimple(t *testing.T) {
Exit("mem")))
CheckFunc(fun.f)
+ domTree(fun.f)
nilcheckelim(fun.f)
// clean up the removed nil check
@@ -120,6 +122,7 @@ func TestNilcheckDomOrder(t *testing.T) {
Goto("exit")))
CheckFunc(fun.f)
+ domTree(fun.f)
nilcheckelim(fun.f)
// clean up the removed nil check
@@ -153,6 +156,7 @@ func TestNilcheckAddr(t *testing.T) {
Exit("mem")))
CheckFunc(fun.f)
+ domTree(fun.f)
nilcheckelim(fun.f)
// clean up the removed nil check
@@ -187,6 +191,7 @@ func TestNilcheckAddPtr(t *testing.T) {
Exit("mem")))
CheckFunc(fun.f)
+ domTree(fun.f)
nilcheckelim(fun.f)
// clean up the removed nil check
@@ -231,6 +236,7 @@ func TestNilcheckPhi(t *testing.T) {
Exit("mem")))
CheckFunc(fun.f)
+ domTree(fun.f)
nilcheckelim(fun.f)
// clean up the removed nil check
@@ -272,6 +278,7 @@ func TestNilcheckKeepRemove(t *testing.T) {
Exit("mem")))
CheckFunc(fun.f)
+ domTree(fun.f)
nilcheckelim(fun.f)
// clean up the removed nil check
@@ -319,6 +326,7 @@ func TestNilcheckInFalseBranch(t *testing.T) {
Exit("mem")))
CheckFunc(fun.f)
+ domTree(fun.f)
nilcheckelim(fun.f)
// clean up the removed nil check
@@ -370,6 +378,7 @@ func TestNilcheckUser(t *testing.T) {
CheckFunc(fun.f)
// we need the opt here to rewrite the user nilcheck
opt(fun.f)
+ domTree(fun.f)
nilcheckelim(fun.f)
// clean up the removed nil check
@@ -409,11 +418,12 @@ func TestNilcheckBug(t *testing.T) {
Goto("exit")),
Bloc("exit",
Valu("phi", OpPhi, TypeMem, 0, nil, "mem", "store"),
- Exit("mem")))
+ Exit("phi")))
CheckFunc(fun.f)
// we need the opt here to rewrite the user nilcheck
opt(fun.f)
+ domTree(fun.f)
nilcheckelim(fun.f)
// clean up the removed nil check
diff --git a/src/cmd/compile/internal/ssa/op.go b/src/cmd/compile/internal/ssa/op.go
index d10ea230ff..cadbc7cd7a 100644
--- a/src/cmd/compile/internal/ssa/op.go
+++ b/src/cmd/compile/internal/ssa/op.go
@@ -26,7 +26,7 @@ type opInfo struct {
generic bool // this is a generic (arch-independent) opcode
rematerializeable bool // this op is rematerializeable
commutative bool // this operation is commutative (e.g. addition)
- resultInArg0 bool // prefer v and v.Args[0] to be allocated to the same register
+ resultInArg0 bool // v and v.Args[0] must be allocated to the same register
}
type inputInfo struct {
@@ -49,9 +49,10 @@ const (
auxInt16 // auxInt is a 16-bit integer
auxInt32 // auxInt is a 32-bit integer
auxInt64 // auxInt is a 64-bit integer
+ auxInt128 // auxInt represents a 128-bit integer. Always 0.
auxFloat32 // auxInt is a float32 (encoded with math.Float64bits)
auxFloat64 // auxInt is a float64 (encoded with math.Float64bits)
- auxString // auxInt is a string
+ auxString // aux is a string
auxSym // aux is a symbol
auxSymOff // aux is a symbol, auxInt is an offset
auxSymValAndOff // aux is a symbol, auxInt is a ValAndOff
diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go
index 5465d7f5ed..9ab9be769c 100644
--- a/src/cmd/compile/internal/ssa/opGen.go
+++ b/src/cmd/compile/internal/ssa/opGen.go
@@ -120,28 +120,16 @@ const (
OpAMD64MOVSDstoreidx8
OpAMD64ADDQ
OpAMD64ADDL
- OpAMD64ADDW
- OpAMD64ADDB
OpAMD64ADDQconst
OpAMD64ADDLconst
- OpAMD64ADDWconst
- OpAMD64ADDBconst
OpAMD64SUBQ
OpAMD64SUBL
- OpAMD64SUBW
- OpAMD64SUBB
OpAMD64SUBQconst
OpAMD64SUBLconst
- OpAMD64SUBWconst
- OpAMD64SUBBconst
OpAMD64MULQ
OpAMD64MULL
- OpAMD64MULW
- OpAMD64MULB
OpAMD64MULQconst
OpAMD64MULLconst
- OpAMD64MULWconst
- OpAMD64MULBconst
OpAMD64HMULQ
OpAMD64HMULL
OpAMD64HMULW
@@ -165,28 +153,16 @@ const (
OpAMD64MODWU
OpAMD64ANDQ
OpAMD64ANDL
- OpAMD64ANDW
- OpAMD64ANDB
OpAMD64ANDQconst
OpAMD64ANDLconst
- OpAMD64ANDWconst
- OpAMD64ANDBconst
OpAMD64ORQ
OpAMD64ORL
- OpAMD64ORW
- OpAMD64ORB
OpAMD64ORQconst
OpAMD64ORLconst
- OpAMD64ORWconst
- OpAMD64ORBconst
OpAMD64XORQ
OpAMD64XORL
- OpAMD64XORW
- OpAMD64XORB
OpAMD64XORQconst
OpAMD64XORLconst
- OpAMD64XORWconst
- OpAMD64XORBconst
OpAMD64CMPQ
OpAMD64CMPL
OpAMD64CMPW
@@ -207,12 +183,8 @@ const (
OpAMD64TESTBconst
OpAMD64SHLQ
OpAMD64SHLL
- OpAMD64SHLW
- OpAMD64SHLB
OpAMD64SHLQconst
OpAMD64SHLLconst
- OpAMD64SHLWconst
- OpAMD64SHLBconst
OpAMD64SHRQ
OpAMD64SHRL
OpAMD64SHRW
@@ -235,12 +207,8 @@ const (
OpAMD64ROLBconst
OpAMD64NEGQ
OpAMD64NEGL
- OpAMD64NEGW
- OpAMD64NEGB
OpAMD64NOTQ
OpAMD64NOTL
- OpAMD64NOTW
- OpAMD64NOTB
OpAMD64BSFQ
OpAMD64BSFL
OpAMD64BSFW
@@ -280,8 +248,6 @@ const (
OpAMD64MOVWQZX
OpAMD64MOVLQSX
OpAMD64MOVLQZX
- OpAMD64MOVBconst
- OpAMD64MOVWconst
OpAMD64MOVLconst
OpAMD64MOVQconst
OpAMD64CVTTSD2SL
@@ -537,6 +503,10 @@ const (
OpGeq64U
OpGeq32F
OpGeq64F
+ OpAndB
+ OpOrB
+ OpEqB
+ OpNeqB
OpNot
OpNeg8
OpNeg16
@@ -971,81 +941,13 @@ var opcodeTable = [...]opInfo{
},
},
{
- name: "ADDQ",
- argLen: 2,
- commutative: true,
- resultInArg0: true,
- asm: x86.AADDQ,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "ADDL",
- argLen: 2,
- commutative: true,
- resultInArg0: true,
- asm: x86.AADDL,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "ADDW",
- argLen: 2,
- commutative: true,
- resultInArg0: true,
- asm: x86.AADDL,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "ADDB",
- argLen: 2,
- commutative: true,
- resultInArg0: true,
- asm: x86.AADDL,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "ADDQconst",
- auxType: auxInt64,
- argLen: 1,
- resultInArg0: true,
- asm: x86.AADDQ,
+ name: "ADDQ",
+ argLen: 2,
+ commutative: true,
+ asm: x86.AADDQ,
reg: regInfo{
inputs: []inputInfo{
+ {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
@@ -1055,13 +957,13 @@ var opcodeTable = [...]opInfo{
},
},
{
- name: "ADDLconst",
- auxType: auxInt32,
- argLen: 1,
- resultInArg0: true,
- asm: x86.AADDL,
+ name: "ADDL",
+ argLen: 2,
+ commutative: true,
+ asm: x86.AADDL,
reg: regInfo{
inputs: []inputInfo{
+ {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
@@ -1071,11 +973,10 @@ var opcodeTable = [...]opInfo{
},
},
{
- name: "ADDWconst",
- auxType: auxInt16,
- argLen: 1,
- resultInArg0: true,
- asm: x86.AADDL,
+ name: "ADDQconst",
+ auxType: auxInt64,
+ argLen: 1,
+ asm: x86.AADDQ,
reg: regInfo{
inputs: []inputInfo{
{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
@@ -1087,11 +988,10 @@ var opcodeTable = [...]opInfo{
},
},
{
- name: "ADDBconst",
- auxType: auxInt8,
- argLen: 1,
- resultInArg0: true,
- asm: x86.AADDL,
+ name: "ADDLconst",
+ auxType: auxInt32,
+ argLen: 1,
+ asm: x86.AADDL,
reg: regInfo{
inputs: []inputInfo{
{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
@@ -1109,8 +1009,8 @@ var opcodeTable = [...]opInfo{
asm: x86.ASUBQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -1125,40 +1025,8 @@ var opcodeTable = [...]opInfo{
asm: x86.ASUBL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "SUBW",
- argLen: 2,
- resultInArg0: true,
- asm: x86.ASUBL,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "SUBB",
- argLen: 2,
- resultInArg0: true,
- asm: x86.ASUBL,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -1174,7 +1042,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASUBQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -1190,39 +1058,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASUBL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "SUBWconst",
- auxType: auxInt16,
- argLen: 1,
- resultInArg0: true,
- asm: x86.ASUBL,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "SUBBconst",
- auxType: auxInt8,
- argLen: 1,
- resultInArg0: true,
- asm: x86.ASUBL,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -1238,8 +1074,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AIMULQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -1255,42 +1091,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AIMULL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "MULW",
- argLen: 2,
- commutative: true,
- resultInArg0: true,
- asm: x86.AIMULW,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "MULB",
- argLen: 2,
- commutative: true,
- resultInArg0: true,
- asm: x86.AIMULW,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -1306,7 +1108,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AIMULQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -1322,39 +1124,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AIMULL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "MULWconst",
- auxType: auxInt16,
- argLen: 1,
- resultInArg0: true,
- asm: x86.AIMULW,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "MULBconst",
- auxType: auxInt8,
- argLen: 1,
- resultInArg0: true,
- asm: x86.AIMULW,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -1489,8 +1259,8 @@ var opcodeTable = [...]opInfo{
resultInArg0: true,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -1686,8 +1456,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AANDQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -1703,42 +1473,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AANDL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "ANDW",
- argLen: 2,
- commutative: true,
- resultInArg0: true,
- asm: x86.AANDL,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "ANDB",
- argLen: 2,
- commutative: true,
- resultInArg0: true,
- asm: x86.AANDL,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -1754,7 +1490,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AANDQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -1770,39 +1506,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AANDL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "ANDWconst",
- auxType: auxInt16,
- argLen: 1,
- resultInArg0: true,
- asm: x86.AANDL,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "ANDBconst",
- auxType: auxInt8,
- argLen: 1,
- resultInArg0: true,
- asm: x86.AANDL,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -1818,8 +1522,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AORQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -1835,42 +1539,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AORL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "ORW",
- argLen: 2,
- commutative: true,
- resultInArg0: true,
- asm: x86.AORL,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "ORB",
- argLen: 2,
- commutative: true,
- resultInArg0: true,
- asm: x86.AORL,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -1886,7 +1556,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AORQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -1902,39 +1572,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AORL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "ORWconst",
- auxType: auxInt16,
- argLen: 1,
- resultInArg0: true,
- asm: x86.AORL,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "ORBconst",
- auxType: auxInt8,
- argLen: 1,
- resultInArg0: true,
- asm: x86.AORL,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -1950,8 +1588,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AXORQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -1967,42 +1605,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AXORL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "XORW",
- argLen: 2,
- commutative: true,
- resultInArg0: true,
- asm: x86.AXORL,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "XORB",
- argLen: 2,
- commutative: true,
- resultInArg0: true,
- asm: x86.AXORL,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2018,7 +1622,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AXORQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2034,39 +1638,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AXORL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "XORWconst",
- auxType: auxInt16,
- argLen: 1,
- resultInArg0: true,
- asm: x86.AXORL,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "XORBconst",
- auxType: auxInt8,
- argLen: 1,
- resultInArg0: true,
- asm: x86.AXORL,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2334,11 +1906,11 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
- 65517, // AX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
},
},
@@ -2350,43 +1922,11 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65517, // AX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "SHLW",
- argLen: 2,
- resultInArg0: true,
- asm: x86.ASHLL,
- reg: regInfo{
- inputs: []inputInfo{
- {1, 2}, // CX
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65517, // AX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "SHLB",
- argLen: 2,
- resultInArg0: true,
- asm: x86.ASHLL,
- reg: regInfo{
- inputs: []inputInfo{
- {1, 2}, // CX
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
- 65517, // AX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
},
},
@@ -2398,7 +1938,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASHLQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2414,39 +1954,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASHLL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "SHLWconst",
- auxType: auxInt16,
- argLen: 1,
- resultInArg0: true,
- asm: x86.ASHLL,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "SHLBconst",
- auxType: auxInt8,
- argLen: 1,
- resultInArg0: true,
- asm: x86.ASHLL,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2462,11 +1970,11 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
- 65517, // AX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
},
},
@@ -2478,11 +1986,11 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
- 65517, // AX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
},
},
@@ -2494,11 +2002,11 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
- 65517, // AX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
},
},
@@ -2510,11 +2018,11 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
- 65517, // AX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
},
},
@@ -2526,7 +2034,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASHRQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2542,7 +2050,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASHRL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2558,7 +2066,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASHRW,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2574,7 +2082,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASHRB,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2590,11 +2098,11 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
- 65517, // AX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
},
},
@@ -2606,11 +2114,11 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
- 65517, // AX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
},
},
@@ -2622,11 +2130,11 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
- 65517, // AX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
},
},
@@ -2638,11 +2146,11 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
- 65517, // AX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
},
},
@@ -2654,7 +2162,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASARQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2670,7 +2178,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASARL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2686,7 +2194,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASARW,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2702,7 +2210,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASARB,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2718,7 +2226,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AROLQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2734,7 +2242,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AROLL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2750,7 +2258,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AROLW,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2766,7 +2274,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AROLB,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2781,7 +2289,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ANEGQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2796,37 +2304,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ANEGL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "NEGW",
- argLen: 1,
- resultInArg0: true,
- asm: x86.ANEGL,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "NEGB",
- argLen: 1,
- resultInArg0: true,
- asm: x86.ANEGL,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2841,7 +2319,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ANOTQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2856,37 +2334,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ANOTL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "NOTW",
- argLen: 1,
- resultInArg0: true,
- asm: x86.ANOTL,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- clobbers: 8589934592, // FLAGS
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "NOTB",
- argLen: 1,
- resultInArg0: true,
- asm: x86.ANOTL,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2900,7 +2348,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ABSFQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2914,7 +2362,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ABSFL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2928,7 +2376,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ABSFW,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2942,7 +2390,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ABSRQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2956,7 +2404,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ABSRL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2970,7 +2418,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ABSRW,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -2987,7 +2435,7 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 8589934592}, // FLAGS
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65518}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934593, // AX FLAGS
outputs: []regMask{
@@ -3004,7 +2452,7 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 8589934592}, // FLAGS
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65518}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934593, // AX FLAGS
outputs: []regMask{
@@ -3021,7 +2469,7 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 8589934592}, // FLAGS
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65518}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934593, // AX FLAGS
outputs: []regMask{
@@ -3038,7 +2486,7 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 8589934592}, // FLAGS
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65518}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934593, // AX FLAGS
outputs: []regMask{
@@ -3055,7 +2503,7 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 8589934592}, // FLAGS
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65518}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934593, // AX FLAGS
outputs: []regMask{
@@ -3072,7 +2520,7 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 8589934592}, // FLAGS
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65518}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934593, // AX FLAGS
outputs: []regMask{
@@ -3087,7 +2535,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ABSWAPQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -3102,7 +2550,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ABSWAPL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 8589934592, // FLAGS
outputs: []regMask{
@@ -3438,30 +2886,6 @@ var opcodeTable = [...]opInfo{
},
},
{
- name: "MOVBconst",
- auxType: auxInt8,
- argLen: 0,
- rematerializeable: true,
- asm: x86.AMOVB,
- reg: regInfo{
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
- name: "MOVWconst",
- auxType: auxInt16,
- argLen: 0,
- rematerializeable: true,
- asm: x86.AMOVW,
- reg: regInfo{
- outputs: []regMask{
- 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- },
- },
- },
- {
name: "MOVLconst",
auxType: auxInt32,
argLen: 0,
@@ -4211,6 +3635,7 @@ var opcodeTable = [...]opInfo{
},
{
name: "MOVOconst",
+ auxType: auxInt128,
argLen: 0,
rematerializeable: true,
reg: regInfo{
@@ -4430,9 +3855,10 @@ var opcodeTable = [...]opInfo{
},
},
{
- name: "MOVWload",
- argLen: 2,
- asm: arm.AMOVW,
+ name: "MOVWload",
+ auxType: auxSymOff,
+ argLen: 2,
+ asm: arm.AMOVW,
reg: regInfo{
inputs: []inputInfo{
{0, 31}, // R0 R1 R2 R3 SP
@@ -4443,9 +3869,10 @@ var opcodeTable = [...]opInfo{
},
},
{
- name: "MOVWstore",
- argLen: 3,
- asm: arm.AMOVW,
+ name: "MOVWstore",
+ auxType: auxSymOff,
+ argLen: 3,
+ asm: arm.AMOVW,
reg: regInfo{
inputs: []inputInfo{
{0, 31}, // R0 R1 R2 R3 SP
@@ -4463,7 +3890,7 @@ var opcodeTable = [...]opInfo{
},
{
name: "LessThan",
- argLen: 2,
+ argLen: 1,
reg: regInfo{
inputs: []inputInfo{
{0, 32}, // FLAGS
@@ -5354,6 +4781,26 @@ var opcodeTable = [...]opInfo{
generic: true,
},
{
+ name: "AndB",
+ argLen: 2,
+ generic: true,
+ },
+ {
+ name: "OrB",
+ argLen: 2,
+ generic: true,
+ },
+ {
+ name: "EqB",
+ argLen: 2,
+ generic: true,
+ },
+ {
+ name: "NeqB",
+ argLen: 2,
+ generic: true,
+ },
+ {
name: "Not",
argLen: 1,
generic: true,
diff --git a/src/cmd/compile/internal/ssa/phielim.go b/src/cmd/compile/internal/ssa/phielim.go
index ce3b5a199a..77013c6481 100644
--- a/src/cmd/compile/internal/ssa/phielim.go
+++ b/src/cmd/compile/internal/ssa/phielim.go
@@ -40,11 +40,7 @@ func phielimValue(v *Value) bool {
// are not v itself, then the phi must remain.
// Otherwise, we can replace it with a copy.
var w *Value
- for i, x := range v.Args {
- if b := v.Block.Preds[i]; b.Kind == BlockFirst && b.Succs[1] == v.Block {
- // This branch is never taken so we can just eliminate it.
- continue
- }
+ for _, x := range v.Args {
if x == v {
continue
}
diff --git a/src/cmd/compile/internal/ssa/phiopt.go b/src/cmd/compile/internal/ssa/phiopt.go
index 2d0a45733a..3b6728ca86 100644
--- a/src/cmd/compile/internal/ssa/phiopt.go
+++ b/src/cmd/compile/internal/ssa/phiopt.go
@@ -26,6 +26,7 @@ package ssa
func phiopt(f *Func) {
for _, b := range f.Blocks {
if len(b.Preds) != 2 || len(b.Values) == 0 {
+ // TODO: handle more than 2 predecessors, e.g. a || b || c.
continue
}
@@ -45,44 +46,67 @@ func phiopt(f *Func) {
}
// b0 is the if block giving the boolean value.
- var reverse bool
+ // reverse is the predecessor from which the truth value comes.
+ var reverse int
if b0.Succs[0] == pb0 && b0.Succs[1] == pb1 {
- reverse = false
+ reverse = 0
} else if b0.Succs[0] == pb1 && b0.Succs[1] == pb0 {
- reverse = true
+ reverse = 1
} else {
b.Fatalf("invalid predecessors\n")
}
for _, v := range b.Values {
- if v.Op != OpPhi || !v.Type.IsBoolean() || v.Args[0].Op != OpConstBool || v.Args[1].Op != OpConstBool {
+ if v.Op != OpPhi || !v.Type.IsBoolean() {
continue
}
- ok, isCopy := false, false
- if v.Args[0].AuxInt == 1 && v.Args[1].AuxInt == 0 {
- ok, isCopy = true, !reverse
- } else if v.Args[0].AuxInt == 0 && v.Args[1].AuxInt == 1 {
- ok, isCopy = true, reverse
+ // Replaces
+ // if a { x = true } else { x = false } with x = a
+ // and
+ // if a { x = false } else { x = true } with x = !a
+ if v.Args[0].Op == OpConstBool && v.Args[1].Op == OpConstBool {
+ if v.Args[reverse].AuxInt != v.Args[1-reverse].AuxInt {
+ ops := [2]Op{OpNot, OpCopy}
+ v.reset(ops[v.Args[reverse].AuxInt])
+ v.AddArg(b0.Control)
+ if f.pass.debug > 0 {
+ f.Config.Warnl(b.Line, "converted OpPhi to %v", v.Op)
+ }
+ continue
+ }
}
- // (Phi (ConstBool [x]) (ConstBool [x])) is already handled by opt / phielim.
-
- if ok && isCopy {
- if f.pass.debug > 0 {
- f.Config.Warnl(b.Line, "converted OpPhi to OpCopy")
+ // Replaces
+ // if a { x = true } else { x = value } with x = a || value.
+ // Requires that value dominates x, meaning that regardless of a,
+ // value is always computed. This guarantees that the side effects
+ // of value are not seen if a is false.
+ if v.Args[reverse].Op == OpConstBool && v.Args[reverse].AuxInt == 1 {
+ if tmp := v.Args[1-reverse]; f.sdom.isAncestorEq(tmp.Block, b) {
+ v.reset(OpOrB)
+ v.SetArgs2(b0.Control, tmp)
+ if f.pass.debug > 0 {
+ f.Config.Warnl(b.Line, "converted OpPhi to %v", v.Op)
+ }
+ continue
}
- v.reset(OpCopy)
- v.AddArg(b0.Control)
- continue
}
- if ok && !isCopy {
- if f.pass.debug > 0 {
- f.Config.Warnl(b.Line, "converted OpPhi to OpNot")
+
+ // Replaces
+ // if a { x = value } else { x = false } with x = a && value.
+ // Requires that value dominates x, meaning that regardless of a,
+ // value is always computed. This guarantees that the side effects
+ // of value are not seen if a is false.
+ if v.Args[1-reverse].Op == OpConstBool && v.Args[1-reverse].AuxInt == 0 {
+ if tmp := v.Args[reverse]; f.sdom.isAncestorEq(tmp.Block, b) {
+ v.reset(OpAndB)
+ v.SetArgs2(b0.Control, tmp)
+ if f.pass.debug > 0 {
+ f.Config.Warnl(b.Line, "converted OpPhi to %v", v.Op)
+ }
+ continue
}
- v.reset(OpNot)
- v.AddArg(b0.Control)
- continue
}
}
}
diff --git a/src/cmd/compile/internal/ssa/prove.go b/src/cmd/compile/internal/ssa/prove.go
index a12a996263..f4a10b508a 100644
--- a/src/cmd/compile/internal/ssa/prove.go
+++ b/src/cmd/compile/internal/ssa/prove.go
@@ -445,9 +445,6 @@ var (
// else branch of the first comparison is executed, we already know that i < len(a).
// The code for the second panic can be removed.
func prove(f *Func) {
- idom := dominators(f)
- sdom := newSparseTree(f, idom)
-
// current node state
type walkState int
const (
@@ -471,8 +468,8 @@ func prove(f *Func) {
for len(work) > 0 {
node := work[len(work)-1]
work = work[:len(work)-1]
- parent := idom[node.block.ID]
- branch := getBranch(sdom, parent, node.block)
+ parent := f.idom[node.block.ID]
+ branch := getBranch(f.sdom, parent, node.block)
switch node.state {
case descend:
@@ -491,7 +488,7 @@ func prove(f *Func) {
block: node.block,
state: simplify,
})
- for s := sdom.Child(node.block); s != nil; s = sdom.Sibling(s) {
+ for s := f.sdom.Child(node.block); s != nil; s = f.sdom.Sibling(s) {
work = append(work, bp{
block: s,
state: descend,
diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go
index 22b9d12c19..65c25dfc5a 100644
--- a/src/cmd/compile/internal/ssa/regalloc.go
+++ b/src/cmd/compile/internal/ssa/regalloc.go
@@ -91,6 +91,18 @@
// will have no use (so don't run deadcode after regalloc!).
// TODO: maybe we should introduce these extra phis?
+// Additional not-quite-SSA output occurs when spills are sunk out
+// of loops to the targets of exit edges from the loop. Before sinking,
+// there is one spill site (one StoreReg) targeting stack slot X, after
+// sinking there may be multiple spill sites targeting stack slot X,
+// with no phi functions at any join points reachable by the multiple
+// spill sites. In addition, uses of the spill from copies of the original
+// will not name the copy in their reference; instead they will name
+// the original, though both will have the same spill location. The
+// first sunk spill will be the original, but moved, to an exit block,
+// thus ensuring that there is a definition somewhere corresponding to
+// the original spill's uses.
+
package ssa
import (
@@ -100,7 +112,8 @@ import (
)
const (
- logSpills = iota
+ moveSpills = iota
+ logSpills
regDebug
stackDebug
)
@@ -176,10 +189,9 @@ type valState struct {
uses *use // list of uses in this block
spill *Value // spilled copy of the Value
spillUsed bool
- needReg bool // cached value of !v.Type.IsMemory() && !v.Type.IsVoid() && !.v.Type.IsFlags()
- rematerializeable bool // cached value of v.rematerializeable()
- desired register // register we want value to be in, if any
- avoid regMask // registers to avoid if we can
+ spillUsedShuffle bool // true if used in shuffling, after ordinary uses
+ needReg bool // cached value of !v.Type.IsMemory() && !v.Type.IsVoid() && !.v.Type.IsFlags()
+ rematerializeable bool // cached value of v.rematerializeable()
}
type regState struct {
@@ -191,10 +203,11 @@ type regState struct {
type regAllocState struct {
f *Func
- registers []Register
- numRegs register
- SPReg register
- SBReg register
+ registers []Register
+ numRegs register
+ SPReg register
+ SBReg register
+ allocatable regMask
// for each block, its primary predecessor.
// A predecessor of b is primary if it is the closest
@@ -206,6 +219,11 @@ type regAllocState struct {
// which are live at the end of b, together with a count of how many instructions
// forward to the next use.
live [][]liveInfo
+ // desired register assignments at the end of each block.
+ // Note that this is a static map computed before allocation occurs. Dynamic
+ // register desires (from partially completed allocations) will trump
+ // this information.
+ desired []desiredState
// current state of each (preregalloc) Value
values []valState
@@ -243,6 +261,15 @@ type regAllocState struct {
loopnest *loopnest
}
+type spillToSink struct {
+ spill *Value // Spill instruction to move (a StoreReg)
+ dests int32 // Bitmask indicating exit blocks from loop in which spill/val is defined. 1<<i set means val is live into loop.exitBlocks[i]
+}
+
+func (sts *spillToSink) spilledValue() *Value {
+ return sts.spill.Args[0]
+}
+
type endReg struct {
r register
v *Value // pre-regalloc value held in this register (TODO: can we use ID here?)
@@ -310,6 +337,7 @@ func (s *regAllocState) assignReg(r register, v *Value, c *Value) {
// If there is no unused register, a Value will be kicked out of
// a register to make room.
func (s *regAllocState) allocReg(v *Value, mask regMask) register {
+ mask &= s.allocatable
mask &^= s.nospill
if mask == 0 {
s.f.Fatalf("no register available")
@@ -317,20 +345,7 @@ func (s *regAllocState) allocReg(v *Value, mask regMask) register {
// Pick an unused register if one is available.
if mask&^s.used != 0 {
- mask &^= s.used
-
- // Use desired register if we can.
- d := s.values[v.ID].desired
- if d != noRegister && mask>>d&1 != 0 {
- mask = regMask(1) << d
- }
-
- // Avoid avoidable registers if we can.
- if mask&^s.values[v.ID].avoid != 0 {
- mask &^= s.values[v.ID].avoid
- }
-
- return pickReg(mask)
+ return pickReg(mask &^ s.used)
}
// Pick a value to spill. Spill the value with the
@@ -340,10 +355,6 @@ func (s *regAllocState) allocReg(v *Value, mask regMask) register {
// TODO: if a single value is in multiple registers, spill one of them
// before spilling a value in just a single register.
- // SP and SB are allocated specially. No regular value should
- // be allocated to them.
- mask &^= 1<<s.SPReg | 1<<s.SBReg
-
// Find a register to spill. We spill the register containing the value
// whose next use is as far in the future as possible.
// https://en.wikipedia.org/wiki/Page_replacement_algorithm#The_theoretically_optimal_page_replacement_algorithm
@@ -389,14 +400,6 @@ func (s *regAllocState) allocValToReg(v *Value, mask regMask, nospill bool, line
return s.regs[r].c
}
- if v.Op != OpSP {
- mask &^= 1 << s.SPReg // dont' spill SP
- }
- if v.Op != OpSB {
- mask &^= 1 << s.SBReg // don't spill SB
- }
- mask &^= s.reserved()
-
// Allocate a register.
r := s.allocReg(v, mask)
@@ -417,7 +420,7 @@ func (s *regAllocState) allocValToReg(v *Value, mask regMask, nospill bool, line
// Load v from its spill location.
case vi.spill != nil:
if s.f.pass.debug > logSpills {
- s.f.Config.Warnl(vi.spill.Line, "load spill")
+ s.f.Config.Warnl(vi.spill.Line, "load spill for %v from %v", v, vi.spill)
}
c = s.curBlock.NewValue1(line, OpLoadReg, v.Type, vi.spill)
vi.spillUsed = true
@@ -434,6 +437,7 @@ func (s *regAllocState) allocValToReg(v *Value, mask regMask, nospill bool, line
}
func (s *regAllocState) init(f *Func) {
+ s.f = f
s.registers = f.Config.registers
s.numRegs = register(len(s.registers))
if s.numRegs > noRegister || s.numRegs > register(unsafe.Sizeof(regMask(0))*8) {
@@ -448,7 +452,17 @@ func (s *regAllocState) init(f *Func) {
}
}
- s.f = f
+ // Figure out which registers we're allowed to use.
+ s.allocatable = regMask(1)<<s.numRegs - 1
+ s.allocatable &^= 1 << s.SPReg
+ s.allocatable &^= 1 << s.SBReg
+ if obj.Framepointer_enabled != 0 {
+ s.allocatable &^= 1 << 5 // BP
+ }
+ if s.f.Config.ctxt.Flag_dynlink {
+ s.allocatable &^= 1 << 15 // R15
+ }
+
s.regs = make([]regState, s.numRegs)
s.values = make([]valState, f.NumValues())
s.orig = make([]*Value, f.NumValues())
@@ -457,7 +471,6 @@ func (s *regAllocState) init(f *Func) {
if !v.Type.IsMemory() && !v.Type.IsVoid() && !v.Type.IsFlags() {
s.values[v.ID].needReg = true
s.values[v.ID].rematerializeable = v.rematerializeable()
- s.values[v.ID].desired = noRegister
s.orig[v.ID] = v
}
}
@@ -527,6 +540,18 @@ func (s *regAllocState) advanceUses(v *Value) {
}
}
+// liveAfterCurrentInstruction reports whether v is live after
+// the current instruction is completed. v must be used by the
+// current instruction.
+func (s *regAllocState) liveAfterCurrentInstruction(v *Value) bool {
+ u := s.values[v.ID].uses
+ d := u.dist
+ for u != nil && u.dist == d {
+ u = u.next
+ }
+ return u != nil && u.dist > d
+}
+
// Sets the state of the registers to that encoded in regs.
func (s *regAllocState) setState(regs []endReg) {
s.freeRegs(s.used)
@@ -541,9 +566,25 @@ func (s *regAllocState) compatRegs(t Type) regMask {
if t.IsFloat() || t == TypeInt128 {
m = 0xffff << 16 // X0-X15
} else {
- m = 0xffef << 0 // AX-R15, except SP
+ m = 0xffff << 0 // AX-R15
+ }
+ return m & s.allocatable
+}
+
+// loopForBlock returns the loop containing block b,
+// provided that the loop is "interesting" for purposes
+// of improving register allocation (= is inner, and does
+// not contain a call)
+func (s *regAllocState) loopForBlock(b *Block) *loop {
+ loop := s.loopnest.b2l[b.ID]
+
+ // Minor for-the-time-being optimization: nothing happens
+ // unless a loop is both inner and call-free, therefore
+ // don't bother with other loops.
+ if loop != nil && (loop.containsCall || !loop.isInner) {
+ loop = nil
}
- return m &^ s.reserved()
+ return loop
}
func (s *regAllocState) regalloc(f *Func) {
@@ -554,12 +595,46 @@ func (s *regAllocState) regalloc(f *Func) {
var phiRegs []register
var args []*Value
+ // statistics
+ var nSpills int // # of spills remaining
+ var nSpillsInner int // # of spills remaining in inner loops
+ var nSpillsSunk int // # of sunk spills remaining
+ var nSpillsChanged int // # of sunk spills lost because of register use change
+ var nSpillsSunkUnused int // # of spills not sunk because they were removed completely
+ var nSpillsNotSunkLateUse int // # of spills not sunk because of very late use (in shuffle)
+
+ // Data structure used for computing desired registers.
+ var desired desiredState
+
+ // Desired registers for inputs & outputs for each instruction in the block.
+ type dentry struct {
+ out [4]register // desired output registers
+ in [3][4]register // desired input registers (for inputs 0,1, and 2)
+ }
+ var dinfo []dentry
+
if f.Entry != f.Blocks[0] {
f.Fatalf("entry block must be first")
}
+ // Get loop nest so that spills in inner loops can be
+ // tracked. When the last block of a loop is processed,
+ // attempt to move spills out of the loop.
+ s.loopnest.findExits()
+
+ // Spills are moved from one block's slice of values to another's.
+ // This confuses register allocation if it occurs before it is
+ // complete, so candidates are recorded, then rechecked and
+ // moved after all allocation (register and stack) is complete.
+ // Because movement is only within a stack slot's lifetime, it
+ // is safe to do this.
+ var toSink []spillToSink
+ // Will be used to figure out live inputs to exit blocks of inner loops.
+ entryCandidates := newSparseMap(f.NumValues())
+
for _, b := range f.Blocks {
s.curBlock = b
+ loop := s.loopForBlock(b)
// Initialize liveSet and uses fields for this block.
// Walk backwards through the block doing liveness analysis.
@@ -739,6 +814,11 @@ func (s *regAllocState) regalloc(f *Func) {
s.setOrig(spill, v)
s.values[v.ID].spill = spill
s.values[v.ID].spillUsed = false
+ if loop != nil {
+ loop.spills = append(loop.spills, v)
+ nSpillsInner++
+ }
+ nSpills++
}
// Save the starting state for use by merge edges.
@@ -765,26 +845,27 @@ func (s *regAllocState) regalloc(f *Func) {
}
}
- // Compute preferred registers for each value using a backwards pass.
+ // Allocate space to record the desired registers for each value.
+ dinfo = dinfo[:0]
+ for i := 0; i < len(oldSched); i++ {
+ dinfo = append(dinfo, dentry{})
+ }
+
+ // Load static desired register info at the end of the block.
+ desired.copy(&s.desired[b.ID])
+
+ // Check actual assigned registers at the start of the next block(s).
+ // Dynamically assigned registers will trump the static
+ // desired registers computed during liveness analysis.
// Note that we do this phase after startRegs is set above, so that
// we get the right behavior for a block which branches to itself.
for _, succ := range b.Succs {
- // TODO: prioritize likely successor.
+ // TODO: prioritize likely successor?
for _, x := range s.startRegs[succ.ID] {
- v := s.orig[x.vid]
- s.values[v.ID].desired = x.r
- }
- // Process phi ops in succ
- i := -1
- for j, p := range succ.Preds {
- if p == b {
- i = j
- break
- }
- }
- if i == -1 {
- s.f.Fatalf("can't find predecssor %s of %s\n", b, succ)
+ desired.add(x.vid, x.r)
}
+ // Process phi ops in succ.
+ pidx := predIdx(succ, b)
for _, v := range succ.Values {
if v.Op != OpPhi {
break
@@ -792,47 +873,44 @@ func (s *regAllocState) regalloc(f *Func) {
if !s.values[v.ID].needReg {
continue
}
- r, ok := s.f.getHome(v.ID).(*Register)
+ rp, ok := s.f.getHome(v.ID).(*Register)
if !ok {
continue
}
- a := s.orig[v.Args[i].ID]
- s.values[a.ID].desired = register(r.Num)
+ desired.add(v.Args[pidx].ID, register(rp.Num))
}
}
-
- // Set avoid fields to help desired register availability.
- liveSet.clear()
- for _, e := range s.live[b.ID] {
- liveSet.add(e.ID)
- }
- if v := b.Control; v != nil && s.values[v.ID].needReg {
- liveSet.add(v.ID)
- }
+ // Walk values backwards computing desired register info.
+ // See computeLive for more comments.
for i := len(oldSched) - 1; i >= 0; i-- {
v := oldSched[i]
- liveSet.remove(v.ID)
-
- r := s.values[v.ID].desired
- if r != noRegister {
- m := regMask(1) << r
- // All live values should avoid this register so
- // it will be available at this point.
- for _, w := range liveSet.contents() {
- s.values[w].avoid |= m
+ prefs := desired.remove(v.ID)
+ desired.clobber(opcodeTable[v.Op].reg.clobbers)
+ for _, j := range opcodeTable[v.Op].reg.inputs {
+ if countRegs(j.regs) != 1 {
+ continue
}
+ desired.clobber(j.regs)
+ desired.add(v.Args[j.idx].ID, pickReg(j.regs))
}
-
- for _, a := range v.Args {
- if !s.values[a.ID].needReg {
- continue
+ if opcodeTable[v.Op].resultInArg0 {
+ if opcodeTable[v.Op].commutative {
+ desired.addList(v.Args[1].ID, prefs)
}
- liveSet.add(a.ID)
+ desired.addList(v.Args[0].ID, prefs)
+ }
+ // Save desired registers for this value.
+ dinfo[i].out = prefs
+ for j, a := range v.Args {
+ if j >= len(dinfo[i].in) {
+ break
+ }
+ dinfo[i].in[j] = desired.get(a.ID)
}
}
// Process all the non-phi values.
- for _, v := range oldSched {
+ for idx, v := range oldSched {
if s.f.pass.debug > regDebug {
fmt.Printf(" processing %s\n", v.LongString())
}
@@ -880,15 +958,132 @@ func (s *regAllocState) regalloc(f *Func) {
continue
}
+ if s.f.pass.debug > regDebug {
+ fmt.Printf("value %s\n", v.LongString())
+ fmt.Printf(" out:")
+ for _, r := range dinfo[idx].out {
+ if r != noRegister {
+ fmt.Printf(" %s", s.registers[r].Name())
+ }
+ }
+ fmt.Println()
+ for i := 0; i < len(v.Args) && i < 3; i++ {
+ fmt.Printf(" in%d:", i)
+ for _, r := range dinfo[idx].in[i] {
+ if r != noRegister {
+ fmt.Printf(" %s", s.registers[r].Name())
+ }
+ }
+ fmt.Println()
+ }
+ }
+
// Move arguments to registers. Process in an ordering defined
// by the register specification (most constrained first).
args = append(args[:0], v.Args...)
for _, i := range regspec.inputs {
- if i.regs == flagRegMask {
+ mask := i.regs
+ if mask == flagRegMask {
// TODO: remove flag input from regspec.inputs.
continue
}
- args[i.idx] = s.allocValToReg(v.Args[i.idx], i.regs, true, v.Line)
+ if mask&s.values[args[i.idx].ID].regs == 0 {
+ // Need a new register for the input.
+ mask &= s.allocatable
+ mask &^= s.nospill
+ // Used desired register if available.
+ if i.idx < 3 {
+ for _, r := range dinfo[idx].in[i.idx] {
+ if r != noRegister && (mask&^s.used)>>r&1 != 0 {
+ // Desired register is allowed and unused.
+ mask = regMask(1) << r
+ break
+ }
+ }
+ }
+ // Avoid registers we're saving for other values.
+ if mask&^desired.avoid != 0 {
+ mask &^= desired.avoid
+ }
+ }
+ args[i.idx] = s.allocValToReg(args[i.idx], mask, true, v.Line)
+ }
+
+ // If the output clobbers the input register, make sure we have
+ // at least two copies of the input register so we don't
+ // have to reload the value from the spill location.
+ if opcodeTable[v.Op].resultInArg0 {
+ var m regMask
+ if !s.liveAfterCurrentInstruction(v.Args[0]) {
+ // arg0 is dead. We can clobber its register.
+ goto ok
+ }
+ if countRegs(s.values[v.Args[0].ID].regs) >= 2 {
+ // we have at least 2 copies of arg0. We can afford to clobber one.
+ goto ok
+ }
+ if opcodeTable[v.Op].commutative {
+ if !s.liveAfterCurrentInstruction(v.Args[1]) {
+ args[0], args[1] = args[1], args[0]
+ goto ok
+ }
+ if countRegs(s.values[v.Args[1].ID].regs) >= 2 {
+ args[0], args[1] = args[1], args[0]
+ goto ok
+ }
+ }
+
+ // We can't overwrite arg0 (or arg1, if commutative). So we
+ // need to make a copy of an input so we have a register we can modify.
+
+ // Possible new registers to copy into.
+ m = s.compatRegs(v.Args[0].Type) &^ s.used
+ if m == 0 {
+ // No free registers. In this case we'll just clobber
+ // an input and future uses of that input must use a restore.
+ // TODO(khr): We should really do this like allocReg does it,
+ // spilling the value with the most distant next use.
+ goto ok
+ }
+
+ // Try to move an input to the desired output.
+ for _, r := range dinfo[idx].out {
+ if r != noRegister && m>>r&1 != 0 {
+ m = regMask(1) << r
+ args[0] = s.allocValToReg(v.Args[0], m, true, v.Line)
+ // Note: we update args[0] so the instruction will
+ // use the register copy we just made.
+ goto ok
+ }
+ }
+ // Try to copy input to its desired location & use its old
+ // location as the result register.
+ for _, r := range dinfo[idx].in[0] {
+ if r != noRegister && m>>r&1 != 0 {
+ m = regMask(1) << r
+ s.allocValToReg(v.Args[0], m, true, v.Line)
+ // Note: no update to args[0] so the instruction will
+ // use the original copy.
+ goto ok
+ }
+ }
+ if opcodeTable[v.Op].commutative {
+ for _, r := range dinfo[idx].in[1] {
+ if r != noRegister && m>>r&1 != 0 {
+ m = regMask(1) << r
+ s.allocValToReg(v.Args[1], m, true, v.Line)
+ args[0], args[1] = args[1], args[0]
+ goto ok
+ }
+ }
+ }
+ // Avoid future fixed uses if we can.
+ if m&^desired.avoid != 0 {
+ m &^= desired.avoid
+ }
+ // Save input 0 to a new register so we can clobber it.
+ s.allocValToReg(v.Args[0], m, true, v.Line)
+ ok:
}
// Now that all args are in regs, we're ready to issue the value itself.
@@ -903,24 +1098,44 @@ func (s *regAllocState) regalloc(f *Func) {
// Pick register for output.
if s.values[v.ID].needReg {
- mask := regspec.outputs[0] &^ s.reserved()
- if mask>>33&1 != 0 {
- s.f.Fatalf("bad mask %s\n", v.LongString())
- }
+ mask := regspec.outputs[0] & s.allocatable
if opcodeTable[v.Op].resultInArg0 {
- r := register(s.f.getHome(args[0].ID).(*Register).Num)
- if (mask&^s.used)>>r&1 != 0 {
+ if !opcodeTable[v.Op].commutative {
+ // Output must use the same register as input 0.
+ r := register(s.f.getHome(args[0].ID).(*Register).Num)
mask = regMask(1) << r
- }
- if opcodeTable[v.Op].commutative {
- r := register(s.f.getHome(args[1].ID).(*Register).Num)
- if (mask&^s.used)>>r&1 != 0 {
- mask = regMask(1) << r
+ } else {
+ // Output must use the same register as input 0 or 1.
+ r0 := register(s.f.getHome(args[0].ID).(*Register).Num)
+ r1 := register(s.f.getHome(args[1].ID).(*Register).Num)
+ // Check r0 and r1 for desired output register.
+ found := false
+ for _, r := range dinfo[idx].out {
+ if (r == r0 || r == r1) && (mask&^s.used)>>r&1 != 0 {
+ mask = regMask(1) << r
+ found = true
+ if r == r1 {
+ args[0], args[1] = args[1], args[0]
+ }
+ break
+ }
+ }
+ if !found {
+ // Neither are desired, pick r0.
+ mask = regMask(1) << r0
}
}
- // TODO: enforce resultInArg0 always, instead of treating it
- // as a hint. Then we don't need the special cases adding
- // moves all throughout ssa.go:genValue.
+ }
+ for _, r := range dinfo[idx].out {
+ if r != noRegister && (mask&^s.used)>>r&1 != 0 {
+ // Desired register is allowed and unused.
+ mask = regMask(1) << r
+ break
+ }
+ }
+ // Avoid registers we're saving for other values.
+ if mask&^desired.avoid != 0 {
+ mask &^= desired.avoid
}
r := s.allocReg(v, mask)
s.assignReg(r, v, v)
@@ -947,6 +1162,11 @@ func (s *regAllocState) regalloc(f *Func) {
s.setOrig(spill, v)
s.values[v.ID].spill = spill
s.values[v.ID].spillUsed = false
+ if loop != nil {
+ loop.spills = append(loop.spills, v)
+ nSpillsInner++
+ }
+ nSpills++
}
}
@@ -993,6 +1213,9 @@ func (s *regAllocState) regalloc(f *Func) {
}
v := s.orig[vid]
m := s.compatRegs(v.Type) &^ s.used
+ if m&^desired.avoid != 0 {
+ m &^= desired.avoid
+ }
if m != 0 {
s.allocValToReg(v, m, false, b.Line)
}
@@ -1056,6 +1279,69 @@ func (s *regAllocState) regalloc(f *Func) {
s.values[e.ID].spillUsed = true
}
+ // Keep track of values that are spilled in the loop, but whose spill
+ // is not used in the loop. It may be possible to move ("sink") the
+ // spill out of the loop into one or more exit blocks.
+ if loop != nil {
+ loop.scratch++ // increment count of blocks in this loop that have been processed
+ if loop.scratch == loop.nBlocks { // just processed last block of loop, if it is an inner loop.
+ // This check is redundant with code at the top of the loop.
+ // This is definitive; the one at the top of the loop is an optimization.
+ if loop.isInner && // Common case, easier, most likely to be profitable
+ !loop.containsCall && // Calls force spills, also lead to puzzling spill info.
+ len(loop.exits) <= 32 { // Almost no inner loops have more than 32 exits,
+ // and this allows use of a bitvector and a sparseMap.
+
+ // TODO: exit calculation is messed up for non-inner loops
+ // because of multilevel exits that are not part of the "exit"
+ // count.
+
+ // Compute the set of spill-movement candidates live at entry to exit blocks.
+ // isLoopSpillCandidate filters for
+ // (1) defined in appropriate loop
+ // (2) needs a register
+ // (3) spill not already used (in the loop)
+ // Condition (3) === "in a register at all loop exits"
+
+ entryCandidates.clear()
+
+ for whichExit, ss := range loop.exits {
+ // Start with live at end.
+ for _, li := range s.live[ss.ID] {
+ if s.isLoopSpillCandidate(loop, s.orig[li.ID]) {
+ entryCandidates.setBit(li.ID, uint(whichExit))
+ }
+ }
+ // Control can also be live.
+ if ss.Control != nil && s.isLoopSpillCandidate(loop, ss.Control) {
+ entryCandidates.setBit(ss.Control.ID, uint(whichExit))
+ }
+ // Walk backwards, filling in locally live values, removing those defined.
+ for i := len(ss.Values) - 1; i >= 0; i-- {
+ v := ss.Values[i]
+ entryCandidates.remove(v.ID) // Cannot be an issue, only keeps the sets smaller.
+ for _, a := range v.Args {
+ if s.isLoopSpillCandidate(loop, a) {
+ entryCandidates.setBit(a.ID, uint(whichExit))
+ }
+ }
+ }
+ }
+
+ for _, e := range loop.spills {
+ whichblocks := entryCandidates.get(e.ID)
+ oldSpill := s.values[e.ID].spill
+ if whichblocks != 0 && whichblocks != -1 { // -1 = not in map.
+ toSink = append(toSink, spillToSink{spill: oldSpill, dests: whichblocks})
+ }
+ }
+
+ } // loop is inner etc
+ loop.scratch = 0 // Don't leave a mess, just in case.
+ loop.spills = nil
+ } // if scratch == nBlocks
+ } // if loop is not nil
+
// Clear any final uses.
// All that is left should be the pseudo-uses added for values which
// are live at the end of b.
@@ -1078,7 +1364,7 @@ func (s *regAllocState) regalloc(f *Func) {
vi := s.values[i]
if vi.spillUsed {
if s.f.pass.debug > logSpills {
- s.f.Config.Warnl(vi.spill.Line, "spilled value")
+ s.f.Config.Warnl(vi.spill.Line, "spilled value at %v remains", vi.spill)
}
continue
}
@@ -1087,9 +1373,16 @@ func (s *regAllocState) regalloc(f *Func) {
// Constants, SP, SB, ...
continue
}
+ loop := s.loopForBlock(spill.Block)
+ if loop != nil {
+ nSpillsInner--
+ }
+
spill.Args[0].Uses--
f.freeValue(spill)
+ nSpills--
}
+
for _, b := range f.Blocks {
i := 0
for _, v := range b.Values {
@@ -1104,12 +1397,161 @@ func (s *regAllocState) regalloc(f *Func) {
// Not important now because this is the last phase that manipulates Values
}
+ // Must clear these out before any potential recycling, though that's
+ // not currently implemented.
+ for i, ts := range toSink {
+ vsp := ts.spill
+ if vsp.Op == OpInvalid { // This spill was completely eliminated
+ toSink[i].spill = nil
+ }
+ }
+
// Anything that didn't get a register gets a stack location here.
// (StoreReg, stack-based phis, inputs, ...)
stacklive := stackalloc(s.f, s.spillLive)
// Fix up all merge edges.
s.shuffle(stacklive)
+
+ // Insert moved spills (that have not been marked invalid above)
+ // at start of appropriate block and remove the originals from their
+ // location within loops. Notice that this can break SSA form;
+ // if a spill is sunk to multiple exits, there will be no phi for that
+ // spill at a join point downstream of those two exits, though the
+ // two spills will target the same stack slot. Notice also that this
+ // takes place after stack allocation, so the stack allocator does
+ // not need to process these malformed flow graphs.
+sinking:
+ for _, ts := range toSink {
+ vsp := ts.spill
+ if vsp == nil { // This spill was completely eliminated
+ nSpillsSunkUnused++
+ continue sinking
+ }
+ e := ts.spilledValue()
+ if s.values[e.ID].spillUsedShuffle {
+ nSpillsNotSunkLateUse++
+ continue sinking
+ }
+
+ // move spills to a better (outside of loop) block.
+ // This would be costly if it occurred very often, but it doesn't.
+ b := vsp.Block
+ loop := s.loopnest.b2l[b.ID]
+ dests := ts.dests
+
+ // Pre-check to be sure that spilled value is still in expected register on all exits where live.
+ check_val_still_in_reg:
+ for i := uint(0); i < 32 && dests != 0; i++ {
+
+ if dests&(1<<i) == 0 {
+ continue
+ }
+ dests ^= 1 << i
+ d := loop.exits[i]
+ if len(d.Preds) > 1 {
+ panic("Should be impossible given critical edges removed")
+ }
+ p := d.Preds[0] // block in loop exiting to d.
+
+ endregs := s.endRegs[p.ID]
+ for _, regrec := range endregs {
+ if regrec.v == e && regrec.r != noRegister && regrec.c == e { // TODO: regrec.c != e implies different spill possible.
+ continue check_val_still_in_reg
+ }
+ }
+ // If here, the register assignment was lost down at least one exit and it can't be sunk
+ if s.f.pass.debug > moveSpills {
+ s.f.Config.Warnl(e.Line, "lost register assignment for spill %v in %v at exit %v to %v",
+ vsp, b, p, d)
+ }
+ nSpillsChanged++
+ continue sinking
+ }
+
+ nSpillsSunk++
+ nSpillsInner--
+ // don't update nSpills, since spill is only moved, and if it is duplicated, the spills-on-a-path is not increased.
+
+ dests = ts.dests
+
+ // remove vsp from b.Values
+ i := 0
+ for _, w := range b.Values {
+ if vsp == w {
+ continue
+ }
+ b.Values[i] = w
+ i++
+ }
+ b.Values = b.Values[:i]
+
+ first := true
+ for i := uint(0); i < 32 && dests != 0; i++ {
+
+ if dests&(1<<i) == 0 {
+ continue
+ }
+
+ dests ^= 1 << i
+
+ d := loop.exits[i]
+ vspnew := vsp // reuse original for first sunk spill, saves tracking down and renaming uses
+ if !first { // any sunk spills after first must make a copy
+ vspnew = d.NewValue1(e.Line, OpStoreReg, e.Type, e)
+ f.setHome(vspnew, f.getHome(vsp.ID)) // copy stack home
+ if s.f.pass.debug > moveSpills {
+ s.f.Config.Warnl(e.Line, "copied spill %v in %v for %v to %v in %v",
+ vsp, b, e, vspnew, d)
+ }
+ } else {
+ first = false
+ vspnew.Block = d
+ d.Values = append(d.Values, vspnew)
+ if s.f.pass.debug > moveSpills {
+ s.f.Config.Warnl(e.Line, "moved spill %v in %v for %v to %v in %v",
+ vsp, b, e, vspnew, d)
+ }
+ }
+
+ // shuffle vspnew to the beginning of its block
+ copy(d.Values[1:], d.Values[0:len(d.Values)-1])
+ d.Values[0] = vspnew
+
+ }
+ }
+
+ if f.pass.stats > 0 {
+ f.logStat("spills_info",
+ nSpills, "spills", nSpillsInner, "inner_spills_remaining", nSpillsSunk, "inner_spills_sunk", nSpillsSunkUnused, "inner_spills_unused", nSpillsNotSunkLateUse, "inner_spills_shuffled", nSpillsChanged, "inner_spills_changed")
+ }
+}
+
+// isLoopSpillCandidate indicates whether the spill for v satisfies preliminary
+// spill-sinking conditions just after the last block of loop has been processed.
+// In particular:
+// v needs a register.
+// v's spill is not (YET) used.
+// v's definition is within loop.
+// The spill may be used in the future, either by an outright use
+// in the code, or by shuffling code inserted after stack allocation.
+// Outright uses cause sinking; shuffling (within the loop) inhibits it.
+func (s *regAllocState) isLoopSpillCandidate(loop *loop, v *Value) bool {
+ return s.values[v.ID].needReg && !s.values[v.ID].spillUsed && s.loopnest.b2l[v.Block.ID] == loop
+}
+
+// lateSpillUse notes a late (after stack allocation) use of the spill of value with ID vid.
+// This will inhibit spill sinking.
+func (s *regAllocState) lateSpillUse(vid ID) {
+ // TODO investigate why this is necessary.
+ // It appears that an outside-the-loop use of
+ // an otherwise sinkable spill makes the spill
+ // a candidate for shuffling, when it would not
+ // otherwise have been the case (spillUsed was not
+ // true when isLoopSpillCandidate was called, yet
+ // it was shuffled). Such shuffling cuts the amount
+ // of spill sinking by more than half (in make.bash)
+ s.values[vid].spillUsedShuffle = true
}
// shuffle fixes up all the merge edges (those going into blocks of indegree > 1).
@@ -1284,6 +1726,7 @@ func (e *edgeState) process() {
if _, isReg := loc.(*Register); isReg {
c = e.p.NewValue1(c.Line, OpCopy, c.Type, c)
} else {
+ e.s.lateSpillUse(vid)
c = e.p.NewValue1(c.Line, OpLoadReg, c.Type, c)
}
e.set(r, vid, c, false)
@@ -1372,6 +1815,7 @@ func (e *edgeState) processDest(loc Location, vid ID, splice **Value) bool {
}
} else {
if dstReg {
+ e.s.lateSpillUse(vid)
x = e.p.NewValue1(c.Line, OpLoadReg, c.Type, c)
} else {
// mem->mem. Use temp register.
@@ -1389,6 +1833,7 @@ func (e *edgeState) processDest(loc Location, vid ID, splice **Value) bool {
e.erase(loc)
r := e.findRegFor(c.Type)
+ e.s.lateSpillUse(vid)
t := e.p.NewValue1(c.Line, OpLoadReg, c.Type, c)
e.set(r, vid, t, false)
x = e.p.NewValue1(c.Line, OpStoreReg, loc.(LocalSlot).Type, t)
@@ -1554,24 +1999,36 @@ func (v *Value) rematerializeable() bool {
}
type liveInfo struct {
- ID ID // ID of variable
+ ID ID // ID of value
dist int32 // # of instructions before next use
}
+// dblock contains information about desired & avoid registers at the end of a block.
+type dblock struct {
+ prefers []desiredStateEntry
+ avoid regMask
+}
+
// computeLive computes a map from block ID to a list of value IDs live at the end
// of that block. Together with the value ID is a count of how many instructions
-// to the next use of that value. The resulting map is stored at s.live.
+// to the next use of that value. The resulting map is stored in s.live.
+// computeLive also computes the desired register information at the end of each block.
+// This desired register information is stored in s.desired.
// TODO: this could be quadratic if lots of variables are live across lots of
// basic blocks. Figure out a way to make this function (or, more precisely, the user
// of this function) require only linear size & time.
func (s *regAllocState) computeLive() {
f := s.f
s.live = make([][]liveInfo, f.NumBlocks())
+ s.desired = make([]desiredState, f.NumBlocks())
var phis []*Value
live := newSparseMap(f.NumValues())
t := newSparseMap(f.NumValues())
+ // Keep track of which value we want in each register.
+ var desired desiredState
+
// Instead of iterating over f.Blocks, iterate over their postordering.
// Liveness information flows backward, so starting at the end
// increases the probability that we will stabilize quickly.
@@ -1594,7 +2051,7 @@ func (s *regAllocState) computeLive() {
d := int32(len(b.Values))
if b.Kind == BlockCall || b.Kind == BlockDefer {
// Because we keep no values in registers across a call,
- // make every use past a call very far away.
+ // make every use past a call appear very far away.
d += unlikelyDistance
}
for _, e := range s.live[b.ID] {
@@ -1623,6 +2080,35 @@ func (s *regAllocState) computeLive() {
}
}
}
+ // Propagate desired registers backwards.
+ desired.copy(&s.desired[b.ID])
+ for i := len(b.Values) - 1; i >= 0; i-- {
+ v := b.Values[i]
+ prefs := desired.remove(v.ID)
+ if v.Op == OpPhi {
+ // TODO: if v is a phi, save desired register for phi inputs.
+ // For now, we just drop it and don't propagate
+ // desired registers back though phi nodes.
+ continue
+ }
+ // Cancel desired registers if they get clobbered.
+ desired.clobber(opcodeTable[v.Op].reg.clobbers)
+ // Update desired registers if there are any fixed register inputs.
+ for _, j := range opcodeTable[v.Op].reg.inputs {
+ if countRegs(j.regs) != 1 {
+ continue
+ }
+ desired.clobber(j.regs)
+ desired.add(v.Args[j.idx].ID, pickReg(j.regs))
+ }
+ // Set desired register of input 0 if this is a 2-operand instruction.
+ if opcodeTable[v.Op].resultInArg0 {
+ if opcodeTable[v.Op].commutative {
+ desired.addList(v.Args[1].ID, prefs)
+ }
+ desired.addList(v.Args[0].ID, prefs)
+ }
+ }
// For each predecessor of b, expand its list of live-at-end values.
// invariant: live contains the values live at the start of b (excluding phi inputs)
@@ -1642,6 +2128,9 @@ func (s *regAllocState) computeLive() {
}
}
+ // Update any desired registers at the end of p.
+ s.desired[p.ID].merge(&desired)
+
// Start t off with the previously known live values at the end of p.
t.clear()
for _, e := range s.live[p.ID] {
@@ -1662,7 +2151,7 @@ func (s *regAllocState) computeLive() {
// simultaneously happening at the start of the block).
for _, v := range phis {
id := v.Args[i].ID
- if s.values[id].needReg && !t.contains(id) || delta < t.get(id) {
+ if s.values[id].needReg && (!t.contains(id) || delta < t.get(id)) {
update = true
t.set(id, delta)
}
@@ -1694,20 +2183,152 @@ func (s *regAllocState) computeLive() {
fmt.Printf(" %s:", b)
for _, x := range s.live[b.ID] {
fmt.Printf(" v%d", x.ID)
+ for _, e := range s.desired[b.ID].entries {
+ if e.ID != x.ID {
+ continue
+ }
+ fmt.Printf("[")
+ first := true
+ for _, r := range e.regs {
+ if r == noRegister {
+ continue
+ }
+ if !first {
+ fmt.Printf(",")
+ }
+ fmt.Print(s.registers[r].Name())
+ first = false
+ }
+ fmt.Printf("]")
+ }
}
+ fmt.Printf(" avoid=%x", int64(s.desired[b.ID].avoid))
fmt.Println()
}
}
}
-// reserved returns a mask of reserved registers.
-func (s *regAllocState) reserved() regMask {
- var m regMask
- if obj.Framepointer_enabled != 0 {
- m |= 1 << 5 // BP
+// A desiredState represents desired register assignments.
+type desiredState struct {
+ // Desired assignments will be small, so we just use a list
+ // of valueID+registers entries.
+ entries []desiredStateEntry
+ // Registers that other values want to be in. This value will
+ // contain at least the union of the regs fields of entries, but
+ // may contain additional entries for values that were once in
+ // this data structure but are no longer.
+ avoid regMask
+}
+type desiredStateEntry struct {
+ // (pre-regalloc) value
+ ID ID
+ // Registers it would like to be in, in priority order.
+ // Unused slots are filled with noRegister.
+ regs [4]register
+}
+
+func (d *desiredState) clear() {
+ d.entries = d.entries[:0]
+ d.avoid = 0
+}
+
+// get returns a list of desired registers for value vid.
+func (d *desiredState) get(vid ID) [4]register {
+ for _, e := range d.entries {
+ if e.ID == vid {
+ return e.regs
+ }
}
- if s.f.Config.ctxt.Flag_dynlink {
- m |= 1 << 15 // R15
+ return [4]register{noRegister, noRegister, noRegister, noRegister}
+}
+
+// add records that we'd like value vid to be in register r.
+func (d *desiredState) add(vid ID, r register) {
+ d.avoid |= regMask(1) << r
+ for i := range d.entries {
+ e := &d.entries[i]
+ if e.ID != vid {
+ continue
+ }
+ if e.regs[0] == r {
+ // Already known and highest priority
+ return
+ }
+ for j := 1; j < len(e.regs); j++ {
+ if e.regs[j] == r {
+ // Move from lower priority to top priority
+ copy(e.regs[1:], e.regs[:j])
+ e.regs[0] = r
+ return
+ }
+ }
+ copy(e.regs[1:], e.regs[:])
+ e.regs[0] = r
+ return
+ }
+ d.entries = append(d.entries, desiredStateEntry{vid, [4]register{r, noRegister, noRegister, noRegister}})
+}
+
+func (d *desiredState) addList(vid ID, regs [4]register) {
+ // regs is in priority order, so iterate in reverse order.
+ for i := len(regs) - 1; i >= 0; i-- {
+ r := regs[i]
+ if r != noRegister {
+ d.add(vid, r)
+ }
+ }
+}
+
+// clobber erases any desired registers in the set m.
+func (d *desiredState) clobber(m regMask) {
+ for i := 0; i < len(d.entries); {
+ e := &d.entries[i]
+ j := 0
+ for _, r := range e.regs {
+ if r != noRegister && m>>r&1 == 0 {
+ e.regs[j] = r
+ j++
+ }
+ }
+ if j == 0 {
+ // No more desired registers for this value.
+ d.entries[i] = d.entries[len(d.entries)-1]
+ d.entries = d.entries[:len(d.entries)-1]
+ continue
+ }
+ for ; j < len(e.regs); j++ {
+ e.regs[j] = noRegister
+ }
+ i++
+ }
+ d.avoid &^= m
+}
+
+// copy copies a desired state from another desiredState x.
+func (d *desiredState) copy(x *desiredState) {
+ d.entries = append(d.entries[:0], x.entries...)
+ d.avoid = x.avoid
+}
+
+// remove removes the desired registers for vid and returns them.
+func (d *desiredState) remove(vid ID) [4]register {
+ for i := range d.entries {
+ if d.entries[i].ID == vid {
+ regs := d.entries[i].regs
+ d.entries[i] = d.entries[len(d.entries)-1]
+ d.entries = d.entries[:len(d.entries)-1]
+ return regs
+ }
+ }
+ return [4]register{noRegister, noRegister, noRegister, noRegister}
+}
+
+// merge merges another desired state x into d.
+func (d *desiredState) merge(x *desiredState) {
+ d.avoid |= x.avoid
+ // There should only be a few desired registers, so
+ // linear insert is ok.
+ for _, e := range x.entries {
+ d.addList(e.ID, e.regs)
}
- return m
}
diff --git a/src/cmd/compile/internal/ssa/regalloc_test.go b/src/cmd/compile/internal/ssa/regalloc_test.go
index 6f3f690f1e..cf8f452d12 100644
--- a/src/cmd/compile/internal/ssa/regalloc_test.go
+++ b/src/cmd/compile/internal/ssa/regalloc_test.go
@@ -11,8 +11,8 @@ func TestLiveControlOps(t *testing.T) {
f := Fun(c, "entry",
Bloc("entry",
Valu("mem", OpInitMem, TypeMem, 0, nil),
- Valu("x", OpAMD64MOVBconst, TypeInt8, 1, nil),
- Valu("y", OpAMD64MOVBconst, TypeInt8, 2, nil),
+ Valu("x", OpAMD64MOVLconst, TypeInt8, 1, nil),
+ Valu("y", OpAMD64MOVLconst, TypeInt8, 2, nil),
Valu("a", OpAMD64TESTB, TypeFlags, 0, nil, "x", "y"),
Valu("b", OpAMD64TESTB, TypeFlags, 0, nil, "y", "x"),
Eq("a", "if", "exit"),
diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go
index e0cb7f517b..e9b408a86c 100644
--- a/src/cmd/compile/internal/ssa/rewrite.go
+++ b/src/cmd/compile/internal/ssa/rewrite.go
@@ -40,9 +40,44 @@ func applyRewrite(f *Func, rb func(*Block) bool, rv func(*Value, *Config) bool)
}
curb = nil
for _, v := range b.Values {
- change = copyelimValue(v) || change
change = phielimValue(v) || change
+ // Eliminate copy inputs.
+ // If any copy input becomes unused, mark it
+ // as invalid and discard its argument. Repeat
+ // recursively on the discarded argument.
+ // This phase helps remove phantom "dead copy" uses
+ // of a value so that a x.Uses==1 rule condition
+ // fires reliably.
+ for i, a := range v.Args {
+ if a.Op != OpCopy {
+ continue
+ }
+ x := a.Args[0]
+ // Rewriting can generate OpCopy loops.
+ // They are harmless (see removePredecessor),
+ // but take care to stop if we find a cycle.
+ slow := x // advances every other iteration
+ var advance bool
+ for x.Op == OpCopy {
+ x = x.Args[0]
+ if slow == x {
+ break
+ }
+ if advance {
+ slow = slow.Args[0]
+ }
+ advance = !advance
+ }
+ v.SetArg(i, x)
+ change = true
+ for a.Uses == 0 {
+ b := a.Args[0]
+ a.reset(OpInvalid)
+ a = b
+ }
+ }
+
// apply rewrite function
curv = v
if rv(v, config) {
@@ -52,7 +87,28 @@ func applyRewrite(f *Func, rb func(*Block) bool, rv func(*Value, *Config) bool)
}
}
if !change {
- return
+ break
+ }
+ }
+ // remove clobbered values
+ for _, b := range f.Blocks {
+ j := 0
+ for i, v := range b.Values {
+ if v.Op == OpInvalid {
+ f.freeValue(v)
+ continue
+ }
+ if i != j {
+ b.Values[j] = v
+ }
+ j++
+ }
+ if j != len(b.Values) {
+ tail := b.Values[j:]
+ for j := range tail {
+ tail[j] = nil
+ }
+ b.Values = b.Values[:j]
}
}
}
@@ -311,3 +367,13 @@ found:
}
return nil // too far away
}
+
+// clobber invalidates v. Returns true.
+// clobber is used by rewrite rules to:
+// A) make sure v is really dead and never used again.
+// B) decrement use counts of v's args.
+func clobber(v *Value) bool {
+ v.reset(OpInvalid)
+ // Note: leave v.Block intact. The Block field is used after clobber.
+ return true
+}
diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go
index 11c2de391c..c26aeb0bd0 100644
--- a/src/cmd/compile/internal/ssa/rewriteAMD64.go
+++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go
@@ -8,10 +8,6 @@ import "math"
var _ = math.MinInt8 // in case not otherwise used
func rewriteValueAMD64(v *Value, config *Config) bool {
switch v.Op {
- case OpAMD64ADDB:
- return rewriteValueAMD64_OpAMD64ADDB(v, config)
- case OpAMD64ADDBconst:
- return rewriteValueAMD64_OpAMD64ADDBconst(v, config)
case OpAMD64ADDL:
return rewriteValueAMD64_OpAMD64ADDL(v, config)
case OpAMD64ADDLconst:
@@ -20,14 +16,6 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
return rewriteValueAMD64_OpAMD64ADDQ(v, config)
case OpAMD64ADDQconst:
return rewriteValueAMD64_OpAMD64ADDQconst(v, config)
- case OpAMD64ADDW:
- return rewriteValueAMD64_OpAMD64ADDW(v, config)
- case OpAMD64ADDWconst:
- return rewriteValueAMD64_OpAMD64ADDWconst(v, config)
- case OpAMD64ANDB:
- return rewriteValueAMD64_OpAMD64ANDB(v, config)
- case OpAMD64ANDBconst:
- return rewriteValueAMD64_OpAMD64ANDBconst(v, config)
case OpAMD64ANDL:
return rewriteValueAMD64_OpAMD64ANDL(v, config)
case OpAMD64ANDLconst:
@@ -36,10 +24,6 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
return rewriteValueAMD64_OpAMD64ANDQ(v, config)
case OpAMD64ANDQconst:
return rewriteValueAMD64_OpAMD64ANDQconst(v, config)
- case OpAMD64ANDW:
- return rewriteValueAMD64_OpAMD64ANDW(v, config)
- case OpAMD64ANDWconst:
- return rewriteValueAMD64_OpAMD64ANDWconst(v, config)
case OpAdd16:
return rewriteValueAMD64_OpAdd16(v, config)
case OpAdd32:
@@ -64,6 +48,8 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
return rewriteValueAMD64_OpAnd64(v, config)
case OpAnd8:
return rewriteValueAMD64_OpAnd8(v, config)
+ case OpAndB:
+ return rewriteValueAMD64_OpAndB(v, config)
case OpAvg64u:
return rewriteValueAMD64_OpAvg64u(v, config)
case OpBswap32:
@@ -180,6 +166,8 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
return rewriteValueAMD64_OpEq64F(v, config)
case OpEq8:
return rewriteValueAMD64_OpEq8(v, config)
+ case OpEqB:
+ return rewriteValueAMD64_OpEqB(v, config)
case OpEqPtr:
return rewriteValueAMD64_OpEqPtr(v, config)
case OpGeq16:
@@ -458,10 +446,6 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
return rewriteValueAMD64_OpAMD64MOVWstoreidx1(v, config)
case OpAMD64MOVWstoreidx2:
return rewriteValueAMD64_OpAMD64MOVWstoreidx2(v, config)
- case OpAMD64MULB:
- return rewriteValueAMD64_OpAMD64MULB(v, config)
- case OpAMD64MULBconst:
- return rewriteValueAMD64_OpAMD64MULBconst(v, config)
case OpAMD64MULL:
return rewriteValueAMD64_OpAMD64MULL(v, config)
case OpAMD64MULLconst:
@@ -470,10 +454,6 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
return rewriteValueAMD64_OpAMD64MULQ(v, config)
case OpAMD64MULQconst:
return rewriteValueAMD64_OpAMD64MULQconst(v, config)
- case OpAMD64MULW:
- return rewriteValueAMD64_OpAMD64MULW(v, config)
- case OpAMD64MULWconst:
- return rewriteValueAMD64_OpAMD64MULWconst(v, config)
case OpMod16:
return rewriteValueAMD64_OpMod16(v, config)
case OpMod16u:
@@ -504,22 +484,14 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
return rewriteValueAMD64_OpMul64F(v, config)
case OpMul8:
return rewriteValueAMD64_OpMul8(v, config)
- case OpAMD64NEGB:
- return rewriteValueAMD64_OpAMD64NEGB(v, config)
case OpAMD64NEGL:
return rewriteValueAMD64_OpAMD64NEGL(v, config)
case OpAMD64NEGQ:
return rewriteValueAMD64_OpAMD64NEGQ(v, config)
- case OpAMD64NEGW:
- return rewriteValueAMD64_OpAMD64NEGW(v, config)
- case OpAMD64NOTB:
- return rewriteValueAMD64_OpAMD64NOTB(v, config)
case OpAMD64NOTL:
return rewriteValueAMD64_OpAMD64NOTL(v, config)
case OpAMD64NOTQ:
return rewriteValueAMD64_OpAMD64NOTQ(v, config)
- case OpAMD64NOTW:
- return rewriteValueAMD64_OpAMD64NOTW(v, config)
case OpNeg16:
return rewriteValueAMD64_OpNeg16(v, config)
case OpNeg32:
@@ -544,16 +516,14 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
return rewriteValueAMD64_OpNeq64F(v, config)
case OpNeq8:
return rewriteValueAMD64_OpNeq8(v, config)
+ case OpNeqB:
+ return rewriteValueAMD64_OpNeqB(v, config)
case OpNeqPtr:
return rewriteValueAMD64_OpNeqPtr(v, config)
case OpNilCheck:
return rewriteValueAMD64_OpNilCheck(v, config)
case OpNot:
return rewriteValueAMD64_OpNot(v, config)
- case OpAMD64ORB:
- return rewriteValueAMD64_OpAMD64ORB(v, config)
- case OpAMD64ORBconst:
- return rewriteValueAMD64_OpAMD64ORBconst(v, config)
case OpAMD64ORL:
return rewriteValueAMD64_OpAMD64ORL(v, config)
case OpAMD64ORLconst:
@@ -562,10 +532,6 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
return rewriteValueAMD64_OpAMD64ORQ(v, config)
case OpAMD64ORQconst:
return rewriteValueAMD64_OpAMD64ORQconst(v, config)
- case OpAMD64ORW:
- return rewriteValueAMD64_OpAMD64ORW(v, config)
- case OpAMD64ORWconst:
- return rewriteValueAMD64_OpAMD64ORWconst(v, config)
case OpOffPtr:
return rewriteValueAMD64_OpOffPtr(v, config)
case OpOr16:
@@ -576,6 +542,8 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
return rewriteValueAMD64_OpOr64(v, config)
case OpOr8:
return rewriteValueAMD64_OpOr8(v, config)
+ case OpOrB:
+ return rewriteValueAMD64_OpOrB(v, config)
case OpRsh16Ux16:
return rewriteValueAMD64_OpRsh16Ux16(v, config)
case OpRsh16Ux32:
@@ -680,14 +648,10 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
return rewriteValueAMD64_OpAMD64SETLE(v, config)
case OpAMD64SETNE:
return rewriteValueAMD64_OpAMD64SETNE(v, config)
- case OpAMD64SHLB:
- return rewriteValueAMD64_OpAMD64SHLB(v, config)
case OpAMD64SHLL:
return rewriteValueAMD64_OpAMD64SHLL(v, config)
case OpAMD64SHLQ:
return rewriteValueAMD64_OpAMD64SHLQ(v, config)
- case OpAMD64SHLW:
- return rewriteValueAMD64_OpAMD64SHLW(v, config)
case OpAMD64SHRB:
return rewriteValueAMD64_OpAMD64SHRB(v, config)
case OpAMD64SHRL:
@@ -696,10 +660,6 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
return rewriteValueAMD64_OpAMD64SHRQ(v, config)
case OpAMD64SHRW:
return rewriteValueAMD64_OpAMD64SHRW(v, config)
- case OpAMD64SUBB:
- return rewriteValueAMD64_OpAMD64SUBB(v, config)
- case OpAMD64SUBBconst:
- return rewriteValueAMD64_OpAMD64SUBBconst(v, config)
case OpAMD64SUBL:
return rewriteValueAMD64_OpAMD64SUBL(v, config)
case OpAMD64SUBLconst:
@@ -708,10 +668,6 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
return rewriteValueAMD64_OpAMD64SUBQ(v, config)
case OpAMD64SUBQconst:
return rewriteValueAMD64_OpAMD64SUBQconst(v, config)
- case OpAMD64SUBW:
- return rewriteValueAMD64_OpAMD64SUBW(v, config)
- case OpAMD64SUBWconst:
- return rewriteValueAMD64_OpAMD64SUBWconst(v, config)
case OpSignExt16to32:
return rewriteValueAMD64_OpSignExt16to32(v, config)
case OpSignExt16to64:
@@ -756,10 +712,6 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
return rewriteValueAMD64_OpTrunc64to32(v, config)
case OpTrunc64to8:
return rewriteValueAMD64_OpTrunc64to8(v, config)
- case OpAMD64XORB:
- return rewriteValueAMD64_OpAMD64XORB(v, config)
- case OpAMD64XORBconst:
- return rewriteValueAMD64_OpAMD64XORBconst(v, config)
case OpAMD64XORL:
return rewriteValueAMD64_OpAMD64XORL(v, config)
case OpAMD64XORLconst:
@@ -768,10 +720,6 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
return rewriteValueAMD64_OpAMD64XORQ(v, config)
case OpAMD64XORQconst:
return rewriteValueAMD64_OpAMD64XORQconst(v, config)
- case OpAMD64XORW:
- return rewriteValueAMD64_OpAMD64XORW(v, config)
- case OpAMD64XORWconst:
- return rewriteValueAMD64_OpAMD64XORWconst(v, config)
case OpXor16:
return rewriteValueAMD64_OpXor16(v, config)
case OpXor32:
@@ -797,105 +745,6 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
}
return false
}
-func rewriteValueAMD64_OpAMD64ADDB(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (ADDB x (MOVBconst [c]))
- // cond:
- // result: (ADDBconst [c] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVBconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64ADDBconst)
- v.AuxInt = c
- v.AddArg(x)
- return true
- }
- // match: (ADDB (MOVBconst [c]) x)
- // cond:
- // result: (ADDBconst [c] x)
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVBconst {
- break
- }
- c := v_0.AuxInt
- x := v.Args[1]
- v.reset(OpAMD64ADDBconst)
- v.AuxInt = c
- v.AddArg(x)
- return true
- }
- // match: (ADDB x (NEGB y))
- // cond:
- // result: (SUBB x y)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64NEGB {
- break
- }
- y := v_1.Args[0]
- v.reset(OpAMD64SUBB)
- v.AddArg(x)
- v.AddArg(y)
- return true
- }
- return false
-}
-func rewriteValueAMD64_OpAMD64ADDBconst(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (ADDBconst [c] x)
- // cond: int8(c)==0
- // result: x
- for {
- c := v.AuxInt
- x := v.Args[0]
- if !(int8(c) == 0) {
- break
- }
- v.reset(OpCopy)
- v.Type = x.Type
- v.AddArg(x)
- return true
- }
- // match: (ADDBconst [c] (MOVBconst [d]))
- // cond:
- // result: (MOVBconst [int64(int8(c+d))])
- for {
- c := v.AuxInt
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVBconst {
- break
- }
- d := v_0.AuxInt
- v.reset(OpAMD64MOVBconst)
- v.AuxInt = int64(int8(c + d))
- return true
- }
- // match: (ADDBconst [c] (ADDBconst [d] x))
- // cond:
- // result: (ADDBconst [int64(int8(c+d))] x)
- for {
- c := v.AuxInt
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64ADDBconst {
- break
- }
- d := v_0.AuxInt
- x := v_0.Args[0]
- v.reset(OpAMD64ADDBconst)
- v.AuxInt = int64(int8(c + d))
- v.AddArg(x)
- return true
- }
- return false
-}
func rewriteValueAMD64_OpAMD64ADDL(v *Value, config *Config) bool {
b := v.Block
_ = b
@@ -1418,244 +1267,6 @@ func rewriteValueAMD64_OpAMD64ADDQconst(v *Value, config *Config) bool {
}
return false
}
-func rewriteValueAMD64_OpAMD64ADDW(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (ADDW x (MOVWconst [c]))
- // cond:
- // result: (ADDWconst [c] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVWconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64ADDWconst)
- v.AuxInt = c
- v.AddArg(x)
- return true
- }
- // match: (ADDW (MOVWconst [c]) x)
- // cond:
- // result: (ADDWconst [c] x)
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVWconst {
- break
- }
- c := v_0.AuxInt
- x := v.Args[1]
- v.reset(OpAMD64ADDWconst)
- v.AuxInt = c
- v.AddArg(x)
- return true
- }
- // match: (ADDW x (NEGW y))
- // cond:
- // result: (SUBW x y)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64NEGW {
- break
- }
- y := v_1.Args[0]
- v.reset(OpAMD64SUBW)
- v.AddArg(x)
- v.AddArg(y)
- return true
- }
- return false
-}
-func rewriteValueAMD64_OpAMD64ADDWconst(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (ADDWconst [c] x)
- // cond: int16(c)==0
- // result: x
- for {
- c := v.AuxInt
- x := v.Args[0]
- if !(int16(c) == 0) {
- break
- }
- v.reset(OpCopy)
- v.Type = x.Type
- v.AddArg(x)
- return true
- }
- // match: (ADDWconst [c] (MOVWconst [d]))
- // cond:
- // result: (MOVWconst [int64(int16(c+d))])
- for {
- c := v.AuxInt
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVWconst {
- break
- }
- d := v_0.AuxInt
- v.reset(OpAMD64MOVWconst)
- v.AuxInt = int64(int16(c + d))
- return true
- }
- // match: (ADDWconst [c] (ADDWconst [d] x))
- // cond:
- // result: (ADDWconst [int64(int16(c+d))] x)
- for {
- c := v.AuxInt
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64ADDWconst {
- break
- }
- d := v_0.AuxInt
- x := v_0.Args[0]
- v.reset(OpAMD64ADDWconst)
- v.AuxInt = int64(int16(c + d))
- v.AddArg(x)
- return true
- }
- return false
-}
-func rewriteValueAMD64_OpAMD64ANDB(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (ANDB x (MOVLconst [c]))
- // cond:
- // result: (ANDBconst [c] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVLconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64ANDBconst)
- v.AuxInt = c
- v.AddArg(x)
- return true
- }
- // match: (ANDB (MOVLconst [c]) x)
- // cond:
- // result: (ANDBconst [c] x)
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVLconst {
- break
- }
- c := v_0.AuxInt
- x := v.Args[1]
- v.reset(OpAMD64ANDBconst)
- v.AuxInt = c
- v.AddArg(x)
- return true
- }
- // match: (ANDB x (MOVBconst [c]))
- // cond:
- // result: (ANDBconst [c] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVBconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64ANDBconst)
- v.AuxInt = c
- v.AddArg(x)
- return true
- }
- // match: (ANDB (MOVBconst [c]) x)
- // cond:
- // result: (ANDBconst [c] x)
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVBconst {
- break
- }
- c := v_0.AuxInt
- x := v.Args[1]
- v.reset(OpAMD64ANDBconst)
- v.AuxInt = c
- v.AddArg(x)
- return true
- }
- // match: (ANDB x x)
- // cond:
- // result: x
- for {
- x := v.Args[0]
- if x != v.Args[1] {
- break
- }
- v.reset(OpCopy)
- v.Type = x.Type
- v.AddArg(x)
- return true
- }
- return false
-}
-func rewriteValueAMD64_OpAMD64ANDBconst(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (ANDBconst [c] (ANDBconst [d] x))
- // cond:
- // result: (ANDBconst [c & d] x)
- for {
- c := v.AuxInt
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64ANDBconst {
- break
- }
- d := v_0.AuxInt
- x := v_0.Args[0]
- v.reset(OpAMD64ANDBconst)
- v.AuxInt = c & d
- v.AddArg(x)
- return true
- }
- // match: (ANDBconst [c] _)
- // cond: int8(c)==0
- // result: (MOVBconst [0])
- for {
- c := v.AuxInt
- if !(int8(c) == 0) {
- break
- }
- v.reset(OpAMD64MOVBconst)
- v.AuxInt = 0
- return true
- }
- // match: (ANDBconst [c] x)
- // cond: int8(c)==-1
- // result: x
- for {
- c := v.AuxInt
- x := v.Args[0]
- if !(int8(c) == -1) {
- break
- }
- v.reset(OpCopy)
- v.Type = x.Type
- v.AddArg(x)
- return true
- }
- // match: (ANDBconst [c] (MOVBconst [d]))
- // cond:
- // result: (MOVBconst [c&d])
- for {
- c := v.AuxInt
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVBconst {
- break
- }
- d := v_0.AuxInt
- v.reset(OpAMD64MOVBconst)
- v.AuxInt = c & d
- return true
- }
- return false
-}
func rewriteValueAMD64_OpAMD64ANDL(v *Value, config *Config) bool {
b := v.Block
_ = b
@@ -1838,180 +1449,77 @@ func rewriteValueAMD64_OpAMD64ANDQconst(v *Value, config *Config) bool {
v.AddArg(x)
return true
}
- // match: (ANDQconst [0] _)
- // cond:
- // result: (MOVQconst [0])
- for {
- if v.AuxInt != 0 {
- break
- }
- v.reset(OpAMD64MOVQconst)
- v.AuxInt = 0
- return true
- }
- // match: (ANDQconst [-1] x)
+ // match: (ANDQconst [0xFF] x)
// cond:
- // result: x
+ // result: (MOVBQZX x)
for {
- if v.AuxInt != -1 {
+ if v.AuxInt != 0xFF {
break
}
x := v.Args[0]
- v.reset(OpCopy)
- v.Type = x.Type
+ v.reset(OpAMD64MOVBQZX)
v.AddArg(x)
return true
}
- // match: (ANDQconst [c] (MOVQconst [d]))
+ // match: (ANDQconst [0xFFFF] x)
// cond:
- // result: (MOVQconst [c&d])
+ // result: (MOVWQZX x)
for {
- c := v.AuxInt
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVQconst {
+ if v.AuxInt != 0xFFFF {
break
}
- d := v_0.AuxInt
- v.reset(OpAMD64MOVQconst)
- v.AuxInt = c & d
- return true
- }
- return false
-}
-func rewriteValueAMD64_OpAMD64ANDW(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (ANDW x (MOVLconst [c]))
- // cond:
- // result: (ANDWconst [c] x)
- for {
x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVLconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64ANDWconst)
- v.AuxInt = c
+ v.reset(OpAMD64MOVWQZX)
v.AddArg(x)
return true
}
- // match: (ANDW (MOVLconst [c]) x)
+ // match: (ANDQconst [0xFFFFFFFF] x)
// cond:
- // result: (ANDWconst [c] x)
+ // result: (MOVLQZX x)
for {
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVLconst {
+ if v.AuxInt != 0xFFFFFFFF {
break
}
- c := v_0.AuxInt
- x := v.Args[1]
- v.reset(OpAMD64ANDWconst)
- v.AuxInt = c
- v.AddArg(x)
- return true
- }
- // match: (ANDW x (MOVWconst [c]))
- // cond:
- // result: (ANDWconst [c] x)
- for {
x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVWconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64ANDWconst)
- v.AuxInt = c
+ v.reset(OpAMD64MOVLQZX)
v.AddArg(x)
return true
}
- // match: (ANDW (MOVWconst [c]) x)
+ // match: (ANDQconst [0] _)
// cond:
- // result: (ANDWconst [c] x)
+ // result: (MOVQconst [0])
for {
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVWconst {
+ if v.AuxInt != 0 {
break
}
- c := v_0.AuxInt
- x := v.Args[1]
- v.reset(OpAMD64ANDWconst)
- v.AuxInt = c
- v.AddArg(x)
+ v.reset(OpAMD64MOVQconst)
+ v.AuxInt = 0
return true
}
- // match: (ANDW x x)
+ // match: (ANDQconst [-1] x)
// cond:
// result: x
for {
- x := v.Args[0]
- if x != v.Args[1] {
- break
- }
- v.reset(OpCopy)
- v.Type = x.Type
- v.AddArg(x)
- return true
- }
- return false
-}
-func rewriteValueAMD64_OpAMD64ANDWconst(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (ANDWconst [c] (ANDWconst [d] x))
- // cond:
- // result: (ANDWconst [c & d] x)
- for {
- c := v.AuxInt
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64ANDWconst {
- break
- }
- d := v_0.AuxInt
- x := v_0.Args[0]
- v.reset(OpAMD64ANDWconst)
- v.AuxInt = c & d
- v.AddArg(x)
- return true
- }
- // match: (ANDWconst [c] _)
- // cond: int16(c)==0
- // result: (MOVWconst [0])
- for {
- c := v.AuxInt
- if !(int16(c) == 0) {
+ if v.AuxInt != -1 {
break
}
- v.reset(OpAMD64MOVWconst)
- v.AuxInt = 0
- return true
- }
- // match: (ANDWconst [c] x)
- // cond: int16(c)==-1
- // result: x
- for {
- c := v.AuxInt
x := v.Args[0]
- if !(int16(c) == -1) {
- break
- }
v.reset(OpCopy)
v.Type = x.Type
v.AddArg(x)
return true
}
- // match: (ANDWconst [c] (MOVWconst [d]))
+ // match: (ANDQconst [c] (MOVQconst [d]))
// cond:
- // result: (MOVWconst [c&d])
+ // result: (MOVQconst [c&d])
for {
c := v.AuxInt
v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVWconst {
+ if v_0.Op != OpAMD64MOVQconst {
break
}
d := v_0.AuxInt
- v.reset(OpAMD64MOVWconst)
+ v.reset(OpAMD64MOVQconst)
v.AuxInt = c & d
return true
}
@@ -2022,11 +1530,11 @@ func rewriteValueAMD64_OpAdd16(v *Value, config *Config) bool {
_ = b
// match: (Add16 x y)
// cond:
- // result: (ADDW x y)
+ // result: (ADDL x y)
for {
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64ADDW)
+ v.reset(OpAMD64ADDL)
v.AddArg(x)
v.AddArg(y)
return true
@@ -2102,11 +1610,11 @@ func rewriteValueAMD64_OpAdd8(v *Value, config *Config) bool {
_ = b
// match: (Add8 x y)
// cond:
- // result: (ADDB x y)
+ // result: (ADDL x y)
for {
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64ADDB)
+ v.reset(OpAMD64ADDL)
v.AddArg(x)
v.AddArg(y)
return true
@@ -2150,11 +1658,11 @@ func rewriteValueAMD64_OpAnd16(v *Value, config *Config) bool {
_ = b
// match: (And16 x y)
// cond:
- // result: (ANDW x y)
+ // result: (ANDL x y)
for {
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64ANDW)
+ v.reset(OpAMD64ANDL)
v.AddArg(x)
v.AddArg(y)
return true
@@ -2198,11 +1706,27 @@ func rewriteValueAMD64_OpAnd8(v *Value, config *Config) bool {
_ = b
// match: (And8 x y)
// cond:
- // result: (ANDB x y)
+ // result: (ANDL x y)
for {
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64ANDB)
+ v.reset(OpAMD64ANDL)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ return false
+}
+func rewriteValueAMD64_OpAndB(v *Value, config *Config) bool {
+ b := v.Block
+ _ = b
+ // match: (AndB x y)
+ // cond:
+ // result: (ANDL x y)
+ for {
+ x := v.Args[0]
+ y := v.Args[1]
+ v.reset(OpAMD64ANDL)
v.AddArg(x)
v.AddArg(y)
return true
@@ -2529,27 +2053,27 @@ func rewriteValueAMD64_OpAMD64CMOVWEQconst(v *Value, config *Config) bool {
func rewriteValueAMD64_OpAMD64CMPB(v *Value, config *Config) bool {
b := v.Block
_ = b
- // match: (CMPB x (MOVBconst [c]))
+ // match: (CMPB x (MOVLconst [c]))
// cond:
- // result: (CMPBconst x [c])
+ // result: (CMPBconst x [int64(int8(c))])
for {
x := v.Args[0]
v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVBconst {
+ if v_1.Op != OpAMD64MOVLconst {
break
}
c := v_1.AuxInt
v.reset(OpAMD64CMPBconst)
v.AddArg(x)
- v.AuxInt = c
+ v.AuxInt = int64(int8(c))
return true
}
- // match: (CMPB (MOVBconst [c]) x)
+ // match: (CMPB (MOVLconst [c]) x)
// cond:
- // result: (InvertFlags (CMPBconst x [c]))
+ // result: (InvertFlags (CMPBconst x [int64(int8(c))]))
for {
v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVBconst {
+ if v_0.Op != OpAMD64MOVLconst {
break
}
c := v_0.AuxInt
@@ -2557,7 +2081,7 @@ func rewriteValueAMD64_OpAMD64CMPB(v *Value, config *Config) bool {
v.reset(OpAMD64InvertFlags)
v0 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
v0.AddArg(x)
- v0.AuxInt = c
+ v0.AuxInt = int64(int8(c))
v.AddArg(v0)
return true
}
@@ -2566,12 +2090,12 @@ func rewriteValueAMD64_OpAMD64CMPB(v *Value, config *Config) bool {
func rewriteValueAMD64_OpAMD64CMPBconst(v *Value, config *Config) bool {
b := v.Block
_ = b
- // match: (CMPBconst (MOVBconst [x]) [y])
+ // match: (CMPBconst (MOVLconst [x]) [y])
// cond: int8(x)==int8(y)
// result: (FlagEQ)
for {
v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVBconst {
+ if v_0.Op != OpAMD64MOVLconst {
break
}
x := v_0.AuxInt
@@ -2582,12 +2106,12 @@ func rewriteValueAMD64_OpAMD64CMPBconst(v *Value, config *Config) bool {
v.reset(OpAMD64FlagEQ)
return true
}
- // match: (CMPBconst (MOVBconst [x]) [y])
+ // match: (CMPBconst (MOVLconst [x]) [y])
// cond: int8(x)<int8(y) && uint8(x)<uint8(y)
// result: (FlagLT_ULT)
for {
v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVBconst {
+ if v_0.Op != OpAMD64MOVLconst {
break
}
x := v_0.AuxInt
@@ -2598,12 +2122,12 @@ func rewriteValueAMD64_OpAMD64CMPBconst(v *Value, config *Config) bool {
v.reset(OpAMD64FlagLT_ULT)
return true
}
- // match: (CMPBconst (MOVBconst [x]) [y])
+ // match: (CMPBconst (MOVLconst [x]) [y])
// cond: int8(x)<int8(y) && uint8(x)>uint8(y)
// result: (FlagLT_UGT)
for {
v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVBconst {
+ if v_0.Op != OpAMD64MOVLconst {
break
}
x := v_0.AuxInt
@@ -2614,12 +2138,12 @@ func rewriteValueAMD64_OpAMD64CMPBconst(v *Value, config *Config) bool {
v.reset(OpAMD64FlagLT_UGT)
return true
}
- // match: (CMPBconst (MOVBconst [x]) [y])
+ // match: (CMPBconst (MOVLconst [x]) [y])
// cond: int8(x)>int8(y) && uint8(x)<uint8(y)
// result: (FlagGT_ULT)
for {
v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVBconst {
+ if v_0.Op != OpAMD64MOVLconst {
break
}
x := v_0.AuxInt
@@ -2630,12 +2154,12 @@ func rewriteValueAMD64_OpAMD64CMPBconst(v *Value, config *Config) bool {
v.reset(OpAMD64FlagGT_ULT)
return true
}
- // match: (CMPBconst (MOVBconst [x]) [y])
+ // match: (CMPBconst (MOVLconst [x]) [y])
// cond: int8(x)>int8(y) && uint8(x)>uint8(y)
// result: (FlagGT_UGT)
for {
v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVBconst {
+ if v_0.Op != OpAMD64MOVLconst {
break
}
x := v_0.AuxInt
@@ -2646,12 +2170,12 @@ func rewriteValueAMD64_OpAMD64CMPBconst(v *Value, config *Config) bool {
v.reset(OpAMD64FlagGT_UGT)
return true
}
- // match: (CMPBconst (ANDBconst _ [m]) [n])
+ // match: (CMPBconst (ANDLconst _ [m]) [n])
// cond: 0 <= int8(m) && int8(m) < int8(n)
// result: (FlagLT_ULT)
for {
v_0 := v.Args[0]
- if v_0.Op != OpAMD64ANDBconst {
+ if v_0.Op != OpAMD64ANDLconst {
break
}
m := v_0.AuxInt
@@ -2662,12 +2186,12 @@ func rewriteValueAMD64_OpAMD64CMPBconst(v *Value, config *Config) bool {
v.reset(OpAMD64FlagLT_ULT)
return true
}
- // match: (CMPBconst (ANDB x y) [0])
+ // match: (CMPBconst (ANDL x y) [0])
// cond:
// result: (TESTB x y)
for {
v_0 := v.Args[0]
- if v_0.Op != OpAMD64ANDB {
+ if v_0.Op != OpAMD64ANDL {
break
}
x := v_0.Args[0]
@@ -2680,12 +2204,12 @@ func rewriteValueAMD64_OpAMD64CMPBconst(v *Value, config *Config) bool {
v.AddArg(y)
return true
}
- // match: (CMPBconst (ANDBconst [c] x) [0])
+ // match: (CMPBconst (ANDLconst [c] x) [0])
// cond:
- // result: (TESTBconst [c] x)
+ // result: (TESTBconst [int64(int8(c))] x)
for {
v_0 := v.Args[0]
- if v_0.Op != OpAMD64ANDBconst {
+ if v_0.Op != OpAMD64ANDLconst {
break
}
c := v_0.AuxInt
@@ -2694,7 +2218,7 @@ func rewriteValueAMD64_OpAMD64CMPBconst(v *Value, config *Config) bool {
break
}
v.reset(OpAMD64TESTBconst)
- v.AuxInt = c
+ v.AuxInt = int64(int8(c))
v.AddArg(x)
return true
}
@@ -2833,6 +2357,22 @@ func rewriteValueAMD64_OpAMD64CMPLconst(v *Value, config *Config) bool {
v.reset(OpAMD64FlagGT_UGT)
return true
}
+ // match: (CMPLconst (SHRLconst _ [c]) [n])
+ // cond: 0 <= n && 0 < c && c <= 32 && (1<<uint64(32-c)) <= uint64(n)
+ // result: (FlagLT_ULT)
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpAMD64SHRLconst {
+ break
+ }
+ c := v_0.AuxInt
+ n := v.AuxInt
+ if !(0 <= n && 0 < c && c <= 32 && (1<<uint64(32-c)) <= uint64(n)) {
+ break
+ }
+ v.reset(OpAMD64FlagLT_ULT)
+ return true
+ }
// match: (CMPLconst (ANDLconst _ [m]) [n])
// cond: 0 <= int32(m) && int32(m) < int32(n)
// result: (FlagLT_ULT)
@@ -3026,6 +2566,67 @@ func rewriteValueAMD64_OpAMD64CMPQconst(v *Value, config *Config) bool {
v.reset(OpAMD64FlagGT_UGT)
return true
}
+ // match: (CMPQconst (MOVBQZX _) [c])
+ // cond: 0xFF < c
+ // result: (FlagLT_ULT)
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpAMD64MOVBQZX {
+ break
+ }
+ c := v.AuxInt
+ if !(0xFF < c) {
+ break
+ }
+ v.reset(OpAMD64FlagLT_ULT)
+ return true
+ }
+ // match: (CMPQconst (MOVWQZX _) [c])
+ // cond: 0xFFFF < c
+ // result: (FlagLT_ULT)
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpAMD64MOVWQZX {
+ break
+ }
+ c := v.AuxInt
+ if !(0xFFFF < c) {
+ break
+ }
+ v.reset(OpAMD64FlagLT_ULT)
+ return true
+ }
+ // match: (CMPQconst (MOVLQZX _) [c])
+ // cond: 0xFFFFFFFF < c
+ // result: (FlagLT_ULT)
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpAMD64MOVLQZX {
+ break
+ }
+ c := v.AuxInt
+ if !(0xFFFFFFFF < c) {
+ break
+ }
+ v.reset(OpAMD64FlagLT_ULT)
+ return true
+ }
+ // match: (CMPQconst (SHRQconst _ [c]) [n])
+ // cond: 0 <= n && 0 < c && c <= 64 && (1<<uint64(64-c)) <= uint64(n)
+ // result: (FlagLT_ULT)
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpAMD64SHRQconst {
+ break
+ }
+ c := v_0.AuxInt
+ n := v.AuxInt
+ if !(0 <= n && 0 < c && c <= 64 && (1<<uint64(64-c)) <= uint64(n)) {
+ break
+ }
+ v.reset(OpAMD64FlagLT_ULT)
+ return true
+ }
// match: (CMPQconst (ANDQconst _ [m]) [n])
// cond: 0 <= m && m < n
// result: (FlagLT_ULT)
@@ -3096,27 +2697,27 @@ func rewriteValueAMD64_OpAMD64CMPQconst(v *Value, config *Config) bool {
func rewriteValueAMD64_OpAMD64CMPW(v *Value, config *Config) bool {
b := v.Block
_ = b
- // match: (CMPW x (MOVWconst [c]))
+ // match: (CMPW x (MOVLconst [c]))
// cond:
- // result: (CMPWconst x [c])
+ // result: (CMPWconst x [int64(int16(c))])
for {
x := v.Args[0]
v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVWconst {
+ if v_1.Op != OpAMD64MOVLconst {
break
}
c := v_1.AuxInt
v.reset(OpAMD64CMPWconst)
v.AddArg(x)
- v.AuxInt = c
+ v.AuxInt = int64(int16(c))
return true
}
- // match: (CMPW (MOVWconst [c]) x)
+ // match: (CMPW (MOVLconst [c]) x)
// cond:
- // result: (InvertFlags (CMPWconst x [c]))
+ // result: (InvertFlags (CMPWconst x [int64(int16(c))]))
for {
v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVWconst {
+ if v_0.Op != OpAMD64MOVLconst {
break
}
c := v_0.AuxInt
@@ -3124,7 +2725,7 @@ func rewriteValueAMD64_OpAMD64CMPW(v *Value, config *Config) bool {
v.reset(OpAMD64InvertFlags)
v0 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
v0.AddArg(x)
- v0.AuxInt = c
+ v0.AuxInt = int64(int16(c))
v.AddArg(v0)
return true
}
@@ -3133,12 +2734,12 @@ func rewriteValueAMD64_OpAMD64CMPW(v *Value, config *Config) bool {
func rewriteValueAMD64_OpAMD64CMPWconst(v *Value, config *Config) bool {
b := v.Block
_ = b
- // match: (CMPWconst (MOVWconst [x]) [y])
+ // match: (CMPWconst (MOVLconst [x]) [y])
// cond: int16(x)==int16(y)
// result: (FlagEQ)
for {
v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVWconst {
+ if v_0.Op != OpAMD64MOVLconst {
break
}
x := v_0.AuxInt
@@ -3149,12 +2750,12 @@ func rewriteValueAMD64_OpAMD64CMPWconst(v *Value, config *Config) bool {
v.reset(OpAMD64FlagEQ)
return true
}
- // match: (CMPWconst (MOVWconst [x]) [y])
+ // match: (CMPWconst (MOVLconst [x]) [y])
// cond: int16(x)<int16(y) && uint16(x)<uint16(y)
// result: (FlagLT_ULT)
for {
v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVWconst {
+ if v_0.Op != OpAMD64MOVLconst {
break
}
x := v_0.AuxInt
@@ -3165,12 +2766,12 @@ func rewriteValueAMD64_OpAMD64CMPWconst(v *Value, config *Config) bool {
v.reset(OpAMD64FlagLT_ULT)
return true
}
- // match: (CMPWconst (MOVWconst [x]) [y])
+ // match: (CMPWconst (MOVLconst [x]) [y])
// cond: int16(x)<int16(y) && uint16(x)>uint16(y)
// result: (FlagLT_UGT)
for {
v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVWconst {
+ if v_0.Op != OpAMD64MOVLconst {
break
}
x := v_0.AuxInt
@@ -3181,12 +2782,12 @@ func rewriteValueAMD64_OpAMD64CMPWconst(v *Value, config *Config) bool {
v.reset(OpAMD64FlagLT_UGT)
return true
}
- // match: (CMPWconst (MOVWconst [x]) [y])
+ // match: (CMPWconst (MOVLconst [x]) [y])
// cond: int16(x)>int16(y) && uint16(x)<uint16(y)
// result: (FlagGT_ULT)
for {
v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVWconst {
+ if v_0.Op != OpAMD64MOVLconst {
break
}
x := v_0.AuxInt
@@ -3197,12 +2798,12 @@ func rewriteValueAMD64_OpAMD64CMPWconst(v *Value, config *Config) bool {
v.reset(OpAMD64FlagGT_ULT)
return true
}
- // match: (CMPWconst (MOVWconst [x]) [y])
+ // match: (CMPWconst (MOVLconst [x]) [y])
// cond: int16(x)>int16(y) && uint16(x)>uint16(y)
// result: (FlagGT_UGT)
for {
v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVWconst {
+ if v_0.Op != OpAMD64MOVLconst {
break
}
x := v_0.AuxInt
@@ -3213,12 +2814,12 @@ func rewriteValueAMD64_OpAMD64CMPWconst(v *Value, config *Config) bool {
v.reset(OpAMD64FlagGT_UGT)
return true
}
- // match: (CMPWconst (ANDWconst _ [m]) [n])
+ // match: (CMPWconst (ANDLconst _ [m]) [n])
// cond: 0 <= int16(m) && int16(m) < int16(n)
// result: (FlagLT_ULT)
for {
v_0 := v.Args[0]
- if v_0.Op != OpAMD64ANDWconst {
+ if v_0.Op != OpAMD64ANDLconst {
break
}
m := v_0.AuxInt
@@ -3229,12 +2830,12 @@ func rewriteValueAMD64_OpAMD64CMPWconst(v *Value, config *Config) bool {
v.reset(OpAMD64FlagLT_ULT)
return true
}
- // match: (CMPWconst (ANDW x y) [0])
+ // match: (CMPWconst (ANDL x y) [0])
// cond:
// result: (TESTW x y)
for {
v_0 := v.Args[0]
- if v_0.Op != OpAMD64ANDW {
+ if v_0.Op != OpAMD64ANDL {
break
}
x := v_0.Args[0]
@@ -3247,12 +2848,12 @@ func rewriteValueAMD64_OpAMD64CMPWconst(v *Value, config *Config) bool {
v.AddArg(y)
return true
}
- // match: (CMPWconst (ANDWconst [c] x) [0])
+ // match: (CMPWconst (ANDLconst [c] x) [0])
// cond:
- // result: (TESTWconst [c] x)
+ // result: (TESTWconst [int64(int16(c))] x)
for {
v_0 := v.Args[0]
- if v_0.Op != OpAMD64ANDWconst {
+ if v_0.Op != OpAMD64ANDLconst {
break
}
c := v_0.AuxInt
@@ -3261,7 +2862,7 @@ func rewriteValueAMD64_OpAMD64CMPWconst(v *Value, config *Config) bool {
break
}
v.reset(OpAMD64TESTWconst)
- v.AuxInt = c
+ v.AuxInt = int64(int16(c))
v.AddArg(x)
return true
}
@@ -3305,10 +2906,10 @@ func rewriteValueAMD64_OpCom16(v *Value, config *Config) bool {
_ = b
// match: (Com16 x)
// cond:
- // result: (NOTW x)
+ // result: (NOTL x)
for {
x := v.Args[0]
- v.reset(OpAMD64NOTW)
+ v.reset(OpAMD64NOTL)
v.AddArg(x)
return true
}
@@ -3347,10 +2948,10 @@ func rewriteValueAMD64_OpCom8(v *Value, config *Config) bool {
_ = b
// match: (Com8 x)
// cond:
- // result: (NOTB x)
+ // result: (NOTL x)
for {
x := v.Args[0]
- v.reset(OpAMD64NOTB)
+ v.reset(OpAMD64NOTL)
v.AddArg(x)
return true
}
@@ -3361,10 +2962,10 @@ func rewriteValueAMD64_OpConst16(v *Value, config *Config) bool {
_ = b
// match: (Const16 [val])
// cond:
- // result: (MOVWconst [val])
+ // result: (MOVLconst [val])
for {
val := v.AuxInt
- v.reset(OpAMD64MOVWconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = val
return true
}
@@ -3431,10 +3032,10 @@ func rewriteValueAMD64_OpConst8(v *Value, config *Config) bool {
_ = b
// match: (Const8 [val])
// cond:
- // result: (MOVBconst [val])
+ // result: (MOVLconst [val])
for {
val := v.AuxInt
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = val
return true
}
@@ -3445,10 +3046,10 @@ func rewriteValueAMD64_OpConstBool(v *Value, config *Config) bool {
_ = b
// match: (ConstBool [b])
// cond:
- // result: (MOVBconst [b])
+ // result: (MOVLconst [b])
for {
b := v.AuxInt
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = b
return true
}
@@ -3983,6 +3584,24 @@ func rewriteValueAMD64_OpEq8(v *Value, config *Config) bool {
}
return false
}
+func rewriteValueAMD64_OpEqB(v *Value, config *Config) bool {
+ b := v.Block
+ _ = b
+ // match: (EqB x y)
+ // cond:
+ // result: (SETEQ (CMPB x y))
+ for {
+ x := v.Args[0]
+ y := v.Args[1]
+ v.reset(OpAMD64SETEQ)
+ v0 := b.NewValue0(v.Line, OpAMD64CMPB, TypeFlags)
+ v0.AddArg(x)
+ v0.AddArg(y)
+ v.AddArg(v0)
+ return true
+ }
+ return false
+}
func rewriteValueAMD64_OpEqPtr(v *Value, config *Config) bool {
b := v.Block
_ = b
@@ -5842,20 +5461,20 @@ func rewriteValueAMD64_OpLsh16x16(v *Value, config *Config) bool {
_ = b
// match: (Lsh16x16 <t> x y)
// cond:
- // result: (ANDW (SHLW <t> x y) (SBBLcarrymask <t> (CMPWconst y [16])))
+ // result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPWconst y [32])))
for {
t := v.Type
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64ANDW)
- v0 := b.NewValue0(v.Line, OpAMD64SHLW, t)
+ v.reset(OpAMD64ANDL)
+ v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
v0.AddArg(x)
v0.AddArg(y)
v.AddArg(v0)
v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
v2 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
v2.AddArg(y)
- v2.AuxInt = 16
+ v2.AuxInt = 32
v1.AddArg(v2)
v.AddArg(v1)
return true
@@ -5867,20 +5486,20 @@ func rewriteValueAMD64_OpLsh16x32(v *Value, config *Config) bool {
_ = b
// match: (Lsh16x32 <t> x y)
// cond:
- // result: (ANDW (SHLW <t> x y) (SBBLcarrymask <t> (CMPLconst y [16])))
+ // result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPLconst y [32])))
for {
t := v.Type
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64ANDW)
- v0 := b.NewValue0(v.Line, OpAMD64SHLW, t)
+ v.reset(OpAMD64ANDL)
+ v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
v0.AddArg(x)
v0.AddArg(y)
v.AddArg(v0)
v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
v2 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
v2.AddArg(y)
- v2.AuxInt = 16
+ v2.AuxInt = 32
v1.AddArg(v2)
v.AddArg(v1)
return true
@@ -5892,20 +5511,20 @@ func rewriteValueAMD64_OpLsh16x64(v *Value, config *Config) bool {
_ = b
// match: (Lsh16x64 <t> x y)
// cond:
- // result: (ANDW (SHLW <t> x y) (SBBLcarrymask <t> (CMPQconst y [16])))
+ // result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPQconst y [32])))
for {
t := v.Type
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64ANDW)
- v0 := b.NewValue0(v.Line, OpAMD64SHLW, t)
+ v.reset(OpAMD64ANDL)
+ v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
v0.AddArg(x)
v0.AddArg(y)
v.AddArg(v0)
v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
v2 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
v2.AddArg(y)
- v2.AuxInt = 16
+ v2.AuxInt = 32
v1.AddArg(v2)
v.AddArg(v1)
return true
@@ -5917,20 +5536,20 @@ func rewriteValueAMD64_OpLsh16x8(v *Value, config *Config) bool {
_ = b
// match: (Lsh16x8 <t> x y)
// cond:
- // result: (ANDW (SHLW <t> x y) (SBBLcarrymask <t> (CMPBconst y [16])))
+ // result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPBconst y [32])))
for {
t := v.Type
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64ANDW)
- v0 := b.NewValue0(v.Line, OpAMD64SHLW, t)
+ v.reset(OpAMD64ANDL)
+ v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
v0.AddArg(x)
v0.AddArg(y)
v.AddArg(v0)
v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
v2 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
v2.AddArg(y)
- v2.AuxInt = 16
+ v2.AuxInt = 32
v1.AddArg(v2)
v.AddArg(v1)
return true
@@ -6142,20 +5761,20 @@ func rewriteValueAMD64_OpLsh8x16(v *Value, config *Config) bool {
_ = b
// match: (Lsh8x16 <t> x y)
// cond:
- // result: (ANDB (SHLB <t> x y) (SBBLcarrymask <t> (CMPWconst y [8])))
+ // result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPWconst y [32])))
for {
t := v.Type
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64ANDB)
- v0 := b.NewValue0(v.Line, OpAMD64SHLB, t)
+ v.reset(OpAMD64ANDL)
+ v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
v0.AddArg(x)
v0.AddArg(y)
v.AddArg(v0)
v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
v2 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
v2.AddArg(y)
- v2.AuxInt = 8
+ v2.AuxInt = 32
v1.AddArg(v2)
v.AddArg(v1)
return true
@@ -6167,20 +5786,20 @@ func rewriteValueAMD64_OpLsh8x32(v *Value, config *Config) bool {
_ = b
// match: (Lsh8x32 <t> x y)
// cond:
- // result: (ANDB (SHLB <t> x y) (SBBLcarrymask <t> (CMPLconst y [8])))
+ // result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPLconst y [32])))
for {
t := v.Type
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64ANDB)
- v0 := b.NewValue0(v.Line, OpAMD64SHLB, t)
+ v.reset(OpAMD64ANDL)
+ v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
v0.AddArg(x)
v0.AddArg(y)
v.AddArg(v0)
v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
v2 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
v2.AddArg(y)
- v2.AuxInt = 8
+ v2.AuxInt = 32
v1.AddArg(v2)
v.AddArg(v1)
return true
@@ -6192,20 +5811,20 @@ func rewriteValueAMD64_OpLsh8x64(v *Value, config *Config) bool {
_ = b
// match: (Lsh8x64 <t> x y)
// cond:
- // result: (ANDB (SHLB <t> x y) (SBBLcarrymask <t> (CMPQconst y [8])))
+ // result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPQconst y [32])))
for {
t := v.Type
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64ANDB)
- v0 := b.NewValue0(v.Line, OpAMD64SHLB, t)
+ v.reset(OpAMD64ANDL)
+ v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
v0.AddArg(x)
v0.AddArg(y)
v.AddArg(v0)
v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
v2 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
v2.AddArg(y)
- v2.AuxInt = 8
+ v2.AuxInt = 32
v1.AddArg(v2)
v.AddArg(v1)
return true
@@ -6217,20 +5836,20 @@ func rewriteValueAMD64_OpLsh8x8(v *Value, config *Config) bool {
_ = b
// match: (Lsh8x8 <t> x y)
// cond:
- // result: (ANDB (SHLB <t> x y) (SBBLcarrymask <t> (CMPBconst y [8])))
+ // result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPBconst y [32])))
for {
t := v.Type
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64ANDB)
- v0 := b.NewValue0(v.Line, OpAMD64SHLB, t)
+ v.reset(OpAMD64ANDL)
+ v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
v0.AddArg(x)
v0.AddArg(y)
v.AddArg(v0)
v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
v2 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
v2.AddArg(y)
- v2.AuxInt = 8
+ v2.AuxInt = 32
v1.AddArg(v2)
v.AddArg(v1)
return true
@@ -6241,7 +5860,7 @@ func rewriteValueAMD64_OpAMD64MOVBQSX(v *Value, config *Config) bool {
b := v.Block
_ = b
// match: (MOVBQSX x:(MOVBload [off] {sym} ptr mem))
- // cond: x.Uses == 1
+ // cond: x.Uses == 1 && clobber(x)
// result: @x.Block (MOVBQSXload <v.Type> [off] {sym} ptr mem)
for {
x := v.Args[0]
@@ -6252,7 +5871,7 @@ func rewriteValueAMD64_OpAMD64MOVBQSX(v *Value, config *Config) bool {
sym := x.Aux
ptr := x.Args[0]
mem := x.Args[1]
- if !(x.Uses == 1) {
+ if !(x.Uses == 1 && clobber(x)) {
break
}
b = x.Block
@@ -6265,12 +5884,12 @@ func rewriteValueAMD64_OpAMD64MOVBQSX(v *Value, config *Config) bool {
v0.AddArg(mem)
return true
}
- // match: (MOVBQSX (ANDBconst [c] x))
+ // match: (MOVBQSX (ANDLconst [c] x))
// cond: c & 0x80 == 0
- // result: (ANDQconst [c & 0x7f] x)
+ // result: (ANDLconst [c & 0x7f] x)
for {
v_0 := v.Args[0]
- if v_0.Op != OpAMD64ANDBconst {
+ if v_0.Op != OpAMD64ANDLconst {
break
}
c := v_0.AuxInt
@@ -6278,7 +5897,7 @@ func rewriteValueAMD64_OpAMD64MOVBQSX(v *Value, config *Config) bool {
if !(c&0x80 == 0) {
break
}
- v.reset(OpAMD64ANDQconst)
+ v.reset(OpAMD64ANDLconst)
v.AuxInt = c & 0x7f
v.AddArg(x)
return true
@@ -6318,7 +5937,7 @@ func rewriteValueAMD64_OpAMD64MOVBQZX(v *Value, config *Config) bool {
b := v.Block
_ = b
// match: (MOVBQZX x:(MOVBload [off] {sym} ptr mem))
- // cond: x.Uses == 1
+ // cond: x.Uses == 1 && clobber(x)
// result: @x.Block (MOVBload <v.Type> [off] {sym} ptr mem)
for {
x := v.Args[0]
@@ -6329,7 +5948,7 @@ func rewriteValueAMD64_OpAMD64MOVBQZX(v *Value, config *Config) bool {
sym := x.Aux
ptr := x.Args[0]
mem := x.Args[1]
- if !(x.Uses == 1) {
+ if !(x.Uses == 1 && clobber(x)) {
break
}
b = x.Block
@@ -6343,7 +5962,7 @@ func rewriteValueAMD64_OpAMD64MOVBQZX(v *Value, config *Config) bool {
return true
}
// match: (MOVBQZX x:(MOVBloadidx1 [off] {sym} ptr idx mem))
- // cond: x.Uses == 1
+ // cond: x.Uses == 1 && clobber(x)
// result: @x.Block (MOVBloadidx1 <v.Type> [off] {sym} ptr idx mem)
for {
x := v.Args[0]
@@ -6355,7 +5974,7 @@ func rewriteValueAMD64_OpAMD64MOVBQZX(v *Value, config *Config) bool {
ptr := x.Args[0]
idx := x.Args[1]
mem := x.Args[2]
- if !(x.Uses == 1) {
+ if !(x.Uses == 1 && clobber(x)) {
break
}
b = x.Block
@@ -6369,17 +5988,17 @@ func rewriteValueAMD64_OpAMD64MOVBQZX(v *Value, config *Config) bool {
v0.AddArg(mem)
return true
}
- // match: (MOVBQZX (ANDBconst [c] x))
+ // match: (MOVBQZX (ANDLconst [c] x))
// cond:
- // result: (ANDQconst [c & 0xff] x)
+ // result: (ANDLconst [c & 0xff] x)
for {
v_0 := v.Args[0]
- if v_0.Op != OpAMD64ANDBconst {
+ if v_0.Op != OpAMD64ANDLconst {
break
}
c := v_0.AuxInt
x := v_0.Args[0]
- v.reset(OpAMD64ANDQconst)
+ v.reset(OpAMD64ANDLconst)
v.AuxInt = c & 0xff
v.AddArg(x)
return true
@@ -6630,7 +6249,7 @@ func rewriteValueAMD64_OpAMD64MOVBstore(v *Value, config *Config) bool {
v.AddArg(mem)
return true
}
- // match: (MOVBstore [off] {sym} ptr (MOVBconst [c]) mem)
+ // match: (MOVBstore [off] {sym} ptr (MOVLconst [c]) mem)
// cond: validOff(off)
// result: (MOVBstoreconst [makeValAndOff(int64(int8(c)),off)] {sym} ptr mem)
for {
@@ -6638,7 +6257,7 @@ func rewriteValueAMD64_OpAMD64MOVBstore(v *Value, config *Config) bool {
sym := v.Aux
ptr := v.Args[0]
v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVBconst {
+ if v_1.Op != OpAMD64MOVLconst {
break
}
c := v_1.AuxInt
@@ -6733,6 +6352,97 @@ func rewriteValueAMD64_OpAMD64MOVBstore(v *Value, config *Config) bool {
v.AddArg(mem)
return true
}
+ // match: (MOVBstore [i] {s} p (SHRQconst [8] w) x:(MOVBstore [i-1] {s} p w mem))
+ // cond: x.Uses == 1 && clobber(x)
+ // result: (MOVWstore [i-1] {s} p w mem)
+ for {
+ i := v.AuxInt
+ s := v.Aux
+ p := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAMD64SHRQconst {
+ break
+ }
+ if v_1.AuxInt != 8 {
+ break
+ }
+ w := v_1.Args[0]
+ x := v.Args[2]
+ if x.Op != OpAMD64MOVBstore {
+ break
+ }
+ if x.AuxInt != i-1 {
+ break
+ }
+ if x.Aux != s {
+ break
+ }
+ if p != x.Args[0] {
+ break
+ }
+ if w != x.Args[1] {
+ break
+ }
+ mem := x.Args[2]
+ if !(x.Uses == 1 && clobber(x)) {
+ break
+ }
+ v.reset(OpAMD64MOVWstore)
+ v.AuxInt = i - 1
+ v.Aux = s
+ v.AddArg(p)
+ v.AddArg(w)
+ v.AddArg(mem)
+ return true
+ }
+ // match: (MOVBstore [i] {s} p (SHRQconst [j] w) x:(MOVBstore [i-1] {s} p w0:(SHRQconst [j-8] w) mem))
+ // cond: x.Uses == 1 && clobber(x)
+ // result: (MOVWstore [i-1] {s} p w0 mem)
+ for {
+ i := v.AuxInt
+ s := v.Aux
+ p := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAMD64SHRQconst {
+ break
+ }
+ j := v_1.AuxInt
+ w := v_1.Args[0]
+ x := v.Args[2]
+ if x.Op != OpAMD64MOVBstore {
+ break
+ }
+ if x.AuxInt != i-1 {
+ break
+ }
+ if x.Aux != s {
+ break
+ }
+ if p != x.Args[0] {
+ break
+ }
+ w0 := x.Args[1]
+ if w0.Op != OpAMD64SHRQconst {
+ break
+ }
+ if w0.AuxInt != j-8 {
+ break
+ }
+ if w != w0.Args[0] {
+ break
+ }
+ mem := x.Args[2]
+ if !(x.Uses == 1 && clobber(x)) {
+ break
+ }
+ v.reset(OpAMD64MOVWstore)
+ v.AuxInt = i - 1
+ v.Aux = s
+ v.AddArg(p)
+ v.AddArg(w0)
+ v.AddArg(mem)
+ return true
+ }
return false
}
func rewriteValueAMD64_OpAMD64MOVBstoreconst(v *Value, config *Config) bool {
@@ -6832,6 +6542,35 @@ func rewriteValueAMD64_OpAMD64MOVBstoreconst(v *Value, config *Config) bool {
v.AddArg(mem)
return true
}
+ // match: (MOVBstoreconst [c] {s} p x:(MOVBstoreconst [a] {s} p mem))
+ // cond: x.Uses == 1 && ValAndOff(a).Off() + 1 == ValAndOff(c).Off() && clobber(x)
+ // result: (MOVWstoreconst [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p mem)
+ for {
+ c := v.AuxInt
+ s := v.Aux
+ p := v.Args[0]
+ x := v.Args[1]
+ if x.Op != OpAMD64MOVBstoreconst {
+ break
+ }
+ a := x.AuxInt
+ if x.Aux != s {
+ break
+ }
+ if p != x.Args[0] {
+ break
+ }
+ mem := x.Args[1]
+ if !(x.Uses == 1 && ValAndOff(a).Off()+1 == ValAndOff(c).Off() && clobber(x)) {
+ break
+ }
+ v.reset(OpAMD64MOVWstoreconst)
+ v.AuxInt = makeValAndOff(ValAndOff(a).Val()&0xff|ValAndOff(c).Val()<<8, ValAndOff(a).Off())
+ v.Aux = s
+ v.AddArg(p)
+ v.AddArg(mem)
+ return true
+ }
return false
}
func rewriteValueAMD64_OpAMD64MOVBstoreconstidx1(v *Value, config *Config) bool {
@@ -6881,6 +6620,40 @@ func rewriteValueAMD64_OpAMD64MOVBstoreconstidx1(v *Value, config *Config) bool
v.AddArg(mem)
return true
}
+ // match: (MOVBstoreconstidx1 [c] {s} p i x:(MOVBstoreconstidx1 [a] {s} p i mem))
+ // cond: x.Uses == 1 && ValAndOff(a).Off() + 1 == ValAndOff(c).Off() && clobber(x)
+ // result: (MOVWstoreconstidx1 [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p i mem)
+ for {
+ c := v.AuxInt
+ s := v.Aux
+ p := v.Args[0]
+ i := v.Args[1]
+ x := v.Args[2]
+ if x.Op != OpAMD64MOVBstoreconstidx1 {
+ break
+ }
+ a := x.AuxInt
+ if x.Aux != s {
+ break
+ }
+ if p != x.Args[0] {
+ break
+ }
+ if i != x.Args[1] {
+ break
+ }
+ mem := x.Args[2]
+ if !(x.Uses == 1 && ValAndOff(a).Off()+1 == ValAndOff(c).Off() && clobber(x)) {
+ break
+ }
+ v.reset(OpAMD64MOVWstoreconstidx1)
+ v.AuxInt = makeValAndOff(ValAndOff(a).Val()&0xff|ValAndOff(c).Val()<<8, ValAndOff(a).Off())
+ v.Aux = s
+ v.AddArg(p)
+ v.AddArg(i)
+ v.AddArg(mem)
+ return true
+ }
return false
}
func rewriteValueAMD64_OpAMD64MOVBstoreidx1(v *Value, config *Config) bool {
@@ -6934,13 +6707,114 @@ func rewriteValueAMD64_OpAMD64MOVBstoreidx1(v *Value, config *Config) bool {
v.AddArg(mem)
return true
}
+ // match: (MOVBstoreidx1 [i] {s} p idx (SHRQconst [8] w) x:(MOVBstoreidx1 [i-1] {s} p idx w mem))
+ // cond: x.Uses == 1 && clobber(x)
+ // result: (MOVWstoreidx1 [i-1] {s} p idx w mem)
+ for {
+ i := v.AuxInt
+ s := v.Aux
+ p := v.Args[0]
+ idx := v.Args[1]
+ v_2 := v.Args[2]
+ if v_2.Op != OpAMD64SHRQconst {
+ break
+ }
+ if v_2.AuxInt != 8 {
+ break
+ }
+ w := v_2.Args[0]
+ x := v.Args[3]
+ if x.Op != OpAMD64MOVBstoreidx1 {
+ break
+ }
+ if x.AuxInt != i-1 {
+ break
+ }
+ if x.Aux != s {
+ break
+ }
+ if p != x.Args[0] {
+ break
+ }
+ if idx != x.Args[1] {
+ break
+ }
+ if w != x.Args[2] {
+ break
+ }
+ mem := x.Args[3]
+ if !(x.Uses == 1 && clobber(x)) {
+ break
+ }
+ v.reset(OpAMD64MOVWstoreidx1)
+ v.AuxInt = i - 1
+ v.Aux = s
+ v.AddArg(p)
+ v.AddArg(idx)
+ v.AddArg(w)
+ v.AddArg(mem)
+ return true
+ }
+ // match: (MOVBstoreidx1 [i] {s} p idx (SHRQconst [j] w) x:(MOVBstoreidx1 [i-1] {s} p idx w0:(SHRQconst [j-8] w) mem))
+ // cond: x.Uses == 1 && clobber(x)
+ // result: (MOVWstoreidx1 [i-1] {s} p idx w0 mem)
+ for {
+ i := v.AuxInt
+ s := v.Aux
+ p := v.Args[0]
+ idx := v.Args[1]
+ v_2 := v.Args[2]
+ if v_2.Op != OpAMD64SHRQconst {
+ break
+ }
+ j := v_2.AuxInt
+ w := v_2.Args[0]
+ x := v.Args[3]
+ if x.Op != OpAMD64MOVBstoreidx1 {
+ break
+ }
+ if x.AuxInt != i-1 {
+ break
+ }
+ if x.Aux != s {
+ break
+ }
+ if p != x.Args[0] {
+ break
+ }
+ if idx != x.Args[1] {
+ break
+ }
+ w0 := x.Args[2]
+ if w0.Op != OpAMD64SHRQconst {
+ break
+ }
+ if w0.AuxInt != j-8 {
+ break
+ }
+ if w != w0.Args[0] {
+ break
+ }
+ mem := x.Args[3]
+ if !(x.Uses == 1 && clobber(x)) {
+ break
+ }
+ v.reset(OpAMD64MOVWstoreidx1)
+ v.AuxInt = i - 1
+ v.Aux = s
+ v.AddArg(p)
+ v.AddArg(idx)
+ v.AddArg(w0)
+ v.AddArg(mem)
+ return true
+ }
return false
}
func rewriteValueAMD64_OpAMD64MOVLQSX(v *Value, config *Config) bool {
b := v.Block
_ = b
// match: (MOVLQSX x:(MOVLload [off] {sym} ptr mem))
- // cond: x.Uses == 1
+ // cond: x.Uses == 1 && clobber(x)
// result: @x.Block (MOVLQSXload <v.Type> [off] {sym} ptr mem)
for {
x := v.Args[0]
@@ -6951,7 +6825,7 @@ func rewriteValueAMD64_OpAMD64MOVLQSX(v *Value, config *Config) bool {
sym := x.Aux
ptr := x.Args[0]
mem := x.Args[1]
- if !(x.Uses == 1) {
+ if !(x.Uses == 1 && clobber(x)) {
break
}
b = x.Block
@@ -6966,7 +6840,7 @@ func rewriteValueAMD64_OpAMD64MOVLQSX(v *Value, config *Config) bool {
}
// match: (MOVLQSX (ANDLconst [c] x))
// cond: c & 0x80000000 == 0
- // result: (ANDQconst [c & 0x7fffffff] x)
+ // result: (ANDLconst [c & 0x7fffffff] x)
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64ANDLconst {
@@ -6977,7 +6851,7 @@ func rewriteValueAMD64_OpAMD64MOVLQSX(v *Value, config *Config) bool {
if !(c&0x80000000 == 0) {
break
}
- v.reset(OpAMD64ANDQconst)
+ v.reset(OpAMD64ANDLconst)
v.AuxInt = c & 0x7fffffff
v.AddArg(x)
return true
@@ -7017,7 +6891,7 @@ func rewriteValueAMD64_OpAMD64MOVLQZX(v *Value, config *Config) bool {
b := v.Block
_ = b
// match: (MOVLQZX x:(MOVLload [off] {sym} ptr mem))
- // cond: x.Uses == 1
+ // cond: x.Uses == 1 && clobber(x)
// result: @x.Block (MOVLload <v.Type> [off] {sym} ptr mem)
for {
x := v.Args[0]
@@ -7028,7 +6902,7 @@ func rewriteValueAMD64_OpAMD64MOVLQZX(v *Value, config *Config) bool {
sym := x.Aux
ptr := x.Args[0]
mem := x.Args[1]
- if !(x.Uses == 1) {
+ if !(x.Uses == 1 && clobber(x)) {
break
}
b = x.Block
@@ -7042,7 +6916,7 @@ func rewriteValueAMD64_OpAMD64MOVLQZX(v *Value, config *Config) bool {
return true
}
// match: (MOVLQZX x:(MOVLloadidx1 [off] {sym} ptr idx mem))
- // cond: x.Uses == 1
+ // cond: x.Uses == 1 && clobber(x)
// result: @x.Block (MOVLloadidx1 <v.Type> [off] {sym} ptr idx mem)
for {
x := v.Args[0]
@@ -7054,7 +6928,7 @@ func rewriteValueAMD64_OpAMD64MOVLQZX(v *Value, config *Config) bool {
ptr := x.Args[0]
idx := x.Args[1]
mem := x.Args[2]
- if !(x.Uses == 1) {
+ if !(x.Uses == 1 && clobber(x)) {
break
}
b = x.Block
@@ -7069,7 +6943,7 @@ func rewriteValueAMD64_OpAMD64MOVLQZX(v *Value, config *Config) bool {
return true
}
// match: (MOVLQZX x:(MOVLloadidx4 [off] {sym} ptr idx mem))
- // cond: x.Uses == 1
+ // cond: x.Uses == 1 && clobber(x)
// result: @x.Block (MOVLloadidx4 <v.Type> [off] {sym} ptr idx mem)
for {
x := v.Args[0]
@@ -7081,7 +6955,7 @@ func rewriteValueAMD64_OpAMD64MOVLQZX(v *Value, config *Config) bool {
ptr := x.Args[0]
idx := x.Args[1]
mem := x.Args[2]
- if !(x.Uses == 1) {
+ if !(x.Uses == 1 && clobber(x)) {
break
}
b = x.Block
@@ -7096,8 +6970,8 @@ func rewriteValueAMD64_OpAMD64MOVLQZX(v *Value, config *Config) bool {
return true
}
// match: (MOVLQZX (ANDLconst [c] x))
- // cond: c & 0x80000000 == 0
- // result: (ANDQconst [c & 0x7fffffff] x)
+ // cond:
+ // result: (ANDLconst [c] x)
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64ANDLconst {
@@ -7105,11 +6979,8 @@ func rewriteValueAMD64_OpAMD64MOVLQZX(v *Value, config *Config) bool {
}
c := v_0.AuxInt
x := v_0.Args[0]
- if !(c&0x80000000 == 0) {
- break
- }
- v.reset(OpAMD64ANDQconst)
- v.AuxInt = c & 0x7fffffff
+ v.reset(OpAMD64ANDLconst)
+ v.AuxInt = c
v.AddArg(x)
return true
}
@@ -7589,6 +7460,97 @@ func rewriteValueAMD64_OpAMD64MOVLstore(v *Value, config *Config) bool {
v.AddArg(mem)
return true
}
+ // match: (MOVLstore [i] {s} p (SHRQconst [32] w) x:(MOVLstore [i-4] {s} p w mem))
+ // cond: x.Uses == 1 && clobber(x)
+ // result: (MOVQstore [i-4] {s} p w mem)
+ for {
+ i := v.AuxInt
+ s := v.Aux
+ p := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAMD64SHRQconst {
+ break
+ }
+ if v_1.AuxInt != 32 {
+ break
+ }
+ w := v_1.Args[0]
+ x := v.Args[2]
+ if x.Op != OpAMD64MOVLstore {
+ break
+ }
+ if x.AuxInt != i-4 {
+ break
+ }
+ if x.Aux != s {
+ break
+ }
+ if p != x.Args[0] {
+ break
+ }
+ if w != x.Args[1] {
+ break
+ }
+ mem := x.Args[2]
+ if !(x.Uses == 1 && clobber(x)) {
+ break
+ }
+ v.reset(OpAMD64MOVQstore)
+ v.AuxInt = i - 4
+ v.Aux = s
+ v.AddArg(p)
+ v.AddArg(w)
+ v.AddArg(mem)
+ return true
+ }
+ // match: (MOVLstore [i] {s} p (SHRQconst [j] w) x:(MOVLstore [i-4] {s} p w0:(SHRQconst [j-32] w) mem))
+ // cond: x.Uses == 1 && clobber(x)
+ // result: (MOVQstore [i-4] {s} p w0 mem)
+ for {
+ i := v.AuxInt
+ s := v.Aux
+ p := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAMD64SHRQconst {
+ break
+ }
+ j := v_1.AuxInt
+ w := v_1.Args[0]
+ x := v.Args[2]
+ if x.Op != OpAMD64MOVLstore {
+ break
+ }
+ if x.AuxInt != i-4 {
+ break
+ }
+ if x.Aux != s {
+ break
+ }
+ if p != x.Args[0] {
+ break
+ }
+ w0 := x.Args[1]
+ if w0.Op != OpAMD64SHRQconst {
+ break
+ }
+ if w0.AuxInt != j-32 {
+ break
+ }
+ if w != w0.Args[0] {
+ break
+ }
+ mem := x.Args[2]
+ if !(x.Uses == 1 && clobber(x)) {
+ break
+ }
+ v.reset(OpAMD64MOVQstore)
+ v.AuxInt = i - 4
+ v.Aux = s
+ v.AddArg(p)
+ v.AddArg(w0)
+ v.AddArg(mem)
+ return true
+ }
return false
}
func rewriteValueAMD64_OpAMD64MOVLstoreconst(v *Value, config *Config) bool {
@@ -7714,6 +7676,38 @@ func rewriteValueAMD64_OpAMD64MOVLstoreconst(v *Value, config *Config) bool {
v.AddArg(mem)
return true
}
+ // match: (MOVLstoreconst [c] {s} p x:(MOVLstoreconst [a] {s} p mem))
+ // cond: x.Uses == 1 && ValAndOff(a).Off() + 4 == ValAndOff(c).Off() && clobber(x)
+ // result: (MOVQstore [ValAndOff(a).Off()] {s} p (MOVQconst [ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32]) mem)
+ for {
+ c := v.AuxInt
+ s := v.Aux
+ p := v.Args[0]
+ x := v.Args[1]
+ if x.Op != OpAMD64MOVLstoreconst {
+ break
+ }
+ a := x.AuxInt
+ if x.Aux != s {
+ break
+ }
+ if p != x.Args[0] {
+ break
+ }
+ mem := x.Args[1]
+ if !(x.Uses == 1 && ValAndOff(a).Off()+4 == ValAndOff(c).Off() && clobber(x)) {
+ break
+ }
+ v.reset(OpAMD64MOVQstore)
+ v.AuxInt = ValAndOff(a).Off()
+ v.Aux = s
+ v.AddArg(p)
+ v0 := b.NewValue0(v.Line, OpAMD64MOVQconst, config.fe.TypeUInt64())
+ v0.AuxInt = ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32
+ v.AddArg(v0)
+ v.AddArg(mem)
+ return true
+ }
return false
}
func rewriteValueAMD64_OpAMD64MOVLstoreconstidx1(v *Value, config *Config) bool {
@@ -7787,6 +7781,43 @@ func rewriteValueAMD64_OpAMD64MOVLstoreconstidx1(v *Value, config *Config) bool
v.AddArg(mem)
return true
}
+ // match: (MOVLstoreconstidx1 [c] {s} p i x:(MOVLstoreconstidx1 [a] {s} p i mem))
+ // cond: x.Uses == 1 && ValAndOff(a).Off() + 4 == ValAndOff(c).Off() && clobber(x)
+ // result: (MOVQstoreidx1 [ValAndOff(a).Off()] {s} p i (MOVQconst [ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32]) mem)
+ for {
+ c := v.AuxInt
+ s := v.Aux
+ p := v.Args[0]
+ i := v.Args[1]
+ x := v.Args[2]
+ if x.Op != OpAMD64MOVLstoreconstidx1 {
+ break
+ }
+ a := x.AuxInt
+ if x.Aux != s {
+ break
+ }
+ if p != x.Args[0] {
+ break
+ }
+ if i != x.Args[1] {
+ break
+ }
+ mem := x.Args[2]
+ if !(x.Uses == 1 && ValAndOff(a).Off()+4 == ValAndOff(c).Off() && clobber(x)) {
+ break
+ }
+ v.reset(OpAMD64MOVQstoreidx1)
+ v.AuxInt = ValAndOff(a).Off()
+ v.Aux = s
+ v.AddArg(p)
+ v.AddArg(i)
+ v0 := b.NewValue0(v.Line, OpAMD64MOVQconst, config.fe.TypeUInt64())
+ v0.AuxInt = ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32
+ v.AddArg(v0)
+ v.AddArg(mem)
+ return true
+ }
return false
}
func rewriteValueAMD64_OpAMD64MOVLstoreconstidx4(v *Value, config *Config) bool {
@@ -7836,6 +7867,46 @@ func rewriteValueAMD64_OpAMD64MOVLstoreconstidx4(v *Value, config *Config) bool
v.AddArg(mem)
return true
}
+ // match: (MOVLstoreconstidx4 [c] {s} p i x:(MOVLstoreconstidx4 [a] {s} p i mem))
+ // cond: x.Uses == 1 && ValAndOff(a).Off() + 4 == ValAndOff(c).Off() && clobber(x)
+ // result: (MOVQstoreidx1 [ValAndOff(a).Off()] {s} p (SHLQconst <i.Type> [2] i) (MOVQconst [ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32]) mem)
+ for {
+ c := v.AuxInt
+ s := v.Aux
+ p := v.Args[0]
+ i := v.Args[1]
+ x := v.Args[2]
+ if x.Op != OpAMD64MOVLstoreconstidx4 {
+ break
+ }
+ a := x.AuxInt
+ if x.Aux != s {
+ break
+ }
+ if p != x.Args[0] {
+ break
+ }
+ if i != x.Args[1] {
+ break
+ }
+ mem := x.Args[2]
+ if !(x.Uses == 1 && ValAndOff(a).Off()+4 == ValAndOff(c).Off() && clobber(x)) {
+ break
+ }
+ v.reset(OpAMD64MOVQstoreidx1)
+ v.AuxInt = ValAndOff(a).Off()
+ v.Aux = s
+ v.AddArg(p)
+ v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, i.Type)
+ v0.AuxInt = 2
+ v0.AddArg(i)
+ v.AddArg(v0)
+ v1 := b.NewValue0(v.Line, OpAMD64MOVQconst, config.fe.TypeUInt64())
+ v1.AuxInt = ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32
+ v.AddArg(v1)
+ v.AddArg(mem)
+ return true
+ }
return false
}
func rewriteValueAMD64_OpAMD64MOVLstoreidx1(v *Value, config *Config) bool {
@@ -7915,6 +7986,107 @@ func rewriteValueAMD64_OpAMD64MOVLstoreidx1(v *Value, config *Config) bool {
v.AddArg(mem)
return true
}
+ // match: (MOVLstoreidx1 [i] {s} p idx (SHRQconst [32] w) x:(MOVLstoreidx1 [i-4] {s} p idx w mem))
+ // cond: x.Uses == 1 && clobber(x)
+ // result: (MOVQstoreidx1 [i-4] {s} p idx w mem)
+ for {
+ i := v.AuxInt
+ s := v.Aux
+ p := v.Args[0]
+ idx := v.Args[1]
+ v_2 := v.Args[2]
+ if v_2.Op != OpAMD64SHRQconst {
+ break
+ }
+ if v_2.AuxInt != 32 {
+ break
+ }
+ w := v_2.Args[0]
+ x := v.Args[3]
+ if x.Op != OpAMD64MOVLstoreidx1 {
+ break
+ }
+ if x.AuxInt != i-4 {
+ break
+ }
+ if x.Aux != s {
+ break
+ }
+ if p != x.Args[0] {
+ break
+ }
+ if idx != x.Args[1] {
+ break
+ }
+ if w != x.Args[2] {
+ break
+ }
+ mem := x.Args[3]
+ if !(x.Uses == 1 && clobber(x)) {
+ break
+ }
+ v.reset(OpAMD64MOVQstoreidx1)
+ v.AuxInt = i - 4
+ v.Aux = s
+ v.AddArg(p)
+ v.AddArg(idx)
+ v.AddArg(w)
+ v.AddArg(mem)
+ return true
+ }
+ // match: (MOVLstoreidx1 [i] {s} p idx (SHRQconst [j] w) x:(MOVLstoreidx1 [i-4] {s} p idx w0:(SHRQconst [j-32] w) mem))
+ // cond: x.Uses == 1 && clobber(x)
+ // result: (MOVQstoreidx1 [i-4] {s} p idx w0 mem)
+ for {
+ i := v.AuxInt
+ s := v.Aux
+ p := v.Args[0]
+ idx := v.Args[1]
+ v_2 := v.Args[2]
+ if v_2.Op != OpAMD64SHRQconst {
+ break
+ }
+ j := v_2.AuxInt
+ w := v_2.Args[0]
+ x := v.Args[3]
+ if x.Op != OpAMD64MOVLstoreidx1 {
+ break
+ }
+ if x.AuxInt != i-4 {
+ break
+ }
+ if x.Aux != s {
+ break
+ }
+ if p != x.Args[0] {
+ break
+ }
+ if idx != x.Args[1] {
+ break
+ }
+ w0 := x.Args[2]
+ if w0.Op != OpAMD64SHRQconst {
+ break
+ }
+ if w0.AuxInt != j-32 {
+ break
+ }
+ if w != w0.Args[0] {
+ break
+ }
+ mem := x.Args[3]
+ if !(x.Uses == 1 && clobber(x)) {
+ break
+ }
+ v.reset(OpAMD64MOVQstoreidx1)
+ v.AuxInt = i - 4
+ v.Aux = s
+ v.AddArg(p)
+ v.AddArg(idx)
+ v.AddArg(w0)
+ v.AddArg(mem)
+ return true
+ }
return false
}
func rewriteValueAMD64_OpAMD64MOVLstoreidx4(v *Value, config *Config) bool {
@@ -7968,6 +8140,113 @@ func rewriteValueAMD64_OpAMD64MOVLstoreidx4(v *Value, config *Config) bool {
v.AddArg(mem)
return true
}
+ // match: (MOVLstoreidx4 [i] {s} p idx (SHRQconst [32] w) x:(MOVLstoreidx4 [i-4] {s} p idx w mem))
+ // cond: x.Uses == 1 && clobber(x)
+ // result: (MOVQstoreidx1 [i-4] {s} p (SHLQconst <idx.Type> [2] idx) w mem)
+ for {
+ i := v.AuxInt
+ s := v.Aux
+ p := v.Args[0]
+ idx := v.Args[1]
+ v_2 := v.Args[2]
+ if v_2.Op != OpAMD64SHRQconst {
+ break
+ }
+ if v_2.AuxInt != 32 {
+ break
+ }
+ w := v_2.Args[0]
+ x := v.Args[3]
+ if x.Op != OpAMD64MOVLstoreidx4 {
+ break
+ }
+ if x.AuxInt != i-4 {
+ break
+ }
+ if x.Aux != s {
+ break
+ }
+ if p != x.Args[0] {
+ break
+ }
+ if idx != x.Args[1] {
+ break
+ }
+ if w != x.Args[2] {
+ break
+ }
+ mem := x.Args[3]
+ if !(x.Uses == 1 && clobber(x)) {
+ break
+ }
+ v.reset(OpAMD64MOVQstoreidx1)
+ v.AuxInt = i - 4
+ v.Aux = s
+ v.AddArg(p)
+ v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, idx.Type)
+ v0.AuxInt = 2
+ v0.AddArg(idx)
+ v.AddArg(v0)
+ v.AddArg(w)
+ v.AddArg(mem)
+ return true
+ }
+ // match: (MOVLstoreidx4 [i] {s} p idx (SHRQconst [j] w) x:(MOVLstoreidx4 [i-4] {s} p idx w0:(SHRQconst [j-32] w) mem))
+ // cond: x.Uses == 1 && clobber(x)
+ // result: (MOVQstoreidx1 [i-4] {s} p (SHLQconst <idx.Type> [2] idx) w0 mem)
+ for {
+ i := v.AuxInt
+ s := v.Aux
+ p := v.Args[0]
+ idx := v.Args[1]
+ v_2 := v.Args[2]
+ if v_2.Op != OpAMD64SHRQconst {
+ break
+ }
+ j := v_2.AuxInt
+ w := v_2.Args[0]
+ x := v.Args[3]
+ if x.Op != OpAMD64MOVLstoreidx4 {
+ break
+ }
+ if x.AuxInt != i-4 {
+ break
+ }
+ if x.Aux != s {
+ break
+ }
+ if p != x.Args[0] {
+ break
+ }
+ if idx != x.Args[1] {
+ break
+ }
+ w0 := x.Args[2]
+ if w0.Op != OpAMD64SHRQconst {
+ break
+ }
+ if w0.AuxInt != j-32 {
+ break
+ }
+ if w != w0.Args[0] {
+ break
+ }
+ mem := x.Args[3]
+ if !(x.Uses == 1 && clobber(x)) {
+ break
+ }
+ v.reset(OpAMD64MOVQstoreidx1)
+ v.AuxInt = i - 4
+ v.Aux = s
+ v.AddArg(p)
+ v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, idx.Type)
+ v0.AuxInt = 2
+ v0.AddArg(idx)
+ v.AddArg(v0)
+ v.AddArg(w0)
+ v.AddArg(mem)
+ return true
+ }
return false
}
func rewriteValueAMD64_OpAMD64MOVOload(v *Value, config *Config) bool {
@@ -9835,7 +10114,7 @@ func rewriteValueAMD64_OpAMD64MOVWQSX(v *Value, config *Config) bool {
b := v.Block
_ = b
// match: (MOVWQSX x:(MOVWload [off] {sym} ptr mem))
- // cond: x.Uses == 1
+ // cond: x.Uses == 1 && clobber(x)
// result: @x.Block (MOVWQSXload <v.Type> [off] {sym} ptr mem)
for {
x := v.Args[0]
@@ -9846,7 +10125,7 @@ func rewriteValueAMD64_OpAMD64MOVWQSX(v *Value, config *Config) bool {
sym := x.Aux
ptr := x.Args[0]
mem := x.Args[1]
- if !(x.Uses == 1) {
+ if !(x.Uses == 1 && clobber(x)) {
break
}
b = x.Block
@@ -9859,12 +10138,12 @@ func rewriteValueAMD64_OpAMD64MOVWQSX(v *Value, config *Config) bool {
v0.AddArg(mem)
return true
}
- // match: (MOVWQSX (ANDWconst [c] x))
+ // match: (MOVWQSX (ANDLconst [c] x))
// cond: c & 0x8000 == 0
- // result: (ANDQconst [c & 0x7fff] x)
+ // result: (ANDLconst [c & 0x7fff] x)
for {
v_0 := v.Args[0]
- if v_0.Op != OpAMD64ANDWconst {
+ if v_0.Op != OpAMD64ANDLconst {
break
}
c := v_0.AuxInt
@@ -9872,7 +10151,7 @@ func rewriteValueAMD64_OpAMD64MOVWQSX(v *Value, config *Config) bool {
if !(c&0x8000 == 0) {
break
}
- v.reset(OpAMD64ANDQconst)
+ v.reset(OpAMD64ANDLconst)
v.AuxInt = c & 0x7fff
v.AddArg(x)
return true
@@ -9912,7 +10191,7 @@ func rewriteValueAMD64_OpAMD64MOVWQZX(v *Value, config *Config) bool {
b := v.Block
_ = b
// match: (MOVWQZX x:(MOVWload [off] {sym} ptr mem))
- // cond: x.Uses == 1
+ // cond: x.Uses == 1 && clobber(x)
// result: @x.Block (MOVWload <v.Type> [off] {sym} ptr mem)
for {
x := v.Args[0]
@@ -9923,7 +10202,7 @@ func rewriteValueAMD64_OpAMD64MOVWQZX(v *Value, config *Config) bool {
sym := x.Aux
ptr := x.Args[0]
mem := x.Args[1]
- if !(x.Uses == 1) {
+ if !(x.Uses == 1 && clobber(x)) {
break
}
b = x.Block
@@ -9937,7 +10216,7 @@ func rewriteValueAMD64_OpAMD64MOVWQZX(v *Value, config *Config) bool {
return true
}
// match: (MOVWQZX x:(MOVWloadidx1 [off] {sym} ptr idx mem))
- // cond: x.Uses == 1
+ // cond: x.Uses == 1 && clobber(x)
// result: @x.Block (MOVWloadidx1 <v.Type> [off] {sym} ptr idx mem)
for {
x := v.Args[0]
@@ -9949,7 +10228,7 @@ func rewriteValueAMD64_OpAMD64MOVWQZX(v *Value, config *Config) bool {
ptr := x.Args[0]
idx := x.Args[1]
mem := x.Args[2]
- if !(x.Uses == 1) {
+ if !(x.Uses == 1 && clobber(x)) {
break
}
b = x.Block
@@ -9964,7 +10243,7 @@ func rewriteValueAMD64_OpAMD64MOVWQZX(v *Value, config *Config) bool {
return true
}
// match: (MOVWQZX x:(MOVWloadidx2 [off] {sym} ptr idx mem))
- // cond: x.Uses == 1
+ // cond: x.Uses == 1 && clobber(x)
// result: @x.Block (MOVWloadidx2 <v.Type> [off] {sym} ptr idx mem)
for {
x := v.Args[0]
@@ -9976,7 +10255,7 @@ func rewriteValueAMD64_OpAMD64MOVWQZX(v *Value, config *Config) bool {
ptr := x.Args[0]
idx := x.Args[1]
mem := x.Args[2]
- if !(x.Uses == 1) {
+ if !(x.Uses == 1 && clobber(x)) {
break
}
b = x.Block
@@ -9990,17 +10269,17 @@ func rewriteValueAMD64_OpAMD64MOVWQZX(v *Value, config *Config) bool {
v0.AddArg(mem)
return true
}
- // match: (MOVWQZX (ANDWconst [c] x))
+ // match: (MOVWQZX (ANDLconst [c] x))
// cond:
- // result: (ANDQconst [c & 0xffff] x)
+ // result: (ANDLconst [c & 0xffff] x)
for {
v_0 := v.Args[0]
- if v_0.Op != OpAMD64ANDWconst {
+ if v_0.Op != OpAMD64ANDLconst {
break
}
c := v_0.AuxInt
x := v_0.Args[0]
- v.reset(OpAMD64ANDQconst)
+ v.reset(OpAMD64ANDLconst)
v.AuxInt = c & 0xffff
v.AddArg(x)
return true
@@ -10350,7 +10629,7 @@ func rewriteValueAMD64_OpAMD64MOVWstore(v *Value, config *Config) bool {
v.AddArg(mem)
return true
}
- // match: (MOVWstore [off] {sym} ptr (MOVWconst [c]) mem)
+ // match: (MOVWstore [off] {sym} ptr (MOVLconst [c]) mem)
// cond: validOff(off)
// result: (MOVWstoreconst [makeValAndOff(int64(int16(c)),off)] {sym} ptr mem)
for {
@@ -10358,7 +10637,7 @@ func rewriteValueAMD64_OpAMD64MOVWstore(v *Value, config *Config) bool {
sym := v.Aux
ptr := v.Args[0]
v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVWconst {
+ if v_1.Op != OpAMD64MOVLconst {
break
}
c := v_1.AuxInt
@@ -10481,6 +10760,97 @@ func rewriteValueAMD64_OpAMD64MOVWstore(v *Value, config *Config) bool {
v.AddArg(mem)
return true
}
+ // match: (MOVWstore [i] {s} p (SHRQconst [16] w) x:(MOVWstore [i-2] {s} p w mem))
+ // cond: x.Uses == 1 && clobber(x)
+ // result: (MOVLstore [i-2] {s} p w mem)
+ for {
+ i := v.AuxInt
+ s := v.Aux
+ p := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAMD64SHRQconst {
+ break
+ }
+ if v_1.AuxInt != 16 {
+ break
+ }
+ w := v_1.Args[0]
+ x := v.Args[2]
+ if x.Op != OpAMD64MOVWstore {
+ break
+ }
+ if x.AuxInt != i-2 {
+ break
+ }
+ if x.Aux != s {
+ break
+ }
+ if p != x.Args[0] {
+ break
+ }
+ if w != x.Args[1] {
+ break
+ }
+ mem := x.Args[2]
+ if !(x.Uses == 1 && clobber(x)) {
+ break
+ }
+ v.reset(OpAMD64MOVLstore)
+ v.AuxInt = i - 2
+ v.Aux = s
+ v.AddArg(p)
+ v.AddArg(w)
+ v.AddArg(mem)
+ return true
+ }
+ // match: (MOVWstore [i] {s} p (SHRQconst [j] w) x:(MOVWstore [i-2] {s} p w0:(SHRQconst [j-16] w) mem))
+ // cond: x.Uses == 1 && clobber(x)
+ // result: (MOVLstore [i-2] {s} p w0 mem)
+ for {
+ i := v.AuxInt
+ s := v.Aux
+ p := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAMD64SHRQconst {
+ break
+ }
+ j := v_1.AuxInt
+ w := v_1.Args[0]
+ x := v.Args[2]
+ if x.Op != OpAMD64MOVWstore {
+ break
+ }
+ if x.AuxInt != i-2 {
+ break
+ }
+ if x.Aux != s {
+ break
+ }
+ if p != x.Args[0] {
+ break
+ }
+ w0 := x.Args[1]
+ if w0.Op != OpAMD64SHRQconst {
+ break
+ }
+ if w0.AuxInt != j-16 {
+ break
+ }
+ if w != w0.Args[0] {
+ break
+ }
+ mem := x.Args[2]
+ if !(x.Uses == 1 && clobber(x)) {
+ break
+ }
+ v.reset(OpAMD64MOVLstore)
+ v.AuxInt = i - 2
+ v.Aux = s
+ v.AddArg(p)
+ v.AddArg(w0)
+ v.AddArg(mem)
+ return true
+ }
return false
}
func rewriteValueAMD64_OpAMD64MOVWstoreconst(v *Value, config *Config) bool {
@@ -10606,6 +10976,35 @@ func rewriteValueAMD64_OpAMD64MOVWstoreconst(v *Value, config *Config) bool {
v.AddArg(mem)
return true
}
+ // match: (MOVWstoreconst [c] {s} p x:(MOVWstoreconst [a] {s} p mem))
+ // cond: x.Uses == 1 && ValAndOff(a).Off() + 2 == ValAndOff(c).Off() && clobber(x)
+ // result: (MOVLstoreconst [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p mem)
+ for {
+ c := v.AuxInt
+ s := v.Aux
+ p := v.Args[0]
+ x := v.Args[1]
+ if x.Op != OpAMD64MOVWstoreconst {
+ break
+ }
+ a := x.AuxInt
+ if x.Aux != s {
+ break
+ }
+ if p != x.Args[0] {
+ break
+ }
+ mem := x.Args[1]
+ if !(x.Uses == 1 && ValAndOff(a).Off()+2 == ValAndOff(c).Off() && clobber(x)) {
+ break
+ }
+ v.reset(OpAMD64MOVLstoreconst)
+ v.AuxInt = makeValAndOff(ValAndOff(a).Val()&0xffff|ValAndOff(c).Val()<<16, ValAndOff(a).Off())
+ v.Aux = s
+ v.AddArg(p)
+ v.AddArg(mem)
+ return true
+ }
return false
}
func rewriteValueAMD64_OpAMD64MOVWstoreconstidx1(v *Value, config *Config) bool {
@@ -10679,6 +11078,40 @@ func rewriteValueAMD64_OpAMD64MOVWstoreconstidx1(v *Value, config *Config) bool
v.AddArg(mem)
return true
}
+ // match: (MOVWstoreconstidx1 [c] {s} p i x:(MOVWstoreconstidx1 [a] {s} p i mem))
+ // cond: x.Uses == 1 && ValAndOff(a).Off() + 2 == ValAndOff(c).Off() && clobber(x)
+ // result: (MOVLstoreconstidx1 [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p i mem)
+ for {
+ c := v.AuxInt
+ s := v.Aux
+ p := v.Args[0]
+ i := v.Args[1]
+ x := v.Args[2]
+ if x.Op != OpAMD64MOVWstoreconstidx1 {
+ break
+ }
+ a := x.AuxInt
+ if x.Aux != s {
+ break
+ }
+ if p != x.Args[0] {
+ break
+ }
+ if i != x.Args[1] {
+ break
+ }
+ mem := x.Args[2]
+ if !(x.Uses == 1 && ValAndOff(a).Off()+2 == ValAndOff(c).Off() && clobber(x)) {
+ break
+ }
+ v.reset(OpAMD64MOVLstoreconstidx1)
+ v.AuxInt = makeValAndOff(ValAndOff(a).Val()&0xffff|ValAndOff(c).Val()<<16, ValAndOff(a).Off())
+ v.Aux = s
+ v.AddArg(p)
+ v.AddArg(i)
+ v.AddArg(mem)
+ return true
+ }
return false
}
func rewriteValueAMD64_OpAMD64MOVWstoreconstidx2(v *Value, config *Config) bool {
@@ -10728,6 +11161,43 @@ func rewriteValueAMD64_OpAMD64MOVWstoreconstidx2(v *Value, config *Config) bool
v.AddArg(mem)
return true
}
+ // match: (MOVWstoreconstidx2 [c] {s} p i x:(MOVWstoreconstidx2 [a] {s} p i mem))
+ // cond: x.Uses == 1 && ValAndOff(a).Off() + 2 == ValAndOff(c).Off() && clobber(x)
+ // result: (MOVLstoreconstidx1 [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p (SHLQconst <i.Type> [1] i) mem)
+ for {
+ c := v.AuxInt
+ s := v.Aux
+ p := v.Args[0]
+ i := v.Args[1]
+ x := v.Args[2]
+ if x.Op != OpAMD64MOVWstoreconstidx2 {
+ break
+ }
+ a := x.AuxInt
+ if x.Aux != s {
+ break
+ }
+ if p != x.Args[0] {
+ break
+ }
+ if i != x.Args[1] {
+ break
+ }
+ mem := x.Args[2]
+ if !(x.Uses == 1 && ValAndOff(a).Off()+2 == ValAndOff(c).Off() && clobber(x)) {
+ break
+ }
+ v.reset(OpAMD64MOVLstoreconstidx1)
+ v.AuxInt = makeValAndOff(ValAndOff(a).Val()&0xffff|ValAndOff(c).Val()<<16, ValAndOff(a).Off())
+ v.Aux = s
+ v.AddArg(p)
+ v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, i.Type)
+ v0.AuxInt = 1
+ v0.AddArg(i)
+ v.AddArg(v0)
+ v.AddArg(mem)
+ return true
+ }
return false
}
func rewriteValueAMD64_OpAMD64MOVWstoreidx1(v *Value, config *Config) bool {
@@ -10807,6 +11277,107 @@ func rewriteValueAMD64_OpAMD64MOVWstoreidx1(v *Value, config *Config) bool {
v.AddArg(mem)
return true
}
+ // match: (MOVWstoreidx1 [i] {s} p idx (SHRQconst [16] w) x:(MOVWstoreidx1 [i-2] {s} p idx w mem))
+ // cond: x.Uses == 1 && clobber(x)
+ // result: (MOVLstoreidx1 [i-2] {s} p idx w mem)
+ for {
+ i := v.AuxInt
+ s := v.Aux
+ p := v.Args[0]
+ idx := v.Args[1]
+ v_2 := v.Args[2]
+ if v_2.Op != OpAMD64SHRQconst {
+ break
+ }
+ if v_2.AuxInt != 16 {
+ break
+ }
+ w := v_2.Args[0]
+ x := v.Args[3]
+ if x.Op != OpAMD64MOVWstoreidx1 {
+ break
+ }
+ if x.AuxInt != i-2 {
+ break
+ }
+ if x.Aux != s {
+ break
+ }
+ if p != x.Args[0] {
+ break
+ }
+ if idx != x.Args[1] {
+ break
+ }
+ if w != x.Args[2] {
+ break
+ }
+ mem := x.Args[3]
+ if !(x.Uses == 1 && clobber(x)) {
+ break
+ }
+ v.reset(OpAMD64MOVLstoreidx1)
+ v.AuxInt = i - 2
+ v.Aux = s
+ v.AddArg(p)
+ v.AddArg(idx)
+ v.AddArg(w)
+ v.AddArg(mem)
+ return true
+ }
+ // match: (MOVWstoreidx1 [i] {s} p idx (SHRQconst [j] w) x:(MOVWstoreidx1 [i-2] {s} p idx w0:(SHRQconst [j-16] w) mem))
+ // cond: x.Uses == 1 && clobber(x)
+ // result: (MOVLstoreidx1 [i-2] {s} p idx w0 mem)
+ for {
+ i := v.AuxInt
+ s := v.Aux
+ p := v.Args[0]
+ idx := v.Args[1]
+ v_2 := v.Args[2]
+ if v_2.Op != OpAMD64SHRQconst {
+ break
+ }
+ j := v_2.AuxInt
+ w := v_2.Args[0]
+ x := v.Args[3]
+ if x.Op != OpAMD64MOVWstoreidx1 {
+ break
+ }
+ if x.AuxInt != i-2 {
+ break
+ }
+ if x.Aux != s {
+ break
+ }
+ if p != x.Args[0] {
+ break
+ }
+ if idx != x.Args[1] {
+ break
+ }
+ w0 := x.Args[2]
+ if w0.Op != OpAMD64SHRQconst {
+ break
+ }
+ if w0.AuxInt != j-16 {
+ break
+ }
+ if w != w0.Args[0] {
+ break
+ }
+ mem := x.Args[3]
+ if !(x.Uses == 1 && clobber(x)) {
+ break
+ }
+ v.reset(OpAMD64MOVLstoreidx1)
+ v.AuxInt = i - 2
+ v.Aux = s
+ v.AddArg(p)
+ v.AddArg(idx)
+ v.AddArg(w0)
+ v.AddArg(mem)
+ return true
+ }
return false
}
func rewriteValueAMD64_OpAMD64MOVWstoreidx2(v *Value, config *Config) bool {
@@ -10860,58 +11431,111 @@ func rewriteValueAMD64_OpAMD64MOVWstoreidx2(v *Value, config *Config) bool {
v.AddArg(mem)
return true
}
- return false
-}
-func rewriteValueAMD64_OpAMD64MULB(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (MULB x (MOVBconst [c]))
- // cond:
- // result: (MULBconst [c] x)
+ // match: (MOVWstoreidx2 [i] {s} p idx (SHRQconst [16] w) x:(MOVWstoreidx2 [i-2] {s} p idx w mem))
+ // cond: x.Uses == 1 && clobber(x)
+ // result: (MOVLstoreidx1 [i-2] {s} p (SHLQconst <idx.Type> [1] idx) w mem)
for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVBconst {
+ i := v.AuxInt
+ s := v.Aux
+ p := v.Args[0]
+ idx := v.Args[1]
+ v_2 := v.Args[2]
+ if v_2.Op != OpAMD64SHRQconst {
break
}
- c := v_1.AuxInt
- v.reset(OpAMD64MULBconst)
- v.AuxInt = c
- v.AddArg(x)
- return true
- }
- // match: (MULB (MOVBconst [c]) x)
- // cond:
- // result: (MULBconst [c] x)
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVBconst {
+ if v_2.AuxInt != 16 {
break
}
- c := v_0.AuxInt
- x := v.Args[1]
- v.reset(OpAMD64MULBconst)
- v.AuxInt = c
- v.AddArg(x)
+ w := v_2.Args[0]
+ x := v.Args[3]
+ if x.Op != OpAMD64MOVWstoreidx2 {
+ break
+ }
+ if x.AuxInt != i-2 {
+ break
+ }
+ if x.Aux != s {
+ break
+ }
+ if p != x.Args[0] {
+ break
+ }
+ if idx != x.Args[1] {
+ break
+ }
+ if w != x.Args[2] {
+ break
+ }
+ mem := x.Args[3]
+ if !(x.Uses == 1 && clobber(x)) {
+ break
+ }
+ v.reset(OpAMD64MOVLstoreidx1)
+ v.AuxInt = i - 2
+ v.Aux = s
+ v.AddArg(p)
+ v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, idx.Type)
+ v0.AuxInt = 1
+ v0.AddArg(idx)
+ v.AddArg(v0)
+ v.AddArg(w)
+ v.AddArg(mem)
return true
}
- return false
-}
-func rewriteValueAMD64_OpAMD64MULBconst(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (MULBconst [c] (MOVBconst [d]))
- // cond:
- // result: (MOVBconst [int64(int8(c*d))])
+ // match: (MOVWstoreidx2 [i] {s} p idx (SHRQconst [j] w) x:(MOVWstoreidx2 [i-2] {s} p idx w0:(SHRQconst [j-16] w) mem))
+ // cond: x.Uses == 1 && clobber(x)
+ // result: (MOVLstoreidx1 [i-2] {s} p (SHLQconst <idx.Type> [1] idx) w0 mem)
for {
- c := v.AuxInt
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVBconst {
+ i := v.AuxInt
+ s := v.Aux
+ p := v.Args[0]
+ idx := v.Args[1]
+ v_2 := v.Args[2]
+ if v_2.Op != OpAMD64SHRQconst {
break
}
- d := v_0.AuxInt
- v.reset(OpAMD64MOVBconst)
- v.AuxInt = int64(int8(c * d))
+ j := v_2.AuxInt
+ w := v_2.Args[0]
+ x := v.Args[3]
+ if x.Op != OpAMD64MOVWstoreidx2 {
+ break
+ }
+ if x.AuxInt != i-2 {
+ break
+ }
+ if x.Aux != s {
+ break
+ }
+ if p != x.Args[0] {
+ break
+ }
+ if idx != x.Args[1] {
+ break
+ }
+ w0 := x.Args[2]
+ if w0.Op != OpAMD64SHRQconst {
+ break
+ }
+ if w0.AuxInt != j-16 {
+ break
+ }
+ if w != w0.Args[0] {
+ break
+ }
+ mem := x.Args[3]
+ if !(x.Uses == 1 && clobber(x)) {
+ break
+ }
+ v.reset(OpAMD64MOVLstoreidx1)
+ v.AuxInt = i - 2
+ v.Aux = s
+ v.AddArg(p)
+ v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, idx.Type)
+ v0.AuxInt = 1
+ v0.AddArg(idx)
+ v.AddArg(v0)
+ v.AddArg(w0)
+ v.AddArg(mem)
return true
}
return false
@@ -11382,60 +12006,6 @@ func rewriteValueAMD64_OpAMD64MULQconst(v *Value, config *Config) bool {
}
return false
}
-func rewriteValueAMD64_OpAMD64MULW(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (MULW x (MOVWconst [c]))
- // cond:
- // result: (MULWconst [c] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVWconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64MULWconst)
- v.AuxInt = c
- v.AddArg(x)
- return true
- }
- // match: (MULW (MOVWconst [c]) x)
- // cond:
- // result: (MULWconst [c] x)
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVWconst {
- break
- }
- c := v_0.AuxInt
- x := v.Args[1]
- v.reset(OpAMD64MULWconst)
- v.AuxInt = c
- v.AddArg(x)
- return true
- }
- return false
-}
-func rewriteValueAMD64_OpAMD64MULWconst(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (MULWconst [c] (MOVWconst [d]))
- // cond:
- // result: (MOVWconst [int64(int16(c*d))])
- for {
- c := v.AuxInt
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVWconst {
- break
- }
- d := v_0.AuxInt
- v.reset(OpAMD64MOVWconst)
- v.AuxInt = int64(int16(c * d))
- return true
- }
- return false
-}
func rewriteValueAMD64_OpMod16(v *Value, config *Config) bool {
b := v.Block
_ = b
@@ -11931,11 +12501,11 @@ func rewriteValueAMD64_OpMul16(v *Value, config *Config) bool {
_ = b
// match: (Mul16 x y)
// cond:
- // result: (MULW x y)
+ // result: (MULL x y)
for {
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64MULW)
+ v.reset(OpAMD64MULL)
v.AddArg(x)
v.AddArg(y)
return true
@@ -12011,35 +12581,17 @@ func rewriteValueAMD64_OpMul8(v *Value, config *Config) bool {
_ = b
// match: (Mul8 x y)
// cond:
- // result: (MULB x y)
+ // result: (MULL x y)
for {
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64MULB)
+ v.reset(OpAMD64MULL)
v.AddArg(x)
v.AddArg(y)
return true
}
return false
}
-func rewriteValueAMD64_OpAMD64NEGB(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (NEGB (MOVBconst [c]))
- // cond:
- // result: (MOVBconst [int64(int8(-c))])
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVBconst {
- break
- }
- c := v_0.AuxInt
- v.reset(OpAMD64MOVBconst)
- v.AuxInt = int64(int8(-c))
- return true
- }
- return false
-}
func rewriteValueAMD64_OpAMD64NEGL(v *Value, config *Config) bool {
b := v.Block
_ = b
@@ -12076,42 +12628,6 @@ func rewriteValueAMD64_OpAMD64NEGQ(v *Value, config *Config) bool {
}
return false
}
-func rewriteValueAMD64_OpAMD64NEGW(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (NEGW (MOVWconst [c]))
- // cond:
- // result: (MOVWconst [int64(int16(-c))])
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVWconst {
- break
- }
- c := v_0.AuxInt
- v.reset(OpAMD64MOVWconst)
- v.AuxInt = int64(int16(-c))
- return true
- }
- return false
-}
-func rewriteValueAMD64_OpAMD64NOTB(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (NOTB (MOVBconst [c]))
- // cond:
- // result: (MOVBconst [^c])
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVBconst {
- break
- }
- c := v_0.AuxInt
- v.reset(OpAMD64MOVBconst)
- v.AuxInt = ^c
- return true
- }
- return false
-}
func rewriteValueAMD64_OpAMD64NOTL(v *Value, config *Config) bool {
b := v.Block
_ = b
@@ -12148,33 +12664,15 @@ func rewriteValueAMD64_OpAMD64NOTQ(v *Value, config *Config) bool {
}
return false
}
-func rewriteValueAMD64_OpAMD64NOTW(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (NOTW (MOVWconst [c]))
- // cond:
- // result: (MOVWconst [^c])
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVWconst {
- break
- }
- c := v_0.AuxInt
- v.reset(OpAMD64MOVWconst)
- v.AuxInt = ^c
- return true
- }
- return false
-}
func rewriteValueAMD64_OpNeg16(v *Value, config *Config) bool {
b := v.Block
_ = b
// match: (Neg16 x)
// cond:
- // result: (NEGW x)
+ // result: (NEGL x)
for {
x := v.Args[0]
- v.reset(OpAMD64NEGW)
+ v.reset(OpAMD64NEGL)
v.AddArg(x)
return true
}
@@ -12247,10 +12745,10 @@ func rewriteValueAMD64_OpNeg8(v *Value, config *Config) bool {
_ = b
// match: (Neg8 x)
// cond:
- // result: (NEGB x)
+ // result: (NEGL x)
for {
x := v.Args[0]
- v.reset(OpAMD64NEGB)
+ v.reset(OpAMD64NEGL)
v.AddArg(x)
return true
}
@@ -12364,6 +12862,24 @@ func rewriteValueAMD64_OpNeq8(v *Value, config *Config) bool {
}
return false
}
+func rewriteValueAMD64_OpNeqB(v *Value, config *Config) bool {
+ b := v.Block
+ _ = b
+ // match: (NeqB x y)
+ // cond:
+ // result: (SETNE (CMPB x y))
+ for {
+ x := v.Args[0]
+ y := v.Args[1]
+ v.reset(OpAMD64SETNE)
+ v0 := b.NewValue0(v.Line, OpAMD64CMPB, TypeFlags)
+ v0.AddArg(x)
+ v0.AddArg(y)
+ v.AddArg(v0)
+ return true
+ }
+ return false
+}
func rewriteValueAMD64_OpNeqPtr(v *Value, config *Config) bool {
b := v.Block
_ = b
@@ -12403,50 +12919,50 @@ func rewriteValueAMD64_OpNot(v *Value, config *Config) bool {
_ = b
// match: (Not x)
// cond:
- // result: (XORBconst [1] x)
+ // result: (XORLconst [1] x)
for {
x := v.Args[0]
- v.reset(OpAMD64XORBconst)
+ v.reset(OpAMD64XORLconst)
v.AuxInt = 1
v.AddArg(x)
return true
}
return false
}
-func rewriteValueAMD64_OpAMD64ORB(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64ORL(v *Value, config *Config) bool {
b := v.Block
_ = b
- // match: (ORB x (MOVBconst [c]))
+ // match: (ORL x (MOVLconst [c]))
// cond:
- // result: (ORBconst [c] x)
+ // result: (ORLconst [c] x)
for {
x := v.Args[0]
v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVBconst {
+ if v_1.Op != OpAMD64MOVLconst {
break
}
c := v_1.AuxInt
- v.reset(OpAMD64ORBconst)
+ v.reset(OpAMD64ORLconst)
v.AuxInt = c
v.AddArg(x)
return true
}
- // match: (ORB (MOVBconst [c]) x)
+ // match: (ORL (MOVLconst [c]) x)
// cond:
- // result: (ORBconst [c] x)
+ // result: (ORLconst [c] x)
for {
v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVBconst {
+ if v_0.Op != OpAMD64MOVLconst {
break
}
c := v_0.AuxInt
x := v.Args[1]
- v.reset(OpAMD64ORBconst)
+ v.reset(OpAMD64ORLconst)
v.AuxInt = c
v.AddArg(x)
return true
}
- // match: (ORB x x)
+ // match: (ORL x x)
// cond:
// result: x
for {
@@ -12459,112 +12975,67 @@ func rewriteValueAMD64_OpAMD64ORB(v *Value, config *Config) bool {
v.AddArg(x)
return true
}
- return false
-}
-func rewriteValueAMD64_OpAMD64ORBconst(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (ORBconst [c] x)
- // cond: int8(c)==0
- // result: x
+ // match: (ORL x0:(MOVBload [i] {s} p mem) s0:(SHLLconst [8] x1:(MOVBload [i+1] {s} p mem)))
+ // cond: x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && mergePoint(b,x0,x1) != nil && clobber(x0) && clobber(x1) && clobber(s0)
+ // result: @mergePoint(b,x0,x1) (MOVWload [i] {s} p mem)
for {
- c := v.AuxInt
- x := v.Args[0]
- if !(int8(c) == 0) {
+ x0 := v.Args[0]
+ if x0.Op != OpAMD64MOVBload {
break
}
- v.reset(OpCopy)
- v.Type = x.Type
- v.AddArg(x)
- return true
- }
- // match: (ORBconst [c] _)
- // cond: int8(c)==-1
- // result: (MOVBconst [-1])
- for {
- c := v.AuxInt
- if !(int8(c) == -1) {
+ i := x0.AuxInt
+ s := x0.Aux
+ p := x0.Args[0]
+ mem := x0.Args[1]
+ s0 := v.Args[1]
+ if s0.Op != OpAMD64SHLLconst {
break
}
- v.reset(OpAMD64MOVBconst)
- v.AuxInt = -1
- return true
- }
- // match: (ORBconst [c] (MOVBconst [d]))
- // cond:
- // result: (MOVBconst [c|d])
- for {
- c := v.AuxInt
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVBconst {
+ if s0.AuxInt != 8 {
break
}
- d := v_0.AuxInt
- v.reset(OpAMD64MOVBconst)
- v.AuxInt = c | d
- return true
- }
- return false
-}
-func rewriteValueAMD64_OpAMD64ORL(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (ORL x (MOVLconst [c]))
- // cond:
- // result: (ORLconst [c] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVLconst {
+ x1 := s0.Args[0]
+ if x1.Op != OpAMD64MOVBload {
break
}
- c := v_1.AuxInt
- v.reset(OpAMD64ORLconst)
- v.AuxInt = c
- v.AddArg(x)
- return true
- }
- // match: (ORL (MOVLconst [c]) x)
- // cond:
- // result: (ORLconst [c] x)
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVLconst {
+ if x1.AuxInt != i+1 {
break
}
- c := v_0.AuxInt
- x := v.Args[1]
- v.reset(OpAMD64ORLconst)
- v.AuxInt = c
- v.AddArg(x)
- return true
- }
- // match: (ORL x x)
- // cond:
- // result: x
- for {
- x := v.Args[0]
- if x != v.Args[1] {
+ if x1.Aux != s {
+ break
+ }
+ if p != x1.Args[0] {
+ break
+ }
+ if mem != x1.Args[1] {
break
}
+ if !(x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0) && clobber(x1) && clobber(s0)) {
+ break
+ }
+ b = mergePoint(b, x0, x1)
+ v0 := b.NewValue0(v.Line, OpAMD64MOVWload, config.fe.TypeUInt16())
v.reset(OpCopy)
- v.Type = x.Type
- v.AddArg(x)
+ v.AddArg(v0)
+ v0.AuxInt = i
+ v0.Aux = s
+ v0.AddArg(p)
+ v0.AddArg(mem)
return true
}
- // match: (ORL (ORL (ORL x0:(MOVBload [i] {s} p mem) (SHLLconst [8] x1:(MOVBload [i+1] {s} p mem))) (SHLLconst [16] x2:(MOVBload [i+2] {s} p mem))) (SHLLconst [24] x3:(MOVBload [i+3] {s} p mem)))
- // cond: mergePoint(b,x0,x1,x2,x3) != nil
+ // match: (ORL o0:(ORL o1:(ORL x0:(MOVBload [i] {s} p mem) s0:(SHLLconst [8] x1:(MOVBload [i+1] {s} p mem))) s1:(SHLLconst [16] x2:(MOVBload [i+2] {s} p mem))) s2:(SHLLconst [24] x3:(MOVBload [i+3] {s} p mem)))
+ // cond: x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && s2.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && mergePoint(b,x0,x1,x2,x3) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(s0) && clobber(s1) && clobber(s2) && clobber(o0) && clobber(o1)
// result: @mergePoint(b,x0,x1,x2,x3) (MOVLload [i] {s} p mem)
for {
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64ORL {
+ o0 := v.Args[0]
+ if o0.Op != OpAMD64ORL {
break
}
- v_0_0 := v_0.Args[0]
- if v_0_0.Op != OpAMD64ORL {
+ o1 := o0.Args[0]
+ if o1.Op != OpAMD64ORL {
break
}
- x0 := v_0_0.Args[0]
+ x0 := o1.Args[0]
if x0.Op != OpAMD64MOVBload {
break
}
@@ -12572,14 +13043,14 @@ func rewriteValueAMD64_OpAMD64ORL(v *Value, config *Config) bool {
s := x0.Aux
p := x0.Args[0]
mem := x0.Args[1]
- v_0_0_1 := v_0_0.Args[1]
- if v_0_0_1.Op != OpAMD64SHLLconst {
+ s0 := o1.Args[1]
+ if s0.Op != OpAMD64SHLLconst {
break
}
- if v_0_0_1.AuxInt != 8 {
+ if s0.AuxInt != 8 {
break
}
- x1 := v_0_0_1.Args[0]
+ x1 := s0.Args[0]
if x1.Op != OpAMD64MOVBload {
break
}
@@ -12595,14 +13066,14 @@ func rewriteValueAMD64_OpAMD64ORL(v *Value, config *Config) bool {
if mem != x1.Args[1] {
break
}
- v_0_1 := v_0.Args[1]
- if v_0_1.Op != OpAMD64SHLLconst {
+ s1 := o0.Args[1]
+ if s1.Op != OpAMD64SHLLconst {
break
}
- if v_0_1.AuxInt != 16 {
+ if s1.AuxInt != 16 {
break
}
- x2 := v_0_1.Args[0]
+ x2 := s1.Args[0]
if x2.Op != OpAMD64MOVBload {
break
}
@@ -12618,14 +13089,14 @@ func rewriteValueAMD64_OpAMD64ORL(v *Value, config *Config) bool {
if mem != x2.Args[1] {
break
}
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64SHLLconst {
+ s2 := v.Args[1]
+ if s2.Op != OpAMD64SHLLconst {
break
}
- if v_1.AuxInt != 24 {
+ if s2.AuxInt != 24 {
break
}
- x3 := v_1.Args[0]
+ x3 := s2.Args[0]
if x3.Op != OpAMD64MOVBload {
break
}
@@ -12641,7 +13112,7 @@ func rewriteValueAMD64_OpAMD64ORL(v *Value, config *Config) bool {
if mem != x3.Args[1] {
break
}
- if !(mergePoint(b, x0, x1, x2, x3) != nil) {
+ if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && s2.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && mergePoint(b, x0, x1, x2, x3) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(s0) && clobber(s1) && clobber(s2) && clobber(o0) && clobber(o1)) {
break
}
b = mergePoint(b, x0, x1, x2, x3)
@@ -12654,19 +13125,72 @@ func rewriteValueAMD64_OpAMD64ORL(v *Value, config *Config) bool {
v0.AddArg(mem)
return true
}
- // match: (ORL (ORL (ORL x0:(MOVBloadidx1 [i] {s} p idx mem) (SHLLconst [8] x1:(MOVBloadidx1 [i+1] {s} p idx mem))) (SHLLconst [16] x2:(MOVBloadidx1 [i+2] {s} p idx mem))) (SHLLconst [24] x3:(MOVBloadidx1 [i+3] {s} p idx mem)))
- // cond: mergePoint(b,x0,x1,x2,x3) != nil
+ // match: (ORL x0:(MOVBloadidx1 [i] {s} p idx mem) s0:(SHLLconst [8] x1:(MOVBloadidx1 [i+1] {s} p idx mem)))
+ // cond: x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && mergePoint(b,x0,x1) != nil && clobber(x0) && clobber(x1) && clobber(s0)
+ // result: @mergePoint(b,x0,x1) (MOVWloadidx1 <v.Type> [i] {s} p idx mem)
+ for {
+ x0 := v.Args[0]
+ if x0.Op != OpAMD64MOVBloadidx1 {
+ break
+ }
+ i := x0.AuxInt
+ s := x0.Aux
+ p := x0.Args[0]
+ idx := x0.Args[1]
+ mem := x0.Args[2]
+ s0 := v.Args[1]
+ if s0.Op != OpAMD64SHLLconst {
+ break
+ }
+ if s0.AuxInt != 8 {
+ break
+ }
+ x1 := s0.Args[0]
+ if x1.Op != OpAMD64MOVBloadidx1 {
+ break
+ }
+ if x1.AuxInt != i+1 {
+ break
+ }
+ if x1.Aux != s {
+ break
+ }
+ if p != x1.Args[0] {
+ break
+ }
+ if idx != x1.Args[1] {
+ break
+ }
+ if mem != x1.Args[2] {
+ break
+ }
+ if !(x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0) && clobber(x1) && clobber(s0)) {
+ break
+ }
+ b = mergePoint(b, x0, x1)
+ v0 := b.NewValue0(v.Line, OpAMD64MOVWloadidx1, v.Type)
+ v.reset(OpCopy)
+ v.AddArg(v0)
+ v0.AuxInt = i
+ v0.Aux = s
+ v0.AddArg(p)
+ v0.AddArg(idx)
+ v0.AddArg(mem)
+ return true
+ }
+ // match: (ORL o0:(ORL o1:(ORL x0:(MOVBloadidx1 [i] {s} p idx mem) s0:(SHLLconst [8] x1:(MOVBloadidx1 [i+1] {s} p idx mem))) s1:(SHLLconst [16] x2:(MOVBloadidx1 [i+2] {s} p idx mem))) s2:(SHLLconst [24] x3:(MOVBloadidx1 [i+3] {s} p idx mem)))
+ // cond: x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && s2.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && mergePoint(b,x0,x1,x2,x3) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(s0) && clobber(s1) && clobber(s2) && clobber(o0) && clobber(o1)
// result: @mergePoint(b,x0,x1,x2,x3) (MOVLloadidx1 <v.Type> [i] {s} p idx mem)
for {
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64ORL {
+ o0 := v.Args[0]
+ if o0.Op != OpAMD64ORL {
break
}
- v_0_0 := v_0.Args[0]
- if v_0_0.Op != OpAMD64ORL {
+ o1 := o0.Args[0]
+ if o1.Op != OpAMD64ORL {
break
}
- x0 := v_0_0.Args[0]
+ x0 := o1.Args[0]
if x0.Op != OpAMD64MOVBloadidx1 {
break
}
@@ -12675,14 +13199,14 @@ func rewriteValueAMD64_OpAMD64ORL(v *Value, config *Config) bool {
p := x0.Args[0]
idx := x0.Args[1]
mem := x0.Args[2]
- v_0_0_1 := v_0_0.Args[1]
- if v_0_0_1.Op != OpAMD64SHLLconst {
+ s0 := o1.Args[1]
+ if s0.Op != OpAMD64SHLLconst {
break
}
- if v_0_0_1.AuxInt != 8 {
+ if s0.AuxInt != 8 {
break
}
- x1 := v_0_0_1.Args[0]
+ x1 := s0.Args[0]
if x1.Op != OpAMD64MOVBloadidx1 {
break
}
@@ -12701,14 +13225,14 @@ func rewriteValueAMD64_OpAMD64ORL(v *Value, config *Config) bool {
if mem != x1.Args[2] {
break
}
- v_0_1 := v_0.Args[1]
- if v_0_1.Op != OpAMD64SHLLconst {
+ s1 := o0.Args[1]
+ if s1.Op != OpAMD64SHLLconst {
break
}
- if v_0_1.AuxInt != 16 {
+ if s1.AuxInt != 16 {
break
}
- x2 := v_0_1.Args[0]
+ x2 := s1.Args[0]
if x2.Op != OpAMD64MOVBloadidx1 {
break
}
@@ -12727,14 +13251,14 @@ func rewriteValueAMD64_OpAMD64ORL(v *Value, config *Config) bool {
if mem != x2.Args[2] {
break
}
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64SHLLconst {
+ s2 := v.Args[1]
+ if s2.Op != OpAMD64SHLLconst {
break
}
- if v_1.AuxInt != 24 {
+ if s2.AuxInt != 24 {
break
}
- x3 := v_1.Args[0]
+ x3 := s2.Args[0]
if x3.Op != OpAMD64MOVBloadidx1 {
break
}
@@ -12753,7 +13277,7 @@ func rewriteValueAMD64_OpAMD64ORL(v *Value, config *Config) bool {
if mem != x3.Args[2] {
break
}
- if !(mergePoint(b, x0, x1, x2, x3) != nil) {
+ if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && s2.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && mergePoint(b, x0, x1, x2, x3) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(s0) && clobber(s1) && clobber(s2) && clobber(o0) && clobber(o1)) {
break
}
b = mergePoint(b, x0, x1, x2, x3)
@@ -12866,35 +13390,35 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
v.AddArg(x)
return true
}
- // match: (ORQ (ORQ (ORQ (ORQ (ORQ (ORQ (ORQ x0:(MOVBload [i] {s} p mem) (SHLQconst [8] x1:(MOVBload [i+1] {s} p mem))) (SHLQconst [16] x2:(MOVBload [i+2] {s} p mem))) (SHLQconst [24] x3:(MOVBload [i+3] {s} p mem))) (SHLQconst [32] x4:(MOVBload [i+4] {s} p mem))) (SHLQconst [40] x5:(MOVBload [i+5] {s} p mem))) (SHLQconst [48] x6:(MOVBload [i+6] {s} p mem))) (SHLQconst [56] x7:(MOVBload [i+7] {s} p mem)))
- // cond: mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil
+ // match: (ORQ o0:(ORQ o1:(ORQ o2:(ORQ o3:(ORQ o4:(ORQ o5:(ORQ x0:(MOVBload [i] {s} p mem) s0:(SHLQconst [8] x1:(MOVBload [i+1] {s} p mem))) s1:(SHLQconst [16] x2:(MOVBload [i+2] {s} p mem))) s2:(SHLQconst [24] x3:(MOVBload [i+3] {s} p mem))) s3:(SHLQconst [32] x4:(MOVBload [i+4] {s} p mem))) s4:(SHLQconst [40] x5:(MOVBload [i+5] {s} p mem))) s5:(SHLQconst [48] x6:(MOVBload [i+6] {s} p mem))) s6:(SHLQconst [56] x7:(MOVBload [i+7] {s} p mem)))
+ // cond: x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1 && x5.Uses == 1 && x6.Uses == 1 && x7.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && s4.Uses == 1 && s5.Uses == 1 && s6.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && o2.Uses == 1 && o3.Uses == 1 && o4.Uses == 1 && o5.Uses == 1 && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(x4) && clobber(x5) && clobber(x6) && clobber(x7) && clobber(s0) && clobber(s1) && clobber(s2) && clobber(s3) && clobber(s4) && clobber(s5) && clobber(s6) && clobber(o0) && clobber(o1) && clobber(o2) && clobber(o3) && clobber(o4) && clobber(o5)
// result: @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (MOVQload [i] {s} p mem)
for {
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64ORQ {
+ o0 := v.Args[0]
+ if o0.Op != OpAMD64ORQ {
break
}
- v_0_0 := v_0.Args[0]
- if v_0_0.Op != OpAMD64ORQ {
+ o1 := o0.Args[0]
+ if o1.Op != OpAMD64ORQ {
break
}
- v_0_0_0 := v_0_0.Args[0]
- if v_0_0_0.Op != OpAMD64ORQ {
+ o2 := o1.Args[0]
+ if o2.Op != OpAMD64ORQ {
break
}
- v_0_0_0_0 := v_0_0_0.Args[0]
- if v_0_0_0_0.Op != OpAMD64ORQ {
+ o3 := o2.Args[0]
+ if o3.Op != OpAMD64ORQ {
break
}
- v_0_0_0_0_0 := v_0_0_0_0.Args[0]
- if v_0_0_0_0_0.Op != OpAMD64ORQ {
+ o4 := o3.Args[0]
+ if o4.Op != OpAMD64ORQ {
break
}
- v_0_0_0_0_0_0 := v_0_0_0_0_0.Args[0]
- if v_0_0_0_0_0_0.Op != OpAMD64ORQ {
+ o5 := o4.Args[0]
+ if o5.Op != OpAMD64ORQ {
break
}
- x0 := v_0_0_0_0_0_0.Args[0]
+ x0 := o5.Args[0]
if x0.Op != OpAMD64MOVBload {
break
}
@@ -12902,14 +13426,14 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
s := x0.Aux
p := x0.Args[0]
mem := x0.Args[1]
- v_0_0_0_0_0_0_1 := v_0_0_0_0_0_0.Args[1]
- if v_0_0_0_0_0_0_1.Op != OpAMD64SHLQconst {
+ s0 := o5.Args[1]
+ if s0.Op != OpAMD64SHLQconst {
break
}
- if v_0_0_0_0_0_0_1.AuxInt != 8 {
+ if s0.AuxInt != 8 {
break
}
- x1 := v_0_0_0_0_0_0_1.Args[0]
+ x1 := s0.Args[0]
if x1.Op != OpAMD64MOVBload {
break
}
@@ -12925,14 +13449,14 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
if mem != x1.Args[1] {
break
}
- v_0_0_0_0_0_1 := v_0_0_0_0_0.Args[1]
- if v_0_0_0_0_0_1.Op != OpAMD64SHLQconst {
+ s1 := o4.Args[1]
+ if s1.Op != OpAMD64SHLQconst {
break
}
- if v_0_0_0_0_0_1.AuxInt != 16 {
+ if s1.AuxInt != 16 {
break
}
- x2 := v_0_0_0_0_0_1.Args[0]
+ x2 := s1.Args[0]
if x2.Op != OpAMD64MOVBload {
break
}
@@ -12948,14 +13472,14 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
if mem != x2.Args[1] {
break
}
- v_0_0_0_0_1 := v_0_0_0_0.Args[1]
- if v_0_0_0_0_1.Op != OpAMD64SHLQconst {
+ s2 := o3.Args[1]
+ if s2.Op != OpAMD64SHLQconst {
break
}
- if v_0_0_0_0_1.AuxInt != 24 {
+ if s2.AuxInt != 24 {
break
}
- x3 := v_0_0_0_0_1.Args[0]
+ x3 := s2.Args[0]
if x3.Op != OpAMD64MOVBload {
break
}
@@ -12971,14 +13495,14 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
if mem != x3.Args[1] {
break
}
- v_0_0_0_1 := v_0_0_0.Args[1]
- if v_0_0_0_1.Op != OpAMD64SHLQconst {
+ s3 := o2.Args[1]
+ if s3.Op != OpAMD64SHLQconst {
break
}
- if v_0_0_0_1.AuxInt != 32 {
+ if s3.AuxInt != 32 {
break
}
- x4 := v_0_0_0_1.Args[0]
+ x4 := s3.Args[0]
if x4.Op != OpAMD64MOVBload {
break
}
@@ -12994,14 +13518,14 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
if mem != x4.Args[1] {
break
}
- v_0_0_1 := v_0_0.Args[1]
- if v_0_0_1.Op != OpAMD64SHLQconst {
+ s4 := o1.Args[1]
+ if s4.Op != OpAMD64SHLQconst {
break
}
- if v_0_0_1.AuxInt != 40 {
+ if s4.AuxInt != 40 {
break
}
- x5 := v_0_0_1.Args[0]
+ x5 := s4.Args[0]
if x5.Op != OpAMD64MOVBload {
break
}
@@ -13017,14 +13541,14 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
if mem != x5.Args[1] {
break
}
- v_0_1 := v_0.Args[1]
- if v_0_1.Op != OpAMD64SHLQconst {
+ s5 := o0.Args[1]
+ if s5.Op != OpAMD64SHLQconst {
break
}
- if v_0_1.AuxInt != 48 {
+ if s5.AuxInt != 48 {
break
}
- x6 := v_0_1.Args[0]
+ x6 := s5.Args[0]
if x6.Op != OpAMD64MOVBload {
break
}
@@ -13040,14 +13564,14 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
if mem != x6.Args[1] {
break
}
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64SHLQconst {
+ s6 := v.Args[1]
+ if s6.Op != OpAMD64SHLQconst {
break
}
- if v_1.AuxInt != 56 {
+ if s6.AuxInt != 56 {
break
}
- x7 := v_1.Args[0]
+ x7 := s6.Args[0]
if x7.Op != OpAMD64MOVBload {
break
}
@@ -13063,7 +13587,7 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
if mem != x7.Args[1] {
break
}
- if !(mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7) != nil) {
+ if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1 && x5.Uses == 1 && x6.Uses == 1 && x7.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && s4.Uses == 1 && s5.Uses == 1 && s6.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && o2.Uses == 1 && o3.Uses == 1 && o4.Uses == 1 && o5.Uses == 1 && mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(x4) && clobber(x5) && clobber(x6) && clobber(x7) && clobber(s0) && clobber(s1) && clobber(s2) && clobber(s3) && clobber(s4) && clobber(s5) && clobber(s6) && clobber(o0) && clobber(o1) && clobber(o2) && clobber(o3) && clobber(o4) && clobber(o5)) {
break
}
b = mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7)
@@ -13076,35 +13600,35 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
v0.AddArg(mem)
return true
}
- // match: (ORQ (ORQ (ORQ (ORQ (ORQ (ORQ (ORQ x0:(MOVBloadidx1 [i] {s} p idx mem) (SHLQconst [8] x1:(MOVBloadidx1 [i+1] {s} p idx mem))) (SHLQconst [16] x2:(MOVBloadidx1 [i+2] {s} p idx mem))) (SHLQconst [24] x3:(MOVBloadidx1 [i+3] {s} p idx mem))) (SHLQconst [32] x4:(MOVBloadidx1 [i+4] {s} p idx mem))) (SHLQconst [40] x5:(MOVBloadidx1 [i+5] {s} p idx mem))) (SHLQconst [48] x6:(MOVBloadidx1 [i+6] {s} p idx mem))) (SHLQconst [56] x7:(MOVBloadidx1 [i+7] {s} p idx mem)))
- // cond: mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil
+ // match: (ORQ o0:(ORQ o1:(ORQ o2:(ORQ o3:(ORQ o4:(ORQ o5:(ORQ x0:(MOVBloadidx1 [i] {s} p idx mem) s0:(SHLQconst [8] x1:(MOVBloadidx1 [i+1] {s} p idx mem))) s1:(SHLQconst [16] x2:(MOVBloadidx1 [i+2] {s} p idx mem))) s2:(SHLQconst [24] x3:(MOVBloadidx1 [i+3] {s} p idx mem))) s3:(SHLQconst [32] x4:(MOVBloadidx1 [i+4] {s} p idx mem))) s4:(SHLQconst [40] x5:(MOVBloadidx1 [i+5] {s} p idx mem))) s5:(SHLQconst [48] x6:(MOVBloadidx1 [i+6] {s} p idx mem))) s6:(SHLQconst [56] x7:(MOVBloadidx1 [i+7] {s} p idx mem)))
+ // cond: x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1 && x5.Uses == 1 && x6.Uses == 1 && x7.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && s4.Uses == 1 && s5.Uses == 1 && s6.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && o2.Uses == 1 && o3.Uses == 1 && o4.Uses == 1 && o5.Uses == 1 && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(x4) && clobber(x5) && clobber(x6) && clobber(x7) && clobber(s0) && clobber(s1) && clobber(s2) && clobber(s3) && clobber(s4) && clobber(s5) && clobber(s6) && clobber(o0) && clobber(o1) && clobber(o2) && clobber(o3) && clobber(o4) && clobber(o5)
// result: @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (MOVQloadidx1 <v.Type> [i] {s} p idx mem)
for {
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64ORQ {
+ o0 := v.Args[0]
+ if o0.Op != OpAMD64ORQ {
break
}
- v_0_0 := v_0.Args[0]
- if v_0_0.Op != OpAMD64ORQ {
+ o1 := o0.Args[0]
+ if o1.Op != OpAMD64ORQ {
break
}
- v_0_0_0 := v_0_0.Args[0]
- if v_0_0_0.Op != OpAMD64ORQ {
+ o2 := o1.Args[0]
+ if o2.Op != OpAMD64ORQ {
break
}
- v_0_0_0_0 := v_0_0_0.Args[0]
- if v_0_0_0_0.Op != OpAMD64ORQ {
+ o3 := o2.Args[0]
+ if o3.Op != OpAMD64ORQ {
break
}
- v_0_0_0_0_0 := v_0_0_0_0.Args[0]
- if v_0_0_0_0_0.Op != OpAMD64ORQ {
+ o4 := o3.Args[0]
+ if o4.Op != OpAMD64ORQ {
break
}
- v_0_0_0_0_0_0 := v_0_0_0_0_0.Args[0]
- if v_0_0_0_0_0_0.Op != OpAMD64ORQ {
+ o5 := o4.Args[0]
+ if o5.Op != OpAMD64ORQ {
break
}
- x0 := v_0_0_0_0_0_0.Args[0]
+ x0 := o5.Args[0]
if x0.Op != OpAMD64MOVBloadidx1 {
break
}
@@ -13113,14 +13637,14 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
p := x0.Args[0]
idx := x0.Args[1]
mem := x0.Args[2]
- v_0_0_0_0_0_0_1 := v_0_0_0_0_0_0.Args[1]
- if v_0_0_0_0_0_0_1.Op != OpAMD64SHLQconst {
+ s0 := o5.Args[1]
+ if s0.Op != OpAMD64SHLQconst {
break
}
- if v_0_0_0_0_0_0_1.AuxInt != 8 {
+ if s0.AuxInt != 8 {
break
}
- x1 := v_0_0_0_0_0_0_1.Args[0]
+ x1 := s0.Args[0]
if x1.Op != OpAMD64MOVBloadidx1 {
break
}
@@ -13139,14 +13663,14 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
if mem != x1.Args[2] {
break
}
- v_0_0_0_0_0_1 := v_0_0_0_0_0.Args[1]
- if v_0_0_0_0_0_1.Op != OpAMD64SHLQconst {
+ s1 := o4.Args[1]
+ if s1.Op != OpAMD64SHLQconst {
break
}
- if v_0_0_0_0_0_1.AuxInt != 16 {
+ if s1.AuxInt != 16 {
break
}
- x2 := v_0_0_0_0_0_1.Args[0]
+ x2 := s1.Args[0]
if x2.Op != OpAMD64MOVBloadidx1 {
break
}
@@ -13165,14 +13689,14 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
if mem != x2.Args[2] {
break
}
- v_0_0_0_0_1 := v_0_0_0_0.Args[1]
- if v_0_0_0_0_1.Op != OpAMD64SHLQconst {
+ s2 := o3.Args[1]
+ if s2.Op != OpAMD64SHLQconst {
break
}
- if v_0_0_0_0_1.AuxInt != 24 {
+ if s2.AuxInt != 24 {
break
}
- x3 := v_0_0_0_0_1.Args[0]
+ x3 := s2.Args[0]
if x3.Op != OpAMD64MOVBloadidx1 {
break
}
@@ -13191,14 +13715,14 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
if mem != x3.Args[2] {
break
}
- v_0_0_0_1 := v_0_0_0.Args[1]
- if v_0_0_0_1.Op != OpAMD64SHLQconst {
+ s3 := o2.Args[1]
+ if s3.Op != OpAMD64SHLQconst {
break
}
- if v_0_0_0_1.AuxInt != 32 {
+ if s3.AuxInt != 32 {
break
}
- x4 := v_0_0_0_1.Args[0]
+ x4 := s3.Args[0]
if x4.Op != OpAMD64MOVBloadidx1 {
break
}
@@ -13217,14 +13741,14 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
if mem != x4.Args[2] {
break
}
- v_0_0_1 := v_0_0.Args[1]
- if v_0_0_1.Op != OpAMD64SHLQconst {
+ s4 := o1.Args[1]
+ if s4.Op != OpAMD64SHLQconst {
break
}
- if v_0_0_1.AuxInt != 40 {
+ if s4.AuxInt != 40 {
break
}
- x5 := v_0_0_1.Args[0]
+ x5 := s4.Args[0]
if x5.Op != OpAMD64MOVBloadidx1 {
break
}
@@ -13243,14 +13767,14 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
if mem != x5.Args[2] {
break
}
- v_0_1 := v_0.Args[1]
- if v_0_1.Op != OpAMD64SHLQconst {
+ s5 := o0.Args[1]
+ if s5.Op != OpAMD64SHLQconst {
break
}
- if v_0_1.AuxInt != 48 {
+ if s5.AuxInt != 48 {
break
}
- x6 := v_0_1.Args[0]
+ x6 := s5.Args[0]
if x6.Op != OpAMD64MOVBloadidx1 {
break
}
@@ -13269,14 +13793,14 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
if mem != x6.Args[2] {
break
}
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64SHLQconst {
+ s6 := v.Args[1]
+ if s6.Op != OpAMD64SHLQconst {
break
}
- if v_1.AuxInt != 56 {
+ if s6.AuxInt != 56 {
break
}
- x7 := v_1.Args[0]
+ x7 := s6.Args[0]
if x7.Op != OpAMD64MOVBloadidx1 {
break
}
@@ -13295,7 +13819,7 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
if mem != x7.Args[2] {
break
}
- if !(mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7) != nil) {
+ if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1 && x5.Uses == 1 && x6.Uses == 1 && x7.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && s4.Uses == 1 && s5.Uses == 1 && s6.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && o2.Uses == 1 && o3.Uses == 1 && o4.Uses == 1 && o5.Uses == 1 && mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(x4) && clobber(x5) && clobber(x6) && clobber(x7) && clobber(s0) && clobber(s1) && clobber(s2) && clobber(s3) && clobber(s4) && clobber(s5) && clobber(s6) && clobber(o0) && clobber(o1) && clobber(o2) && clobber(o3) && clobber(o4) && clobber(o5)) {
break
}
b = mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7)
@@ -13354,200 +13878,6 @@ func rewriteValueAMD64_OpAMD64ORQconst(v *Value, config *Config) bool {
}
return false
}
-func rewriteValueAMD64_OpAMD64ORW(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (ORW x (MOVWconst [c]))
- // cond:
- // result: (ORWconst [c] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVWconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64ORWconst)
- v.AuxInt = c
- v.AddArg(x)
- return true
- }
- // match: (ORW (MOVWconst [c]) x)
- // cond:
- // result: (ORWconst [c] x)
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVWconst {
- break
- }
- c := v_0.AuxInt
- x := v.Args[1]
- v.reset(OpAMD64ORWconst)
- v.AuxInt = c
- v.AddArg(x)
- return true
- }
- // match: (ORW x x)
- // cond:
- // result: x
- for {
- x := v.Args[0]
- if x != v.Args[1] {
- break
- }
- v.reset(OpCopy)
- v.Type = x.Type
- v.AddArg(x)
- return true
- }
- // match: (ORW x0:(MOVBload [i] {s} p mem) (SHLWconst [8] x1:(MOVBload [i+1] {s} p mem)))
- // cond: mergePoint(b,x0,x1) != nil
- // result: @mergePoint(b,x0,x1) (MOVWload [i] {s} p mem)
- for {
- x0 := v.Args[0]
- if x0.Op != OpAMD64MOVBload {
- break
- }
- i := x0.AuxInt
- s := x0.Aux
- p := x0.Args[0]
- mem := x0.Args[1]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64SHLWconst {
- break
- }
- if v_1.AuxInt != 8 {
- break
- }
- x1 := v_1.Args[0]
- if x1.Op != OpAMD64MOVBload {
- break
- }
- if x1.AuxInt != i+1 {
- break
- }
- if x1.Aux != s {
- break
- }
- if p != x1.Args[0] {
- break
- }
- if mem != x1.Args[1] {
- break
- }
- if !(mergePoint(b, x0, x1) != nil) {
- break
- }
- b = mergePoint(b, x0, x1)
- v0 := b.NewValue0(v.Line, OpAMD64MOVWload, config.fe.TypeUInt16())
- v.reset(OpCopy)
- v.AddArg(v0)
- v0.AuxInt = i
- v0.Aux = s
- v0.AddArg(p)
- v0.AddArg(mem)
- return true
- }
- // match: (ORW x0:(MOVBloadidx1 [i] {s} p idx mem) (SHLWconst [8] x1:(MOVBloadidx1 [i+1] {s} p idx mem)))
- // cond: mergePoint(b,x0,x1) != nil
- // result: @mergePoint(b,x0,x1) (MOVWloadidx1 <v.Type> [i] {s} p idx mem)
- for {
- x0 := v.Args[0]
- if x0.Op != OpAMD64MOVBloadidx1 {
- break
- }
- i := x0.AuxInt
- s := x0.Aux
- p := x0.Args[0]
- idx := x0.Args[1]
- mem := x0.Args[2]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64SHLWconst {
- break
- }
- if v_1.AuxInt != 8 {
- break
- }
- x1 := v_1.Args[0]
- if x1.Op != OpAMD64MOVBloadidx1 {
- break
- }
- if x1.AuxInt != i+1 {
- break
- }
- if x1.Aux != s {
- break
- }
- if p != x1.Args[0] {
- break
- }
- if idx != x1.Args[1] {
- break
- }
- if mem != x1.Args[2] {
- break
- }
- if !(mergePoint(b, x0, x1) != nil) {
- break
- }
- b = mergePoint(b, x0, x1)
- v0 := b.NewValue0(v.Line, OpAMD64MOVWloadidx1, v.Type)
- v.reset(OpCopy)
- v.AddArg(v0)
- v0.AuxInt = i
- v0.Aux = s
- v0.AddArg(p)
- v0.AddArg(idx)
- v0.AddArg(mem)
- return true
- }
- return false
-}
-func rewriteValueAMD64_OpAMD64ORWconst(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (ORWconst [c] x)
- // cond: int16(c)==0
- // result: x
- for {
- c := v.AuxInt
- x := v.Args[0]
- if !(int16(c) == 0) {
- break
- }
- v.reset(OpCopy)
- v.Type = x.Type
- v.AddArg(x)
- return true
- }
- // match: (ORWconst [c] _)
- // cond: int16(c)==-1
- // result: (MOVWconst [-1])
- for {
- c := v.AuxInt
- if !(int16(c) == -1) {
- break
- }
- v.reset(OpAMD64MOVWconst)
- v.AuxInt = -1
- return true
- }
- // match: (ORWconst [c] (MOVWconst [d]))
- // cond:
- // result: (MOVWconst [c|d])
- for {
- c := v.AuxInt
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVWconst {
- break
- }
- d := v_0.AuxInt
- v.reset(OpAMD64MOVWconst)
- v.AuxInt = c | d
- return true
- }
- return false
-}
func rewriteValueAMD64_OpOffPtr(v *Value, config *Config) bool {
b := v.Block
_ = b
@@ -13585,11 +13915,11 @@ func rewriteValueAMD64_OpOr16(v *Value, config *Config) bool {
_ = b
// match: (Or16 x y)
// cond:
- // result: (ORW x y)
+ // result: (ORL x y)
for {
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64ORW)
+ v.reset(OpAMD64ORL)
v.AddArg(x)
v.AddArg(y)
return true
@@ -13633,11 +13963,27 @@ func rewriteValueAMD64_OpOr8(v *Value, config *Config) bool {
_ = b
// match: (Or8 x y)
// cond:
- // result: (ORB x y)
+ // result: (ORL x y)
for {
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64ORB)
+ v.reset(OpAMD64ORL)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ return false
+}
+func rewriteValueAMD64_OpOrB(v *Value, config *Config) bool {
+ b := v.Block
+ _ = b
+ // match: (OrB x y)
+ // cond:
+ // result: (ORL x y)
+ for {
+ x := v.Args[0]
+ y := v.Args[1]
+ v.reset(OpAMD64ORL)
v.AddArg(x)
v.AddArg(y)
return true
@@ -13649,12 +13995,12 @@ func rewriteValueAMD64_OpRsh16Ux16(v *Value, config *Config) bool {
_ = b
// match: (Rsh16Ux16 <t> x y)
// cond:
- // result: (ANDW (SHRW <t> x y) (SBBLcarrymask <t> (CMPWconst y [16])))
+ // result: (ANDL (SHRW <t> x y) (SBBLcarrymask <t> (CMPWconst y [16])))
for {
t := v.Type
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64ANDW)
+ v.reset(OpAMD64ANDL)
v0 := b.NewValue0(v.Line, OpAMD64SHRW, t)
v0.AddArg(x)
v0.AddArg(y)
@@ -13674,12 +14020,12 @@ func rewriteValueAMD64_OpRsh16Ux32(v *Value, config *Config) bool {
_ = b
// match: (Rsh16Ux32 <t> x y)
// cond:
- // result: (ANDW (SHRW <t> x y) (SBBLcarrymask <t> (CMPLconst y [16])))
+ // result: (ANDL (SHRW <t> x y) (SBBLcarrymask <t> (CMPLconst y [16])))
for {
t := v.Type
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64ANDW)
+ v.reset(OpAMD64ANDL)
v0 := b.NewValue0(v.Line, OpAMD64SHRW, t)
v0.AddArg(x)
v0.AddArg(y)
@@ -13699,12 +14045,12 @@ func rewriteValueAMD64_OpRsh16Ux64(v *Value, config *Config) bool {
_ = b
// match: (Rsh16Ux64 <t> x y)
// cond:
- // result: (ANDW (SHRW <t> x y) (SBBLcarrymask <t> (CMPQconst y [16])))
+ // result: (ANDL (SHRW <t> x y) (SBBLcarrymask <t> (CMPQconst y [16])))
for {
t := v.Type
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64ANDW)
+ v.reset(OpAMD64ANDL)
v0 := b.NewValue0(v.Line, OpAMD64SHRW, t)
v0.AddArg(x)
v0.AddArg(y)
@@ -13724,12 +14070,12 @@ func rewriteValueAMD64_OpRsh16Ux8(v *Value, config *Config) bool {
_ = b
// match: (Rsh16Ux8 <t> x y)
// cond:
- // result: (ANDW (SHRW <t> x y) (SBBLcarrymask <t> (CMPBconst y [16])))
+ // result: (ANDL (SHRW <t> x y) (SBBLcarrymask <t> (CMPBconst y [16])))
for {
t := v.Type
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64ANDW)
+ v.reset(OpAMD64ANDL)
v0 := b.NewValue0(v.Line, OpAMD64SHRW, t)
v0.AddArg(x)
v0.AddArg(y)
@@ -13749,7 +14095,7 @@ func rewriteValueAMD64_OpRsh16x16(v *Value, config *Config) bool {
_ = b
// match: (Rsh16x16 <t> x y)
// cond:
- // result: (SARW <t> x (ORW <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [16])))))
+ // result: (SARW <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [16])))))
for {
t := v.Type
x := v.Args[0]
@@ -13757,7 +14103,7 @@ func rewriteValueAMD64_OpRsh16x16(v *Value, config *Config) bool {
v.reset(OpAMD64SARW)
v.Type = t
v.AddArg(x)
- v0 := b.NewValue0(v.Line, OpAMD64ORW, y.Type)
+ v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
v0.AddArg(y)
v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
@@ -13833,7 +14179,7 @@ func rewriteValueAMD64_OpRsh16x8(v *Value, config *Config) bool {
_ = b
// match: (Rsh16x8 <t> x y)
// cond:
- // result: (SARW <t> x (ORB <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [16])))))
+ // result: (SARW <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [16])))))
for {
t := v.Type
x := v.Args[0]
@@ -13841,7 +14187,7 @@ func rewriteValueAMD64_OpRsh16x8(v *Value, config *Config) bool {
v.reset(OpAMD64SARW)
v.Type = t
v.AddArg(x)
- v0 := b.NewValue0(v.Line, OpAMD64ORB, y.Type)
+ v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
v0.AddArg(y)
v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
@@ -13961,7 +14307,7 @@ func rewriteValueAMD64_OpRsh32x16(v *Value, config *Config) bool {
_ = b
// match: (Rsh32x16 <t> x y)
// cond:
- // result: (SARL <t> x (ORW <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [32])))))
+ // result: (SARL <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [32])))))
for {
t := v.Type
x := v.Args[0]
@@ -13969,7 +14315,7 @@ func rewriteValueAMD64_OpRsh32x16(v *Value, config *Config) bool {
v.reset(OpAMD64SARL)
v.Type = t
v.AddArg(x)
- v0 := b.NewValue0(v.Line, OpAMD64ORW, y.Type)
+ v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
v0.AddArg(y)
v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
@@ -14045,7 +14391,7 @@ func rewriteValueAMD64_OpRsh32x8(v *Value, config *Config) bool {
_ = b
// match: (Rsh32x8 <t> x y)
// cond:
- // result: (SARL <t> x (ORB <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [32])))))
+ // result: (SARL <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [32])))))
for {
t := v.Type
x := v.Args[0]
@@ -14053,7 +14399,7 @@ func rewriteValueAMD64_OpRsh32x8(v *Value, config *Config) bool {
v.reset(OpAMD64SARL)
v.Type = t
v.AddArg(x)
- v0 := b.NewValue0(v.Line, OpAMD64ORB, y.Type)
+ v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
v0.AddArg(y)
v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
@@ -14173,7 +14519,7 @@ func rewriteValueAMD64_OpRsh64x16(v *Value, config *Config) bool {
_ = b
// match: (Rsh64x16 <t> x y)
// cond:
- // result: (SARQ <t> x (ORW <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [64])))))
+ // result: (SARQ <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [64])))))
for {
t := v.Type
x := v.Args[0]
@@ -14181,7 +14527,7 @@ func rewriteValueAMD64_OpRsh64x16(v *Value, config *Config) bool {
v.reset(OpAMD64SARQ)
v.Type = t
v.AddArg(x)
- v0 := b.NewValue0(v.Line, OpAMD64ORW, y.Type)
+ v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
v0.AddArg(y)
v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
@@ -14257,7 +14603,7 @@ func rewriteValueAMD64_OpRsh64x8(v *Value, config *Config) bool {
_ = b
// match: (Rsh64x8 <t> x y)
// cond:
- // result: (SARQ <t> x (ORB <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [64])))))
+ // result: (SARQ <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [64])))))
for {
t := v.Type
x := v.Args[0]
@@ -14265,7 +14611,7 @@ func rewriteValueAMD64_OpRsh64x8(v *Value, config *Config) bool {
v.reset(OpAMD64SARQ)
v.Type = t
v.AddArg(x)
- v0 := b.NewValue0(v.Line, OpAMD64ORB, y.Type)
+ v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
v0.AddArg(y)
v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
@@ -14285,12 +14631,12 @@ func rewriteValueAMD64_OpRsh8Ux16(v *Value, config *Config) bool {
_ = b
// match: (Rsh8Ux16 <t> x y)
// cond:
- // result: (ANDB (SHRB <t> x y) (SBBLcarrymask <t> (CMPWconst y [8])))
+ // result: (ANDL (SHRB <t> x y) (SBBLcarrymask <t> (CMPWconst y [8])))
for {
t := v.Type
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64ANDB)
+ v.reset(OpAMD64ANDL)
v0 := b.NewValue0(v.Line, OpAMD64SHRB, t)
v0.AddArg(x)
v0.AddArg(y)
@@ -14310,12 +14656,12 @@ func rewriteValueAMD64_OpRsh8Ux32(v *Value, config *Config) bool {
_ = b
// match: (Rsh8Ux32 <t> x y)
// cond:
- // result: (ANDB (SHRB <t> x y) (SBBLcarrymask <t> (CMPLconst y [8])))
+ // result: (ANDL (SHRB <t> x y) (SBBLcarrymask <t> (CMPLconst y [8])))
for {
t := v.Type
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64ANDB)
+ v.reset(OpAMD64ANDL)
v0 := b.NewValue0(v.Line, OpAMD64SHRB, t)
v0.AddArg(x)
v0.AddArg(y)
@@ -14335,12 +14681,12 @@ func rewriteValueAMD64_OpRsh8Ux64(v *Value, config *Config) bool {
_ = b
// match: (Rsh8Ux64 <t> x y)
// cond:
- // result: (ANDB (SHRB <t> x y) (SBBLcarrymask <t> (CMPQconst y [8])))
+ // result: (ANDL (SHRB <t> x y) (SBBLcarrymask <t> (CMPQconst y [8])))
for {
t := v.Type
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64ANDB)
+ v.reset(OpAMD64ANDL)
v0 := b.NewValue0(v.Line, OpAMD64SHRB, t)
v0.AddArg(x)
v0.AddArg(y)
@@ -14360,12 +14706,12 @@ func rewriteValueAMD64_OpRsh8Ux8(v *Value, config *Config) bool {
_ = b
// match: (Rsh8Ux8 <t> x y)
// cond:
- // result: (ANDB (SHRB <t> x y) (SBBLcarrymask <t> (CMPBconst y [8])))
+ // result: (ANDL (SHRB <t> x y) (SBBLcarrymask <t> (CMPBconst y [8])))
for {
t := v.Type
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64ANDB)
+ v.reset(OpAMD64ANDL)
v0 := b.NewValue0(v.Line, OpAMD64SHRB, t)
v0.AddArg(x)
v0.AddArg(y)
@@ -14385,7 +14731,7 @@ func rewriteValueAMD64_OpRsh8x16(v *Value, config *Config) bool {
_ = b
// match: (Rsh8x16 <t> x y)
// cond:
- // result: (SARB <t> x (ORW <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [8])))))
+ // result: (SARB <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [8])))))
for {
t := v.Type
x := v.Args[0]
@@ -14393,7 +14739,7 @@ func rewriteValueAMD64_OpRsh8x16(v *Value, config *Config) bool {
v.reset(OpAMD64SARB)
v.Type = t
v.AddArg(x)
- v0 := b.NewValue0(v.Line, OpAMD64ORW, y.Type)
+ v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
v0.AddArg(y)
v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
@@ -14469,7 +14815,7 @@ func rewriteValueAMD64_OpRsh8x8(v *Value, config *Config) bool {
_ = b
// match: (Rsh8x8 <t> x y)
// cond:
- // result: (SARB <t> x (ORB <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [8])))))
+ // result: (SARB <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [8])))))
for {
t := v.Type
x := v.Args[0]
@@ -14477,7 +14823,7 @@ func rewriteValueAMD64_OpRsh8x8(v *Value, config *Config) bool {
v.reset(OpAMD64SARB)
v.Type = t
v.AddArg(x)
- v0 := b.NewValue0(v.Line, OpAMD64ORB, y.Type)
+ v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
v0.AddArg(y)
v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
@@ -14525,54 +14871,6 @@ func rewriteValueAMD64_OpAMD64SARB(v *Value, config *Config) bool {
v.AddArg(x)
return true
}
- // match: (SARB x (MOVWconst [c]))
- // cond:
- // result: (SARBconst [c&31] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVWconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SARBconst)
- v.AuxInt = c & 31
- v.AddArg(x)
- return true
- }
- // match: (SARB x (MOVBconst [c]))
- // cond:
- // result: (SARBconst [c&31] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVBconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SARBconst)
- v.AuxInt = c & 31
- v.AddArg(x)
- return true
- }
- // match: (SARB x (ANDBconst [31] y))
- // cond:
- // result: (SARB x y)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64ANDBconst {
- break
- }
- if v_1.AuxInt != 31 {
- break
- }
- y := v_1.Args[0]
- v.reset(OpAMD64SARB)
- v.AddArg(x)
- v.AddArg(y)
- return true
- }
return false
}
func rewriteValueAMD64_OpAMD64SARBconst(v *Value, config *Config) bool {
@@ -14627,36 +14925,6 @@ func rewriteValueAMD64_OpAMD64SARL(v *Value, config *Config) bool {
v.AddArg(x)
return true
}
- // match: (SARL x (MOVWconst [c]))
- // cond:
- // result: (SARLconst [c&31] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVWconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SARLconst)
- v.AuxInt = c & 31
- v.AddArg(x)
- return true
- }
- // match: (SARL x (MOVBconst [c]))
- // cond:
- // result: (SARLconst [c&31] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVBconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SARLconst)
- v.AuxInt = c & 31
- v.AddArg(x)
- return true
- }
// match: (SARL x (ANDLconst [31] y))
// cond:
// result: (SARL x y)
@@ -14729,36 +14997,6 @@ func rewriteValueAMD64_OpAMD64SARQ(v *Value, config *Config) bool {
v.AddArg(x)
return true
}
- // match: (SARQ x (MOVWconst [c]))
- // cond:
- // result: (SARQconst [c&63] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVWconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SARQconst)
- v.AuxInt = c & 63
- v.AddArg(x)
- return true
- }
- // match: (SARQ x (MOVBconst [c]))
- // cond:
- // result: (SARQconst [c&63] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVBconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SARQconst)
- v.AuxInt = c & 63
- v.AddArg(x)
- return true
- }
// match: (SARQ x (ANDQconst [63] y))
// cond:
// result: (SARQ x y)
@@ -14831,54 +15069,6 @@ func rewriteValueAMD64_OpAMD64SARW(v *Value, config *Config) bool {
v.AddArg(x)
return true
}
- // match: (SARW x (MOVWconst [c]))
- // cond:
- // result: (SARWconst [c&31] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVWconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SARWconst)
- v.AuxInt = c & 31
- v.AddArg(x)
- return true
- }
- // match: (SARW x (MOVBconst [c]))
- // cond:
- // result: (SARWconst [c&31] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVBconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SARWconst)
- v.AuxInt = c & 31
- v.AddArg(x)
- return true
- }
- // match: (SARW x (ANDWconst [31] y))
- // cond:
- // result: (SARW x y)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64ANDWconst {
- break
- }
- if v_1.AuxInt != 31 {
- break
- }
- y := v_1.Args[0]
- v.reset(OpAMD64SARW)
- v.AddArg(x)
- v.AddArg(y)
- return true
- }
return false
}
func rewriteValueAMD64_OpAMD64SARWconst(v *Value, config *Config) bool {
@@ -15048,61 +15238,61 @@ func rewriteValueAMD64_OpAMD64SETA(v *Value, config *Config) bool {
}
// match: (SETA (FlagEQ))
// cond:
- // result: (MOVBconst [0])
+ // result: (MOVLconst [0])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagEQ {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 0
return true
}
// match: (SETA (FlagLT_ULT))
// cond:
- // result: (MOVBconst [0])
+ // result: (MOVLconst [0])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagLT_ULT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 0
return true
}
// match: (SETA (FlagLT_UGT))
// cond:
- // result: (MOVBconst [1])
+ // result: (MOVLconst [1])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagLT_UGT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 1
return true
}
// match: (SETA (FlagGT_ULT))
// cond:
- // result: (MOVBconst [0])
+ // result: (MOVLconst [0])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagGT_ULT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 0
return true
}
// match: (SETA (FlagGT_UGT))
// cond:
- // result: (MOVBconst [1])
+ // result: (MOVLconst [1])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagGT_UGT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 1
return true
}
@@ -15126,61 +15316,61 @@ func rewriteValueAMD64_OpAMD64SETAE(v *Value, config *Config) bool {
}
// match: (SETAE (FlagEQ))
// cond:
- // result: (MOVBconst [1])
+ // result: (MOVLconst [1])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagEQ {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 1
return true
}
// match: (SETAE (FlagLT_ULT))
// cond:
- // result: (MOVBconst [0])
+ // result: (MOVLconst [0])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagLT_ULT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 0
return true
}
// match: (SETAE (FlagLT_UGT))
// cond:
- // result: (MOVBconst [1])
+ // result: (MOVLconst [1])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagLT_UGT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 1
return true
}
// match: (SETAE (FlagGT_ULT))
// cond:
- // result: (MOVBconst [0])
+ // result: (MOVLconst [0])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagGT_ULT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 0
return true
}
// match: (SETAE (FlagGT_UGT))
// cond:
- // result: (MOVBconst [1])
+ // result: (MOVLconst [1])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagGT_UGT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 1
return true
}
@@ -15204,61 +15394,61 @@ func rewriteValueAMD64_OpAMD64SETB(v *Value, config *Config) bool {
}
// match: (SETB (FlagEQ))
// cond:
- // result: (MOVBconst [0])
+ // result: (MOVLconst [0])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagEQ {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 0
return true
}
// match: (SETB (FlagLT_ULT))
// cond:
- // result: (MOVBconst [1])
+ // result: (MOVLconst [1])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagLT_ULT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 1
return true
}
// match: (SETB (FlagLT_UGT))
// cond:
- // result: (MOVBconst [0])
+ // result: (MOVLconst [0])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagLT_UGT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 0
return true
}
// match: (SETB (FlagGT_ULT))
// cond:
- // result: (MOVBconst [1])
+ // result: (MOVLconst [1])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagGT_ULT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 1
return true
}
// match: (SETB (FlagGT_UGT))
// cond:
- // result: (MOVBconst [0])
+ // result: (MOVLconst [0])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagGT_UGT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 0
return true
}
@@ -15282,61 +15472,61 @@ func rewriteValueAMD64_OpAMD64SETBE(v *Value, config *Config) bool {
}
// match: (SETBE (FlagEQ))
// cond:
- // result: (MOVBconst [1])
+ // result: (MOVLconst [1])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagEQ {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 1
return true
}
// match: (SETBE (FlagLT_ULT))
// cond:
- // result: (MOVBconst [1])
+ // result: (MOVLconst [1])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagLT_ULT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 1
return true
}
// match: (SETBE (FlagLT_UGT))
// cond:
- // result: (MOVBconst [0])
+ // result: (MOVLconst [0])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagLT_UGT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 0
return true
}
// match: (SETBE (FlagGT_ULT))
// cond:
- // result: (MOVBconst [1])
+ // result: (MOVLconst [1])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagGT_ULT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 1
return true
}
// match: (SETBE (FlagGT_UGT))
// cond:
- // result: (MOVBconst [0])
+ // result: (MOVLconst [0])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagGT_UGT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 0
return true
}
@@ -15360,61 +15550,61 @@ func rewriteValueAMD64_OpAMD64SETEQ(v *Value, config *Config) bool {
}
// match: (SETEQ (FlagEQ))
// cond:
- // result: (MOVBconst [1])
+ // result: (MOVLconst [1])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagEQ {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 1
return true
}
// match: (SETEQ (FlagLT_ULT))
// cond:
- // result: (MOVBconst [0])
+ // result: (MOVLconst [0])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagLT_ULT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 0
return true
}
// match: (SETEQ (FlagLT_UGT))
// cond:
- // result: (MOVBconst [0])
+ // result: (MOVLconst [0])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagLT_UGT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 0
return true
}
// match: (SETEQ (FlagGT_ULT))
// cond:
- // result: (MOVBconst [0])
+ // result: (MOVLconst [0])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagGT_ULT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 0
return true
}
// match: (SETEQ (FlagGT_UGT))
// cond:
- // result: (MOVBconst [0])
+ // result: (MOVLconst [0])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagGT_UGT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 0
return true
}
@@ -15438,61 +15628,61 @@ func rewriteValueAMD64_OpAMD64SETG(v *Value, config *Config) bool {
}
// match: (SETG (FlagEQ))
// cond:
- // result: (MOVBconst [0])
+ // result: (MOVLconst [0])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagEQ {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 0
return true
}
// match: (SETG (FlagLT_ULT))
// cond:
- // result: (MOVBconst [0])
+ // result: (MOVLconst [0])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagLT_ULT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 0
return true
}
// match: (SETG (FlagLT_UGT))
// cond:
- // result: (MOVBconst [0])
+ // result: (MOVLconst [0])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagLT_UGT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 0
return true
}
// match: (SETG (FlagGT_ULT))
// cond:
- // result: (MOVBconst [1])
+ // result: (MOVLconst [1])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagGT_ULT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 1
return true
}
// match: (SETG (FlagGT_UGT))
// cond:
- // result: (MOVBconst [1])
+ // result: (MOVLconst [1])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagGT_UGT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 1
return true
}
@@ -15516,61 +15706,61 @@ func rewriteValueAMD64_OpAMD64SETGE(v *Value, config *Config) bool {
}
// match: (SETGE (FlagEQ))
// cond:
- // result: (MOVBconst [1])
+ // result: (MOVLconst [1])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagEQ {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 1
return true
}
// match: (SETGE (FlagLT_ULT))
// cond:
- // result: (MOVBconst [0])
+ // result: (MOVLconst [0])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagLT_ULT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 0
return true
}
// match: (SETGE (FlagLT_UGT))
// cond:
- // result: (MOVBconst [0])
+ // result: (MOVLconst [0])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagLT_UGT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 0
return true
}
// match: (SETGE (FlagGT_ULT))
// cond:
- // result: (MOVBconst [1])
+ // result: (MOVLconst [1])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagGT_ULT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 1
return true
}
// match: (SETGE (FlagGT_UGT))
// cond:
- // result: (MOVBconst [1])
+ // result: (MOVLconst [1])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagGT_UGT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 1
return true
}
@@ -15594,61 +15784,61 @@ func rewriteValueAMD64_OpAMD64SETL(v *Value, config *Config) bool {
}
// match: (SETL (FlagEQ))
// cond:
- // result: (MOVBconst [0])
+ // result: (MOVLconst [0])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagEQ {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 0
return true
}
// match: (SETL (FlagLT_ULT))
// cond:
- // result: (MOVBconst [1])
+ // result: (MOVLconst [1])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagLT_ULT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 1
return true
}
// match: (SETL (FlagLT_UGT))
// cond:
- // result: (MOVBconst [1])
+ // result: (MOVLconst [1])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagLT_UGT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 1
return true
}
// match: (SETL (FlagGT_ULT))
// cond:
- // result: (MOVBconst [0])
+ // result: (MOVLconst [0])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagGT_ULT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 0
return true
}
// match: (SETL (FlagGT_UGT))
// cond:
- // result: (MOVBconst [0])
+ // result: (MOVLconst [0])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagGT_UGT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 0
return true
}
@@ -15672,61 +15862,61 @@ func rewriteValueAMD64_OpAMD64SETLE(v *Value, config *Config) bool {
}
// match: (SETLE (FlagEQ))
// cond:
- // result: (MOVBconst [1])
+ // result: (MOVLconst [1])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagEQ {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 1
return true
}
// match: (SETLE (FlagLT_ULT))
// cond:
- // result: (MOVBconst [1])
+ // result: (MOVLconst [1])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagLT_ULT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 1
return true
}
// match: (SETLE (FlagLT_UGT))
// cond:
- // result: (MOVBconst [1])
+ // result: (MOVLconst [1])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagLT_UGT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 1
return true
}
// match: (SETLE (FlagGT_ULT))
// cond:
- // result: (MOVBconst [0])
+ // result: (MOVLconst [0])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagGT_ULT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 0
return true
}
// match: (SETLE (FlagGT_UGT))
// cond:
- // result: (MOVBconst [0])
+ // result: (MOVLconst [0])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagGT_UGT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 0
return true
}
@@ -15750,149 +15940,66 @@ func rewriteValueAMD64_OpAMD64SETNE(v *Value, config *Config) bool {
}
// match: (SETNE (FlagEQ))
// cond:
- // result: (MOVBconst [0])
+ // result: (MOVLconst [0])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagEQ {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 0
return true
}
// match: (SETNE (FlagLT_ULT))
// cond:
- // result: (MOVBconst [1])
+ // result: (MOVLconst [1])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagLT_ULT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 1
return true
}
// match: (SETNE (FlagLT_UGT))
// cond:
- // result: (MOVBconst [1])
+ // result: (MOVLconst [1])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagLT_UGT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 1
return true
}
// match: (SETNE (FlagGT_ULT))
// cond:
- // result: (MOVBconst [1])
+ // result: (MOVLconst [1])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagGT_ULT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 1
return true
}
// match: (SETNE (FlagGT_UGT))
// cond:
- // result: (MOVBconst [1])
+ // result: (MOVLconst [1])
for {
v_0 := v.Args[0]
if v_0.Op != OpAMD64FlagGT_UGT {
break
}
- v.reset(OpAMD64MOVBconst)
+ v.reset(OpAMD64MOVLconst)
v.AuxInt = 1
return true
}
return false
}
-func rewriteValueAMD64_OpAMD64SHLB(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (SHLB x (MOVQconst [c]))
- // cond:
- // result: (SHLBconst [c&31] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVQconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SHLBconst)
- v.AuxInt = c & 31
- v.AddArg(x)
- return true
- }
- // match: (SHLB x (MOVLconst [c]))
- // cond:
- // result: (SHLBconst [c&31] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVLconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SHLBconst)
- v.AuxInt = c & 31
- v.AddArg(x)
- return true
- }
- // match: (SHLB x (MOVWconst [c]))
- // cond:
- // result: (SHLBconst [c&31] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVWconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SHLBconst)
- v.AuxInt = c & 31
- v.AddArg(x)
- return true
- }
- // match: (SHLB x (MOVBconst [c]))
- // cond:
- // result: (SHLBconst [c&31] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVBconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SHLBconst)
- v.AuxInt = c & 31
- v.AddArg(x)
- return true
- }
- // match: (SHLB x (ANDBconst [31] y))
- // cond:
- // result: (SHLB x y)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64ANDBconst {
- break
- }
- if v_1.AuxInt != 31 {
- break
- }
- y := v_1.Args[0]
- v.reset(OpAMD64SHLB)
- v.AddArg(x)
- v.AddArg(y)
- return true
- }
- return false
-}
func rewriteValueAMD64_OpAMD64SHLL(v *Value, config *Config) bool {
b := v.Block
_ = b
@@ -15926,36 +16033,6 @@ func rewriteValueAMD64_OpAMD64SHLL(v *Value, config *Config) bool {
v.AddArg(x)
return true
}
- // match: (SHLL x (MOVWconst [c]))
- // cond:
- // result: (SHLLconst [c&31] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVWconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SHLLconst)
- v.AuxInt = c & 31
- v.AddArg(x)
- return true
- }
- // match: (SHLL x (MOVBconst [c]))
- // cond:
- // result: (SHLLconst [c&31] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVBconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SHLLconst)
- v.AuxInt = c & 31
- v.AddArg(x)
- return true
- }
// match: (SHLL x (ANDLconst [31] y))
// cond:
// result: (SHLL x y)
@@ -16009,36 +16086,6 @@ func rewriteValueAMD64_OpAMD64SHLQ(v *Value, config *Config) bool {
v.AddArg(x)
return true
}
- // match: (SHLQ x (MOVWconst [c]))
- // cond:
- // result: (SHLQconst [c&63] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVWconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SHLQconst)
- v.AuxInt = c & 63
- v.AddArg(x)
- return true
- }
- // match: (SHLQ x (MOVBconst [c]))
- // cond:
- // result: (SHLQconst [c&63] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVBconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SHLQconst)
- v.AuxInt = c & 63
- v.AddArg(x)
- return true
- }
// match: (SHLQ x (ANDQconst [63] y))
// cond:
// result: (SHLQ x y)
@@ -16059,89 +16106,6 @@ func rewriteValueAMD64_OpAMD64SHLQ(v *Value, config *Config) bool {
}
return false
}
-func rewriteValueAMD64_OpAMD64SHLW(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (SHLW x (MOVQconst [c]))
- // cond:
- // result: (SHLWconst [c&31] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVQconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SHLWconst)
- v.AuxInt = c & 31
- v.AddArg(x)
- return true
- }
- // match: (SHLW x (MOVLconst [c]))
- // cond:
- // result: (SHLWconst [c&31] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVLconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SHLWconst)
- v.AuxInt = c & 31
- v.AddArg(x)
- return true
- }
- // match: (SHLW x (MOVWconst [c]))
- // cond:
- // result: (SHLWconst [c&31] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVWconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SHLWconst)
- v.AuxInt = c & 31
- v.AddArg(x)
- return true
- }
- // match: (SHLW x (MOVBconst [c]))
- // cond:
- // result: (SHLWconst [c&31] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVBconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SHLWconst)
- v.AuxInt = c & 31
- v.AddArg(x)
- return true
- }
- // match: (SHLW x (ANDWconst [31] y))
- // cond:
- // result: (SHLW x y)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64ANDWconst {
- break
- }
- if v_1.AuxInt != 31 {
- break
- }
- y := v_1.Args[0]
- v.reset(OpAMD64SHLW)
- v.AddArg(x)
- v.AddArg(y)
- return true
- }
- return false
-}
func rewriteValueAMD64_OpAMD64SHRB(v *Value, config *Config) bool {
b := v.Block
_ = b
@@ -16175,54 +16139,6 @@ func rewriteValueAMD64_OpAMD64SHRB(v *Value, config *Config) bool {
v.AddArg(x)
return true
}
- // match: (SHRB x (MOVWconst [c]))
- // cond:
- // result: (SHRBconst [c&31] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVWconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SHRBconst)
- v.AuxInt = c & 31
- v.AddArg(x)
- return true
- }
- // match: (SHRB x (MOVBconst [c]))
- // cond:
- // result: (SHRBconst [c&31] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVBconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SHRBconst)
- v.AuxInt = c & 31
- v.AddArg(x)
- return true
- }
- // match: (SHRB x (ANDBconst [31] y))
- // cond:
- // result: (SHRB x y)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64ANDBconst {
- break
- }
- if v_1.AuxInt != 31 {
- break
- }
- y := v_1.Args[0]
- v.reset(OpAMD64SHRB)
- v.AddArg(x)
- v.AddArg(y)
- return true
- }
return false
}
func rewriteValueAMD64_OpAMD64SHRL(v *Value, config *Config) bool {
@@ -16258,36 +16174,6 @@ func rewriteValueAMD64_OpAMD64SHRL(v *Value, config *Config) bool {
v.AddArg(x)
return true
}
- // match: (SHRL x (MOVWconst [c]))
- // cond:
- // result: (SHRLconst [c&31] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVWconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SHRLconst)
- v.AuxInt = c & 31
- v.AddArg(x)
- return true
- }
- // match: (SHRL x (MOVBconst [c]))
- // cond:
- // result: (SHRLconst [c&31] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVBconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SHRLconst)
- v.AuxInt = c & 31
- v.AddArg(x)
- return true
- }
// match: (SHRL x (ANDLconst [31] y))
// cond:
// result: (SHRL x y)
@@ -16341,36 +16227,6 @@ func rewriteValueAMD64_OpAMD64SHRQ(v *Value, config *Config) bool {
v.AddArg(x)
return true
}
- // match: (SHRQ x (MOVWconst [c]))
- // cond:
- // result: (SHRQconst [c&63] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVWconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SHRQconst)
- v.AuxInt = c & 63
- v.AddArg(x)
- return true
- }
- // match: (SHRQ x (MOVBconst [c]))
- // cond:
- // result: (SHRQconst [c&63] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVBconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SHRQconst)
- v.AuxInt = c & 63
- v.AddArg(x)
- return true
- }
// match: (SHRQ x (ANDQconst [63] y))
// cond:
// result: (SHRQ x y)
@@ -16424,152 +16280,6 @@ func rewriteValueAMD64_OpAMD64SHRW(v *Value, config *Config) bool {
v.AddArg(x)
return true
}
- // match: (SHRW x (MOVWconst [c]))
- // cond:
- // result: (SHRWconst [c&31] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVWconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SHRWconst)
- v.AuxInt = c & 31
- v.AddArg(x)
- return true
- }
- // match: (SHRW x (MOVBconst [c]))
- // cond:
- // result: (SHRWconst [c&31] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVBconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SHRWconst)
- v.AuxInt = c & 31
- v.AddArg(x)
- return true
- }
- // match: (SHRW x (ANDWconst [31] y))
- // cond:
- // result: (SHRW x y)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64ANDWconst {
- break
- }
- if v_1.AuxInt != 31 {
- break
- }
- y := v_1.Args[0]
- v.reset(OpAMD64SHRW)
- v.AddArg(x)
- v.AddArg(y)
- return true
- }
- return false
-}
-func rewriteValueAMD64_OpAMD64SUBB(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (SUBB x (MOVBconst [c]))
- // cond:
- // result: (SUBBconst x [c])
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVBconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SUBBconst)
- v.AddArg(x)
- v.AuxInt = c
- return true
- }
- // match: (SUBB (MOVBconst [c]) x)
- // cond:
- // result: (NEGB (SUBBconst <v.Type> x [c]))
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVBconst {
- break
- }
- c := v_0.AuxInt
- x := v.Args[1]
- v.reset(OpAMD64NEGB)
- v0 := b.NewValue0(v.Line, OpAMD64SUBBconst, v.Type)
- v0.AddArg(x)
- v0.AuxInt = c
- v.AddArg(v0)
- return true
- }
- // match: (SUBB x x)
- // cond:
- // result: (MOVBconst [0])
- for {
- x := v.Args[0]
- if x != v.Args[1] {
- break
- }
- v.reset(OpAMD64MOVBconst)
- v.AuxInt = 0
- return true
- }
- return false
-}
-func rewriteValueAMD64_OpAMD64SUBBconst(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (SUBBconst [c] x)
- // cond: int8(c) == 0
- // result: x
- for {
- c := v.AuxInt
- x := v.Args[0]
- if !(int8(c) == 0) {
- break
- }
- v.reset(OpCopy)
- v.Type = x.Type
- v.AddArg(x)
- return true
- }
- // match: (SUBBconst (MOVBconst [d]) [c])
- // cond:
- // result: (MOVBconst [int64(int8(d-c))])
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVBconst {
- break
- }
- d := v_0.AuxInt
- c := v.AuxInt
- v.reset(OpAMD64MOVBconst)
- v.AuxInt = int64(int8(d - c))
- return true
- }
- // match: (SUBBconst (SUBBconst x [d]) [c])
- // cond:
- // result: (ADDBconst [int64(int8(-c-d))] x)
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64SUBBconst {
- break
- }
- x := v_0.Args[0]
- d := v_0.AuxInt
- c := v.AuxInt
- v.reset(OpAMD64ADDBconst)
- v.AuxInt = int64(int8(-c - d))
- v.AddArg(x)
- return true
- }
return false
}
func rewriteValueAMD64_OpAMD64SUBL(v *Value, config *Config) bool {
@@ -16638,6 +16348,17 @@ func rewriteValueAMD64_OpAMD64SUBLconst(v *Value, config *Config) bool {
v.AddArg(x)
return true
}
+ // match: (SUBLconst [c] x)
+ // cond:
+ // result: (ADDLconst [int64(int32(-c))] x)
+ for {
+ c := v.AuxInt
+ x := v.Args[0]
+ v.reset(OpAMD64ADDLconst)
+ v.AuxInt = int64(int32(-c))
+ v.AddArg(x)
+ return true
+ }
// match: (SUBLconst (MOVLconst [d]) [c])
// cond:
// result: (MOVLconst [int64(int32(d-c))])
@@ -16741,6 +16462,20 @@ func rewriteValueAMD64_OpAMD64SUBQconst(v *Value, config *Config) bool {
v.AddArg(x)
return true
}
+ // match: (SUBQconst [c] x)
+ // cond: c != -(1<<31)
+ // result: (ADDQconst [-c] x)
+ for {
+ c := v.AuxInt
+ x := v.Args[0]
+ if !(c != -(1 << 31)) {
+ break
+ }
+ v.reset(OpAMD64ADDQconst)
+ v.AuxInt = -c
+ v.AddArg(x)
+ return true
+ }
// match: (SUBQconst (MOVQconst [d]) [c])
// cond:
// result: (MOVQconst [d-c])
@@ -16776,104 +16511,6 @@ func rewriteValueAMD64_OpAMD64SUBQconst(v *Value, config *Config) bool {
}
return false
}
-func rewriteValueAMD64_OpAMD64SUBW(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (SUBW x (MOVWconst [c]))
- // cond:
- // result: (SUBWconst x [c])
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVWconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64SUBWconst)
- v.AddArg(x)
- v.AuxInt = c
- return true
- }
- // match: (SUBW (MOVWconst [c]) x)
- // cond:
- // result: (NEGW (SUBWconst <v.Type> x [c]))
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVWconst {
- break
- }
- c := v_0.AuxInt
- x := v.Args[1]
- v.reset(OpAMD64NEGW)
- v0 := b.NewValue0(v.Line, OpAMD64SUBWconst, v.Type)
- v0.AddArg(x)
- v0.AuxInt = c
- v.AddArg(v0)
- return true
- }
- // match: (SUBW x x)
- // cond:
- // result: (MOVWconst [0])
- for {
- x := v.Args[0]
- if x != v.Args[1] {
- break
- }
- v.reset(OpAMD64MOVWconst)
- v.AuxInt = 0
- return true
- }
- return false
-}
-func rewriteValueAMD64_OpAMD64SUBWconst(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (SUBWconst [c] x)
- // cond: int16(c) == 0
- // result: x
- for {
- c := v.AuxInt
- x := v.Args[0]
- if !(int16(c) == 0) {
- break
- }
- v.reset(OpCopy)
- v.Type = x.Type
- v.AddArg(x)
- return true
- }
- // match: (SUBWconst (MOVWconst [d]) [c])
- // cond:
- // result: (MOVWconst [int64(int16(d-c))])
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVWconst {
- break
- }
- d := v_0.AuxInt
- c := v.AuxInt
- v.reset(OpAMD64MOVWconst)
- v.AuxInt = int64(int16(d - c))
- return true
- }
- // match: (SUBWconst (SUBWconst x [d]) [c])
- // cond:
- // result: (ADDWconst [int64(int16(-c-d))] x)
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64SUBWconst {
- break
- }
- x := v_0.Args[0]
- d := v_0.AuxInt
- c := v.AuxInt
- v.reset(OpAMD64ADDWconst)
- v.AuxInt = int64(int16(-c - d))
- v.AddArg(x)
- return true
- }
- return false
-}
func rewriteValueAMD64_OpSignExt16to32(v *Value, config *Config) bool {
b := v.Block
_ = b
@@ -17102,11 +16739,11 @@ func rewriteValueAMD64_OpSub16(v *Value, config *Config) bool {
_ = b
// match: (Sub16 x y)
// cond:
- // result: (SUBW x y)
+ // result: (SUBL x y)
for {
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64SUBW)
+ v.reset(OpAMD64SUBL)
v.AddArg(x)
v.AddArg(y)
return true
@@ -17182,11 +16819,11 @@ func rewriteValueAMD64_OpSub8(v *Value, config *Config) bool {
_ = b
// match: (Sub8 x y)
// cond:
- // result: (SUBB x y)
+ // result: (SUBL x y)
for {
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64SUBB)
+ v.reset(OpAMD64SUBL)
v.AddArg(x)
v.AddArg(y)
return true
@@ -17299,86 +16936,6 @@ func rewriteValueAMD64_OpTrunc64to8(v *Value, config *Config) bool {
}
return false
}
-func rewriteValueAMD64_OpAMD64XORB(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (XORB x (MOVBconst [c]))
- // cond:
- // result: (XORBconst [c] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVBconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64XORBconst)
- v.AuxInt = c
- v.AddArg(x)
- return true
- }
- // match: (XORB (MOVBconst [c]) x)
- // cond:
- // result: (XORBconst [c] x)
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVBconst {
- break
- }
- c := v_0.AuxInt
- x := v.Args[1]
- v.reset(OpAMD64XORBconst)
- v.AuxInt = c
- v.AddArg(x)
- return true
- }
- // match: (XORB x x)
- // cond:
- // result: (MOVBconst [0])
- for {
- x := v.Args[0]
- if x != v.Args[1] {
- break
- }
- v.reset(OpAMD64MOVBconst)
- v.AuxInt = 0
- return true
- }
- return false
-}
-func rewriteValueAMD64_OpAMD64XORBconst(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (XORBconst [c] x)
- // cond: int8(c)==0
- // result: x
- for {
- c := v.AuxInt
- x := v.Args[0]
- if !(int8(c) == 0) {
- break
- }
- v.reset(OpCopy)
- v.Type = x.Type
- v.AddArg(x)
- return true
- }
- // match: (XORBconst [c] (MOVBconst [d]))
- // cond:
- // result: (MOVBconst [c^d])
- for {
- c := v.AuxInt
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVBconst {
- break
- }
- d := v_0.AuxInt
- v.reset(OpAMD64MOVBconst)
- v.AuxInt = c ^ d
- return true
- }
- return false
-}
func rewriteValueAMD64_OpAMD64XORL(v *Value, config *Config) bool {
b := v.Block
_ = b
@@ -17544,96 +17101,16 @@ func rewriteValueAMD64_OpAMD64XORQconst(v *Value, config *Config) bool {
}
return false
}
-func rewriteValueAMD64_OpAMD64XORW(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (XORW x (MOVWconst [c]))
- // cond:
- // result: (XORWconst [c] x)
- for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpAMD64MOVWconst {
- break
- }
- c := v_1.AuxInt
- v.reset(OpAMD64XORWconst)
- v.AuxInt = c
- v.AddArg(x)
- return true
- }
- // match: (XORW (MOVWconst [c]) x)
- // cond:
- // result: (XORWconst [c] x)
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVWconst {
- break
- }
- c := v_0.AuxInt
- x := v.Args[1]
- v.reset(OpAMD64XORWconst)
- v.AuxInt = c
- v.AddArg(x)
- return true
- }
- // match: (XORW x x)
- // cond:
- // result: (MOVWconst [0])
- for {
- x := v.Args[0]
- if x != v.Args[1] {
- break
- }
- v.reset(OpAMD64MOVWconst)
- v.AuxInt = 0
- return true
- }
- return false
-}
-func rewriteValueAMD64_OpAMD64XORWconst(v *Value, config *Config) bool {
- b := v.Block
- _ = b
- // match: (XORWconst [c] x)
- // cond: int16(c)==0
- // result: x
- for {
- c := v.AuxInt
- x := v.Args[0]
- if !(int16(c) == 0) {
- break
- }
- v.reset(OpCopy)
- v.Type = x.Type
- v.AddArg(x)
- return true
- }
- // match: (XORWconst [c] (MOVWconst [d]))
- // cond:
- // result: (MOVWconst [c^d])
- for {
- c := v.AuxInt
- v_0 := v.Args[0]
- if v_0.Op != OpAMD64MOVWconst {
- break
- }
- d := v_0.AuxInt
- v.reset(OpAMD64MOVWconst)
- v.AuxInt = c ^ d
- return true
- }
- return false
-}
func rewriteValueAMD64_OpXor16(v *Value, config *Config) bool {
b := v.Block
_ = b
// match: (Xor16 x y)
// cond:
- // result: (XORW x y)
+ // result: (XORL x y)
for {
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64XORW)
+ v.reset(OpAMD64XORL)
v.AddArg(x)
v.AddArg(y)
return true
@@ -17677,11 +17154,11 @@ func rewriteValueAMD64_OpXor8(v *Value, config *Config) bool {
_ = b
// match: (Xor8 x y)
// cond:
- // result: (XORB x y)
+ // result: (XORL x y)
for {
x := v.Args[0]
y := v.Args[1]
- v.reset(OpAMD64XORB)
+ v.reset(OpAMD64XORL)
v.AddArg(x)
v.AddArg(y)
return true
@@ -18849,7 +18326,7 @@ func rewriteBlockAMD64(b *Block) bool {
return true
}
case BlockAMD64NE:
- // match: (NE (TESTB (SETL cmp)) yes no)
+ // match: (NE (TESTB (SETL cmp) (SETL cmp)) yes no)
// cond:
// result: (LT cmp yes no)
for {
@@ -18862,6 +18339,13 @@ func rewriteBlockAMD64(b *Block) bool {
break
}
cmp := v_0.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAMD64SETL {
+ break
+ }
+ if cmp != v_1.Args[0] {
+ break
+ }
yes := b.Succs[0]
no := b.Succs[1]
b.Kind = BlockAMD64LT
@@ -18870,7 +18354,7 @@ func rewriteBlockAMD64(b *Block) bool {
b.Succs[1] = no
return true
}
- // match: (NE (TESTB (SETLE cmp)) yes no)
+ // match: (NE (TESTB (SETLE cmp) (SETLE cmp)) yes no)
// cond:
// result: (LE cmp yes no)
for {
@@ -18883,6 +18367,13 @@ func rewriteBlockAMD64(b *Block) bool {
break
}
cmp := v_0.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAMD64SETLE {
+ break
+ }
+ if cmp != v_1.Args[0] {
+ break
+ }
yes := b.Succs[0]
no := b.Succs[1]
b.Kind = BlockAMD64LE
@@ -18891,7 +18382,7 @@ func rewriteBlockAMD64(b *Block) bool {
b.Succs[1] = no
return true
}
- // match: (NE (TESTB (SETG cmp)) yes no)
+ // match: (NE (TESTB (SETG cmp) (SETG cmp)) yes no)
// cond:
// result: (GT cmp yes no)
for {
@@ -18904,6 +18395,13 @@ func rewriteBlockAMD64(b *Block) bool {
break
}
cmp := v_0.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAMD64SETG {
+ break
+ }
+ if cmp != v_1.Args[0] {
+ break
+ }
yes := b.Succs[0]
no := b.Succs[1]
b.Kind = BlockAMD64GT
@@ -18912,7 +18410,7 @@ func rewriteBlockAMD64(b *Block) bool {
b.Succs[1] = no
return true
}
- // match: (NE (TESTB (SETGE cmp)) yes no)
+ // match: (NE (TESTB (SETGE cmp) (SETGE cmp)) yes no)
// cond:
// result: (GE cmp yes no)
for {
@@ -18925,6 +18423,13 @@ func rewriteBlockAMD64(b *Block) bool {
break
}
cmp := v_0.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAMD64SETGE {
+ break
+ }
+ if cmp != v_1.Args[0] {
+ break
+ }
yes := b.Succs[0]
no := b.Succs[1]
b.Kind = BlockAMD64GE
@@ -18933,7 +18438,7 @@ func rewriteBlockAMD64(b *Block) bool {
b.Succs[1] = no
return true
}
- // match: (NE (TESTB (SETEQ cmp)) yes no)
+ // match: (NE (TESTB (SETEQ cmp) (SETEQ cmp)) yes no)
// cond:
// result: (EQ cmp yes no)
for {
@@ -18946,6 +18451,13 @@ func rewriteBlockAMD64(b *Block) bool {
break
}
cmp := v_0.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAMD64SETEQ {
+ break
+ }
+ if cmp != v_1.Args[0] {
+ break
+ }
yes := b.Succs[0]
no := b.Succs[1]
b.Kind = BlockAMD64EQ
@@ -18954,7 +18466,7 @@ func rewriteBlockAMD64(b *Block) bool {
b.Succs[1] = no
return true
}
- // match: (NE (TESTB (SETNE cmp)) yes no)
+ // match: (NE (TESTB (SETNE cmp) (SETNE cmp)) yes no)
// cond:
// result: (NE cmp yes no)
for {
@@ -18967,6 +18479,13 @@ func rewriteBlockAMD64(b *Block) bool {
break
}
cmp := v_0.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAMD64SETNE {
+ break
+ }
+ if cmp != v_1.Args[0] {
+ break
+ }
yes := b.Succs[0]
no := b.Succs[1]
b.Kind = BlockAMD64NE
@@ -18975,7 +18494,7 @@ func rewriteBlockAMD64(b *Block) bool {
b.Succs[1] = no
return true
}
- // match: (NE (TESTB (SETB cmp)) yes no)
+ // match: (NE (TESTB (SETB cmp) (SETB cmp)) yes no)
// cond:
// result: (ULT cmp yes no)
for {
@@ -18988,6 +18507,13 @@ func rewriteBlockAMD64(b *Block) bool {
break
}
cmp := v_0.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAMD64SETB {
+ break
+ }
+ if cmp != v_1.Args[0] {
+ break
+ }
yes := b.Succs[0]
no := b.Succs[1]
b.Kind = BlockAMD64ULT
@@ -18996,7 +18522,7 @@ func rewriteBlockAMD64(b *Block) bool {
b.Succs[1] = no
return true
}
- // match: (NE (TESTB (SETBE cmp)) yes no)
+ // match: (NE (TESTB (SETBE cmp) (SETBE cmp)) yes no)
// cond:
// result: (ULE cmp yes no)
for {
@@ -19009,6 +18535,13 @@ func rewriteBlockAMD64(b *Block) bool {
break
}
cmp := v_0.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAMD64SETBE {
+ break
+ }
+ if cmp != v_1.Args[0] {
+ break
+ }
yes := b.Succs[0]
no := b.Succs[1]
b.Kind = BlockAMD64ULE
@@ -19017,7 +18550,7 @@ func rewriteBlockAMD64(b *Block) bool {
b.Succs[1] = no
return true
}
- // match: (NE (TESTB (SETA cmp)) yes no)
+ // match: (NE (TESTB (SETA cmp) (SETA cmp)) yes no)
// cond:
// result: (UGT cmp yes no)
for {
@@ -19030,6 +18563,13 @@ func rewriteBlockAMD64(b *Block) bool {
break
}
cmp := v_0.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAMD64SETA {
+ break
+ }
+ if cmp != v_1.Args[0] {
+ break
+ }
yes := b.Succs[0]
no := b.Succs[1]
b.Kind = BlockAMD64UGT
@@ -19038,7 +18578,7 @@ func rewriteBlockAMD64(b *Block) bool {
b.Succs[1] = no
return true
}
- // match: (NE (TESTB (SETAE cmp)) yes no)
+ // match: (NE (TESTB (SETAE cmp) (SETAE cmp)) yes no)
// cond:
// result: (UGE cmp yes no)
for {
@@ -19051,6 +18591,13 @@ func rewriteBlockAMD64(b *Block) bool {
break
}
cmp := v_0.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAMD64SETAE {
+ break
+ }
+ if cmp != v_1.Args[0] {
+ break
+ }
yes := b.Succs[0]
no := b.Succs[1]
b.Kind = BlockAMD64UGE
@@ -19059,7 +18606,7 @@ func rewriteBlockAMD64(b *Block) bool {
b.Succs[1] = no
return true
}
- // match: (NE (TESTB (SETGF cmp)) yes no)
+ // match: (NE (TESTB (SETGF cmp) (SETGF cmp)) yes no)
// cond:
// result: (UGT cmp yes no)
for {
@@ -19072,6 +18619,13 @@ func rewriteBlockAMD64(b *Block) bool {
break
}
cmp := v_0.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAMD64SETGF {
+ break
+ }
+ if cmp != v_1.Args[0] {
+ break
+ }
yes := b.Succs[0]
no := b.Succs[1]
b.Kind = BlockAMD64UGT
@@ -19080,7 +18634,7 @@ func rewriteBlockAMD64(b *Block) bool {
b.Succs[1] = no
return true
}
- // match: (NE (TESTB (SETGEF cmp)) yes no)
+ // match: (NE (TESTB (SETGEF cmp) (SETGEF cmp)) yes no)
// cond:
// result: (UGE cmp yes no)
for {
@@ -19093,6 +18647,13 @@ func rewriteBlockAMD64(b *Block) bool {
break
}
cmp := v_0.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAMD64SETGEF {
+ break
+ }
+ if cmp != v_1.Args[0] {
+ break
+ }
yes := b.Succs[0]
no := b.Succs[1]
b.Kind = BlockAMD64UGE
@@ -19101,7 +18662,7 @@ func rewriteBlockAMD64(b *Block) bool {
b.Succs[1] = no
return true
}
- // match: (NE (TESTB (SETEQF cmp)) yes no)
+ // match: (NE (TESTB (SETEQF cmp) (SETEQF cmp)) yes no)
// cond:
// result: (EQF cmp yes no)
for {
@@ -19114,6 +18675,13 @@ func rewriteBlockAMD64(b *Block) bool {
break
}
cmp := v_0.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAMD64SETEQF {
+ break
+ }
+ if cmp != v_1.Args[0] {
+ break
+ }
yes := b.Succs[0]
no := b.Succs[1]
b.Kind = BlockAMD64EQF
@@ -19122,7 +18690,7 @@ func rewriteBlockAMD64(b *Block) bool {
b.Succs[1] = no
return true
}
- // match: (NE (TESTB (SETNEF cmp)) yes no)
+ // match: (NE (TESTB (SETNEF cmp) (SETNEF cmp)) yes no)
// cond:
// result: (NEF cmp yes no)
for {
@@ -19135,6 +18703,13 @@ func rewriteBlockAMD64(b *Block) bool {
break
}
cmp := v_0.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAMD64SETNEF {
+ break
+ }
+ if cmp != v_1.Args[0] {
+ break
+ }
yes := b.Succs[0]
no := b.Succs[1]
b.Kind = BlockAMD64NEF
diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go
index 932cb42235..43e87c3bf6 100644
--- a/src/cmd/compile/internal/ssa/rewritegeneric.go
+++ b/src/cmd/compile/internal/ssa/rewritegeneric.go
@@ -66,6 +66,8 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
return rewriteValuegeneric_OpEq64(v, config)
case OpEq8:
return rewriteValuegeneric_OpEq8(v, config)
+ case OpEqB:
+ return rewriteValuegeneric_OpEqB(v, config)
case OpEqInter:
return rewriteValuegeneric_OpEqInter(v, config)
case OpEqPtr:
@@ -218,6 +220,8 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
return rewriteValuegeneric_OpNeq64(v, config)
case OpNeq8:
return rewriteValuegeneric_OpNeq8(v, config)
+ case OpNeqB:
+ return rewriteValuegeneric_OpNeqB(v, config)
case OpNeqInter:
return rewriteValuegeneric_OpNeqInter(v, config)
case OpNeqPtr:
@@ -732,6 +736,78 @@ func rewriteValuegeneric_OpAnd16(v *Value, config *Config) bool {
v.AuxInt = 0
return true
}
+ // match: (And16 x (And16 x y))
+ // cond:
+ // result: (And16 x y)
+ for {
+ x := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAnd16 {
+ break
+ }
+ if x != v_1.Args[0] {
+ break
+ }
+ y := v_1.Args[1]
+ v.reset(OpAnd16)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ // match: (And16 x (And16 y x))
+ // cond:
+ // result: (And16 x y)
+ for {
+ x := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAnd16 {
+ break
+ }
+ y := v_1.Args[0]
+ if x != v_1.Args[1] {
+ break
+ }
+ v.reset(OpAnd16)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ // match: (And16 (And16 x y) x)
+ // cond:
+ // result: (And16 x y)
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpAnd16 {
+ break
+ }
+ x := v_0.Args[0]
+ y := v_0.Args[1]
+ if x != v.Args[1] {
+ break
+ }
+ v.reset(OpAnd16)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ // match: (And16 (And16 x y) y)
+ // cond:
+ // result: (And16 x y)
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpAnd16 {
+ break
+ }
+ x := v_0.Args[0]
+ y := v_0.Args[1]
+ if y != v.Args[1] {
+ break
+ }
+ v.reset(OpAnd16)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
return false
}
func rewriteValuegeneric_OpAnd32(v *Value, config *Config) bool {
@@ -803,6 +879,78 @@ func rewriteValuegeneric_OpAnd32(v *Value, config *Config) bool {
v.AuxInt = 0
return true
}
+ // match: (And32 x (And32 x y))
+ // cond:
+ // result: (And32 x y)
+ for {
+ x := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAnd32 {
+ break
+ }
+ if x != v_1.Args[0] {
+ break
+ }
+ y := v_1.Args[1]
+ v.reset(OpAnd32)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ // match: (And32 x (And32 y x))
+ // cond:
+ // result: (And32 x y)
+ for {
+ x := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAnd32 {
+ break
+ }
+ y := v_1.Args[0]
+ if x != v_1.Args[1] {
+ break
+ }
+ v.reset(OpAnd32)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ // match: (And32 (And32 x y) x)
+ // cond:
+ // result: (And32 x y)
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpAnd32 {
+ break
+ }
+ x := v_0.Args[0]
+ y := v_0.Args[1]
+ if x != v.Args[1] {
+ break
+ }
+ v.reset(OpAnd32)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ // match: (And32 (And32 x y) y)
+ // cond:
+ // result: (And32 x y)
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpAnd32 {
+ break
+ }
+ x := v_0.Args[0]
+ y := v_0.Args[1]
+ if y != v.Args[1] {
+ break
+ }
+ v.reset(OpAnd32)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
return false
}
func rewriteValuegeneric_OpAnd64(v *Value, config *Config) bool {
@@ -874,6 +1022,78 @@ func rewriteValuegeneric_OpAnd64(v *Value, config *Config) bool {
v.AuxInt = 0
return true
}
+ // match: (And64 x (And64 x y))
+ // cond:
+ // result: (And64 x y)
+ for {
+ x := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAnd64 {
+ break
+ }
+ if x != v_1.Args[0] {
+ break
+ }
+ y := v_1.Args[1]
+ v.reset(OpAnd64)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ // match: (And64 x (And64 y x))
+ // cond:
+ // result: (And64 x y)
+ for {
+ x := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAnd64 {
+ break
+ }
+ y := v_1.Args[0]
+ if x != v_1.Args[1] {
+ break
+ }
+ v.reset(OpAnd64)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ // match: (And64 (And64 x y) x)
+ // cond:
+ // result: (And64 x y)
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpAnd64 {
+ break
+ }
+ x := v_0.Args[0]
+ y := v_0.Args[1]
+ if x != v.Args[1] {
+ break
+ }
+ v.reset(OpAnd64)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ // match: (And64 (And64 x y) y)
+ // cond:
+ // result: (And64 x y)
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpAnd64 {
+ break
+ }
+ x := v_0.Args[0]
+ y := v_0.Args[1]
+ if y != v.Args[1] {
+ break
+ }
+ v.reset(OpAnd64)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
// match: (And64 <t> (Const64 [y]) x)
// cond: nlz(y) + nto(y) == 64 && nto(y) >= 32
// result: (Rsh64Ux64 (Lsh64x64 <t> x (Const64 <t> [nlz(y)])) (Const64 <t> [nlz(y)]))
@@ -997,6 +1217,78 @@ func rewriteValuegeneric_OpAnd8(v *Value, config *Config) bool {
v.AuxInt = 0
return true
}
+ // match: (And8 x (And8 x y))
+ // cond:
+ // result: (And8 x y)
+ for {
+ x := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAnd8 {
+ break
+ }
+ if x != v_1.Args[0] {
+ break
+ }
+ y := v_1.Args[1]
+ v.reset(OpAnd8)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ // match: (And8 x (And8 y x))
+ // cond:
+ // result: (And8 x y)
+ for {
+ x := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAnd8 {
+ break
+ }
+ y := v_1.Args[0]
+ if x != v_1.Args[1] {
+ break
+ }
+ v.reset(OpAnd8)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ // match: (And8 (And8 x y) x)
+ // cond:
+ // result: (And8 x y)
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpAnd8 {
+ break
+ }
+ x := v_0.Args[0]
+ y := v_0.Args[1]
+ if x != v.Args[1] {
+ break
+ }
+ v.reset(OpAnd8)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ // match: (And8 (And8 x y) y)
+ // cond:
+ // result: (And8 x y)
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpAnd8 {
+ break
+ }
+ x := v_0.Args[0]
+ y := v_0.Args[1]
+ if y != v.Args[1] {
+ break
+ }
+ v.reset(OpAnd8)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
return false
}
func rewriteValuegeneric_OpArg(v *Value, config *Config) bool {
@@ -2060,57 +2352,6 @@ func rewriteValuegeneric_OpEq8(v *Value, config *Config) bool {
v.AuxInt = 1
return true
}
- // match: (Eq8 (ConstBool [c]) (ConstBool [d]))
- // cond:
- // result: (ConstBool [b2i(c == d)])
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpConstBool {
- break
- }
- c := v_0.AuxInt
- v_1 := v.Args[1]
- if v_1.Op != OpConstBool {
- break
- }
- d := v_1.AuxInt
- v.reset(OpConstBool)
- v.AuxInt = b2i(c == d)
- return true
- }
- // match: (Eq8 (ConstBool [0]) x)
- // cond:
- // result: (Not x)
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpConstBool {
- break
- }
- if v_0.AuxInt != 0 {
- break
- }
- x := v.Args[1]
- v.reset(OpNot)
- v.AddArg(x)
- return true
- }
- // match: (Eq8 (ConstBool [1]) x)
- // cond:
- // result: x
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpConstBool {
- break
- }
- if v_0.AuxInt != 1 {
- break
- }
- x := v.Args[1]
- v.reset(OpCopy)
- v.Type = x.Type
- v.AddArg(x)
- return true
- }
// match: (Eq8 (Const8 <t> [c]) (Add8 (Const8 <t> [d]) x))
// cond:
// result: (Eq8 (Const8 <t> [int64(int8(c-d))]) x)
@@ -2162,38 +2403,40 @@ func rewriteValuegeneric_OpEq8(v *Value, config *Config) bool {
v.AddArg(x)
return true
}
- // match: (Eq8 x (ConstBool <t> [c]))
- // cond: x.Op != OpConstBool
- // result: (Eq8 (ConstBool <t> [c]) x)
+ // match: (Eq8 (Const8 [c]) (Const8 [d]))
+ // cond:
+ // result: (ConstBool [b2i(c == d)])
for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpConstBool {
+ v_0 := v.Args[0]
+ if v_0.Op != OpConst8 {
break
}
- t := v_1.Type
- c := v_1.AuxInt
- if !(x.Op != OpConstBool) {
+ c := v_0.AuxInt
+ v_1 := v.Args[1]
+ if v_1.Op != OpConst8 {
break
}
- v.reset(OpEq8)
- v0 := b.NewValue0(v.Line, OpConstBool, t)
- v0.AuxInt = c
- v.AddArg(v0)
- v.AddArg(x)
+ d := v_1.AuxInt
+ v.reset(OpConstBool)
+ v.AuxInt = b2i(c == d)
return true
}
- // match: (Eq8 (Const8 [c]) (Const8 [d]))
+ return false
+}
+func rewriteValuegeneric_OpEqB(v *Value, config *Config) bool {
+ b := v.Block
+ _ = b
+ // match: (EqB (ConstBool [c]) (ConstBool [d]))
// cond:
// result: (ConstBool [b2i(c == d)])
for {
v_0 := v.Args[0]
- if v_0.Op != OpConst8 {
+ if v_0.Op != OpConstBool {
break
}
c := v_0.AuxInt
v_1 := v.Args[1]
- if v_1.Op != OpConst8 {
+ if v_1.Op != OpConstBool {
break
}
d := v_1.AuxInt
@@ -2201,6 +2444,39 @@ func rewriteValuegeneric_OpEq8(v *Value, config *Config) bool {
v.AuxInt = b2i(c == d)
return true
}
+ // match: (EqB (ConstBool [0]) x)
+ // cond:
+ // result: (Not x)
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpConstBool {
+ break
+ }
+ if v_0.AuxInt != 0 {
+ break
+ }
+ x := v.Args[1]
+ v.reset(OpNot)
+ v.AddArg(x)
+ return true
+ }
+ // match: (EqB (ConstBool [1]) x)
+ // cond:
+ // result: x
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpConstBool {
+ break
+ }
+ if v_0.AuxInt != 1 {
+ break
+ }
+ x := v.Args[1]
+ v.reset(OpCopy)
+ v.Type = x.Type
+ v.AddArg(x)
+ return true
+ }
return false
}
func rewriteValuegeneric_OpEqInter(v *Value, config *Config) bool {
@@ -5419,57 +5695,6 @@ func rewriteValuegeneric_OpNeq8(v *Value, config *Config) bool {
v.AuxInt = 0
return true
}
- // match: (Neq8 (ConstBool [c]) (ConstBool [d]))
- // cond:
- // result: (ConstBool [b2i(c != d)])
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpConstBool {
- break
- }
- c := v_0.AuxInt
- v_1 := v.Args[1]
- if v_1.Op != OpConstBool {
- break
- }
- d := v_1.AuxInt
- v.reset(OpConstBool)
- v.AuxInt = b2i(c != d)
- return true
- }
- // match: (Neq8 (ConstBool [0]) x)
- // cond:
- // result: x
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpConstBool {
- break
- }
- if v_0.AuxInt != 0 {
- break
- }
- x := v.Args[1]
- v.reset(OpCopy)
- v.Type = x.Type
- v.AddArg(x)
- return true
- }
- // match: (Neq8 (ConstBool [1]) x)
- // cond:
- // result: (Not x)
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpConstBool {
- break
- }
- if v_0.AuxInt != 1 {
- break
- }
- x := v.Args[1]
- v.reset(OpNot)
- v.AddArg(x)
- return true
- }
// match: (Neq8 (Const8 <t> [c]) (Add8 (Const8 <t> [d]) x))
// cond:
// result: (Neq8 (Const8 <t> [int64(int8(c-d))]) x)
@@ -5521,38 +5746,40 @@ func rewriteValuegeneric_OpNeq8(v *Value, config *Config) bool {
v.AddArg(x)
return true
}
- // match: (Neq8 x (ConstBool <t> [c]))
- // cond: x.Op != OpConstBool
- // result: (Neq8 (ConstBool <t> [c]) x)
+ // match: (Neq8 (Const8 [c]) (Const8 [d]))
+ // cond:
+ // result: (ConstBool [b2i(c != d)])
for {
- x := v.Args[0]
- v_1 := v.Args[1]
- if v_1.Op != OpConstBool {
+ v_0 := v.Args[0]
+ if v_0.Op != OpConst8 {
break
}
- t := v_1.Type
- c := v_1.AuxInt
- if !(x.Op != OpConstBool) {
+ c := v_0.AuxInt
+ v_1 := v.Args[1]
+ if v_1.Op != OpConst8 {
break
}
- v.reset(OpNeq8)
- v0 := b.NewValue0(v.Line, OpConstBool, t)
- v0.AuxInt = c
- v.AddArg(v0)
- v.AddArg(x)
+ d := v_1.AuxInt
+ v.reset(OpConstBool)
+ v.AuxInt = b2i(c != d)
return true
}
- // match: (Neq8 (Const8 [c]) (Const8 [d]))
+ return false
+}
+func rewriteValuegeneric_OpNeqB(v *Value, config *Config) bool {
+ b := v.Block
+ _ = b
+ // match: (NeqB (ConstBool [c]) (ConstBool [d]))
// cond:
// result: (ConstBool [b2i(c != d)])
for {
v_0 := v.Args[0]
- if v_0.Op != OpConst8 {
+ if v_0.Op != OpConstBool {
break
}
c := v_0.AuxInt
v_1 := v.Args[1]
- if v_1.Op != OpConst8 {
+ if v_1.Op != OpConstBool {
break
}
d := v_1.AuxInt
@@ -5560,6 +5787,39 @@ func rewriteValuegeneric_OpNeq8(v *Value, config *Config) bool {
v.AuxInt = b2i(c != d)
return true
}
+ // match: (NeqB (ConstBool [0]) x)
+ // cond:
+ // result: x
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpConstBool {
+ break
+ }
+ if v_0.AuxInt != 0 {
+ break
+ }
+ x := v.Args[1]
+ v.reset(OpCopy)
+ v.Type = x.Type
+ v.AddArg(x)
+ return true
+ }
+ // match: (NeqB (ConstBool [1]) x)
+ // cond:
+ // result: (Not x)
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpConstBool {
+ break
+ }
+ if v_0.AuxInt != 1 {
+ break
+ }
+ x := v.Args[1]
+ v.reset(OpNot)
+ v.AddArg(x)
+ return true
+ }
return false
}
func rewriteValuegeneric_OpNeqInter(v *Value, config *Config) bool {
@@ -5739,6 +5999,78 @@ func rewriteValuegeneric_OpOr16(v *Value, config *Config) bool {
v.AuxInt = -1
return true
}
+ // match: (Or16 x (Or16 x y))
+ // cond:
+ // result: (Or16 x y)
+ for {
+ x := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpOr16 {
+ break
+ }
+ if x != v_1.Args[0] {
+ break
+ }
+ y := v_1.Args[1]
+ v.reset(OpOr16)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ // match: (Or16 x (Or16 y x))
+ // cond:
+ // result: (Or16 x y)
+ for {
+ x := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpOr16 {
+ break
+ }
+ y := v_1.Args[0]
+ if x != v_1.Args[1] {
+ break
+ }
+ v.reset(OpOr16)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ // match: (Or16 (Or16 x y) x)
+ // cond:
+ // result: (Or16 x y)
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpOr16 {
+ break
+ }
+ x := v_0.Args[0]
+ y := v_0.Args[1]
+ if x != v.Args[1] {
+ break
+ }
+ v.reset(OpOr16)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ // match: (Or16 (Or16 x y) y)
+ // cond:
+ // result: (Or16 x y)
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpOr16 {
+ break
+ }
+ x := v_0.Args[0]
+ y := v_0.Args[1]
+ if y != v.Args[1] {
+ break
+ }
+ v.reset(OpOr16)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
return false
}
func rewriteValuegeneric_OpOr32(v *Value, config *Config) bool {
@@ -5810,6 +6142,78 @@ func rewriteValuegeneric_OpOr32(v *Value, config *Config) bool {
v.AuxInt = -1
return true
}
+ // match: (Or32 x (Or32 x y))
+ // cond:
+ // result: (Or32 x y)
+ for {
+ x := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpOr32 {
+ break
+ }
+ if x != v_1.Args[0] {
+ break
+ }
+ y := v_1.Args[1]
+ v.reset(OpOr32)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ // match: (Or32 x (Or32 y x))
+ // cond:
+ // result: (Or32 x y)
+ for {
+ x := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpOr32 {
+ break
+ }
+ y := v_1.Args[0]
+ if x != v_1.Args[1] {
+ break
+ }
+ v.reset(OpOr32)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ // match: (Or32 (Or32 x y) x)
+ // cond:
+ // result: (Or32 x y)
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpOr32 {
+ break
+ }
+ x := v_0.Args[0]
+ y := v_0.Args[1]
+ if x != v.Args[1] {
+ break
+ }
+ v.reset(OpOr32)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ // match: (Or32 (Or32 x y) y)
+ // cond:
+ // result: (Or32 x y)
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpOr32 {
+ break
+ }
+ x := v_0.Args[0]
+ y := v_0.Args[1]
+ if y != v.Args[1] {
+ break
+ }
+ v.reset(OpOr32)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
return false
}
func rewriteValuegeneric_OpOr64(v *Value, config *Config) bool {
@@ -5881,6 +6285,78 @@ func rewriteValuegeneric_OpOr64(v *Value, config *Config) bool {
v.AuxInt = -1
return true
}
+ // match: (Or64 x (Or64 x y))
+ // cond:
+ // result: (Or64 x y)
+ for {
+ x := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpOr64 {
+ break
+ }
+ if x != v_1.Args[0] {
+ break
+ }
+ y := v_1.Args[1]
+ v.reset(OpOr64)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ // match: (Or64 x (Or64 y x))
+ // cond:
+ // result: (Or64 x y)
+ for {
+ x := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpOr64 {
+ break
+ }
+ y := v_1.Args[0]
+ if x != v_1.Args[1] {
+ break
+ }
+ v.reset(OpOr64)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ // match: (Or64 (Or64 x y) x)
+ // cond:
+ // result: (Or64 x y)
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpOr64 {
+ break
+ }
+ x := v_0.Args[0]
+ y := v_0.Args[1]
+ if x != v.Args[1] {
+ break
+ }
+ v.reset(OpOr64)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ // match: (Or64 (Or64 x y) y)
+ // cond:
+ // result: (Or64 x y)
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpOr64 {
+ break
+ }
+ x := v_0.Args[0]
+ y := v_0.Args[1]
+ if y != v.Args[1] {
+ break
+ }
+ v.reset(OpOr64)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
return false
}
func rewriteValuegeneric_OpOr8(v *Value, config *Config) bool {
@@ -5952,6 +6428,78 @@ func rewriteValuegeneric_OpOr8(v *Value, config *Config) bool {
v.AuxInt = -1
return true
}
+ // match: (Or8 x (Or8 x y))
+ // cond:
+ // result: (Or8 x y)
+ for {
+ x := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpOr8 {
+ break
+ }
+ if x != v_1.Args[0] {
+ break
+ }
+ y := v_1.Args[1]
+ v.reset(OpOr8)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ // match: (Or8 x (Or8 y x))
+ // cond:
+ // result: (Or8 x y)
+ for {
+ x := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpOr8 {
+ break
+ }
+ y := v_1.Args[0]
+ if x != v_1.Args[1] {
+ break
+ }
+ v.reset(OpOr8)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ // match: (Or8 (Or8 x y) x)
+ // cond:
+ // result: (Or8 x y)
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpOr8 {
+ break
+ }
+ x := v_0.Args[0]
+ y := v_0.Args[1]
+ if x != v.Args[1] {
+ break
+ }
+ v.reset(OpOr8)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ // match: (Or8 (Or8 x y) y)
+ // cond:
+ // result: (Or8 x y)
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpOr8 {
+ break
+ }
+ x := v_0.Args[0]
+ y := v_0.Args[1]
+ if y != v.Args[1] {
+ break
+ }
+ v.reset(OpOr8)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
return false
}
func rewriteValuegeneric_OpPhi(v *Value, config *Config) bool {
@@ -6185,7 +6733,7 @@ func rewriteValuegeneric_OpRsh16Ux64(v *Value, config *Config) bool {
_ = b
// match: (Rsh16Ux64 (Const16 [c]) (Const64 [d]))
// cond:
- // result: (Const16 [int64(uint16(c) >> uint64(d))])
+ // result: (Const16 [int64(int16(uint16(c) >> uint64(d)))])
for {
v_0 := v.Args[0]
if v_0.Op != OpConst16 {
@@ -6198,7 +6746,7 @@ func rewriteValuegeneric_OpRsh16Ux64(v *Value, config *Config) bool {
}
d := v_1.AuxInt
v.reset(OpConst16)
- v.AuxInt = int64(uint16(c) >> uint64(d))
+ v.AuxInt = int64(int16(uint16(c) >> uint64(d)))
return true
}
// match: (Rsh16Ux64 (Const16 [0]) _)
@@ -6547,7 +7095,7 @@ func rewriteValuegeneric_OpRsh32Ux64(v *Value, config *Config) bool {
_ = b
// match: (Rsh32Ux64 (Const32 [c]) (Const64 [d]))
// cond:
- // result: (Const32 [int64(uint32(c) >> uint64(d))])
+ // result: (Const32 [int64(int32(uint32(c) >> uint64(d)))])
for {
v_0 := v.Args[0]
if v_0.Op != OpConst32 {
@@ -6560,7 +7108,7 @@ func rewriteValuegeneric_OpRsh32Ux64(v *Value, config *Config) bool {
}
d := v_1.AuxInt
v.reset(OpConst32)
- v.AuxInt = int64(uint32(c) >> uint64(d))
+ v.AuxInt = int64(int32(uint32(c) >> uint64(d)))
return true
}
// match: (Rsh32Ux64 (Const32 [0]) _)
@@ -7353,7 +7901,7 @@ func rewriteValuegeneric_OpRsh8Ux64(v *Value, config *Config) bool {
_ = b
// match: (Rsh8Ux64 (Const8 [c]) (Const64 [d]))
// cond:
- // result: (Const8 [int64(uint8(c) >> uint64(d))])
+ // result: (Const8 [int64(int8(uint8(c) >> uint64(d)))])
for {
v_0 := v.Args[0]
if v_0.Op != OpConst8 {
@@ -7366,7 +7914,7 @@ func rewriteValuegeneric_OpRsh8Ux64(v *Value, config *Config) bool {
}
d := v_1.AuxInt
v.reset(OpConst8)
- v.AuxInt = int64(uint8(c) >> uint64(d))
+ v.AuxInt = int64(int8(uint8(c) >> uint64(d)))
return true
}
// match: (Rsh8Ux64 (Const8 [0]) _)
@@ -8941,6 +9489,78 @@ func rewriteValuegeneric_OpXor16(v *Value, config *Config) bool {
v.AddArg(x)
return true
}
+ // match: (Xor16 x (Xor16 x y))
+ // cond:
+ // result: y
+ for {
+ x := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpXor16 {
+ break
+ }
+ if x != v_1.Args[0] {
+ break
+ }
+ y := v_1.Args[1]
+ v.reset(OpCopy)
+ v.Type = y.Type
+ v.AddArg(y)
+ return true
+ }
+ // match: (Xor16 x (Xor16 y x))
+ // cond:
+ // result: y
+ for {
+ x := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpXor16 {
+ break
+ }
+ y := v_1.Args[0]
+ if x != v_1.Args[1] {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = y.Type
+ v.AddArg(y)
+ return true
+ }
+ // match: (Xor16 (Xor16 x y) x)
+ // cond:
+ // result: y
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpXor16 {
+ break
+ }
+ x := v_0.Args[0]
+ y := v_0.Args[1]
+ if x != v.Args[1] {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = y.Type
+ v.AddArg(y)
+ return true
+ }
+ // match: (Xor16 (Xor16 x y) y)
+ // cond:
+ // result: x
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpXor16 {
+ break
+ }
+ x := v_0.Args[0]
+ y := v_0.Args[1]
+ if y != v.Args[1] {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = x.Type
+ v.AddArg(x)
+ return true
+ }
return false
}
func rewriteValuegeneric_OpXor32(v *Value, config *Config) bool {
@@ -8996,6 +9616,78 @@ func rewriteValuegeneric_OpXor32(v *Value, config *Config) bool {
v.AddArg(x)
return true
}
+ // match: (Xor32 x (Xor32 x y))
+ // cond:
+ // result: y
+ for {
+ x := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpXor32 {
+ break
+ }
+ if x != v_1.Args[0] {
+ break
+ }
+ y := v_1.Args[1]
+ v.reset(OpCopy)
+ v.Type = y.Type
+ v.AddArg(y)
+ return true
+ }
+ // match: (Xor32 x (Xor32 y x))
+ // cond:
+ // result: y
+ for {
+ x := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpXor32 {
+ break
+ }
+ y := v_1.Args[0]
+ if x != v_1.Args[1] {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = y.Type
+ v.AddArg(y)
+ return true
+ }
+ // match: (Xor32 (Xor32 x y) x)
+ // cond:
+ // result: y
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpXor32 {
+ break
+ }
+ x := v_0.Args[0]
+ y := v_0.Args[1]
+ if x != v.Args[1] {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = y.Type
+ v.AddArg(y)
+ return true
+ }
+ // match: (Xor32 (Xor32 x y) y)
+ // cond:
+ // result: x
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpXor32 {
+ break
+ }
+ x := v_0.Args[0]
+ y := v_0.Args[1]
+ if y != v.Args[1] {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = x.Type
+ v.AddArg(x)
+ return true
+ }
return false
}
func rewriteValuegeneric_OpXor64(v *Value, config *Config) bool {
@@ -9051,6 +9743,78 @@ func rewriteValuegeneric_OpXor64(v *Value, config *Config) bool {
v.AddArg(x)
return true
}
+ // match: (Xor64 x (Xor64 x y))
+ // cond:
+ // result: y
+ for {
+ x := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpXor64 {
+ break
+ }
+ if x != v_1.Args[0] {
+ break
+ }
+ y := v_1.Args[1]
+ v.reset(OpCopy)
+ v.Type = y.Type
+ v.AddArg(y)
+ return true
+ }
+ // match: (Xor64 x (Xor64 y x))
+ // cond:
+ // result: y
+ for {
+ x := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpXor64 {
+ break
+ }
+ y := v_1.Args[0]
+ if x != v_1.Args[1] {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = y.Type
+ v.AddArg(y)
+ return true
+ }
+ // match: (Xor64 (Xor64 x y) x)
+ // cond:
+ // result: y
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpXor64 {
+ break
+ }
+ x := v_0.Args[0]
+ y := v_0.Args[1]
+ if x != v.Args[1] {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = y.Type
+ v.AddArg(y)
+ return true
+ }
+ // match: (Xor64 (Xor64 x y) y)
+ // cond:
+ // result: x
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpXor64 {
+ break
+ }
+ x := v_0.Args[0]
+ y := v_0.Args[1]
+ if y != v.Args[1] {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = x.Type
+ v.AddArg(x)
+ return true
+ }
return false
}
func rewriteValuegeneric_OpXor8(v *Value, config *Config) bool {
@@ -9106,6 +9870,78 @@ func rewriteValuegeneric_OpXor8(v *Value, config *Config) bool {
v.AddArg(x)
return true
}
+ // match: (Xor8 x (Xor8 x y))
+ // cond:
+ // result: y
+ for {
+ x := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpXor8 {
+ break
+ }
+ if x != v_1.Args[0] {
+ break
+ }
+ y := v_1.Args[1]
+ v.reset(OpCopy)
+ v.Type = y.Type
+ v.AddArg(y)
+ return true
+ }
+ // match: (Xor8 x (Xor8 y x))
+ // cond:
+ // result: y
+ for {
+ x := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpXor8 {
+ break
+ }
+ y := v_1.Args[0]
+ if x != v_1.Args[1] {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = y.Type
+ v.AddArg(y)
+ return true
+ }
+ // match: (Xor8 (Xor8 x y) x)
+ // cond:
+ // result: y
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpXor8 {
+ break
+ }
+ x := v_0.Args[0]
+ y := v_0.Args[1]
+ if x != v.Args[1] {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = y.Type
+ v.AddArg(y)
+ return true
+ }
+ // match: (Xor8 (Xor8 x y) y)
+ // cond:
+ // result: x
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpXor8 {
+ break
+ }
+ x := v_0.Args[0]
+ y := v_0.Args[1]
+ if y != v.Args[1] {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = x.Type
+ v.AddArg(x)
+ return true
+ }
return false
}
func rewriteBlockgeneric(b *Block) bool {
diff --git a/src/cmd/compile/internal/ssa/sparsemap.go b/src/cmd/compile/internal/ssa/sparsemap.go
index 6c0043b230..0211a70f09 100644
--- a/src/cmd/compile/internal/ssa/sparsemap.go
+++ b/src/cmd/compile/internal/ssa/sparsemap.go
@@ -32,6 +32,8 @@ func (s *sparseMap) contains(k ID) bool {
return i < len(s.dense) && s.dense[i].key == k
}
+// get returns the value for key k, or -1 if k does
+// not appear in the map.
func (s *sparseMap) get(k ID) int32 {
i := s.sparse[k]
if i < len(s.dense) && s.dense[i].key == k {
@@ -50,6 +52,20 @@ func (s *sparseMap) set(k ID, v int32) {
s.sparse[k] = len(s.dense) - 1
}
+// setBit sets the v'th bit of k's value, where 0 <= v < 32
+func (s *sparseMap) setBit(k ID, v uint) {
+ if v >= 32 {
+ panic("bit index too large.")
+ }
+ i := s.sparse[k]
+ if i < len(s.dense) && s.dense[i].key == k {
+ s.dense[i].val |= 1 << v
+ return
+ }
+ s.dense = append(s.dense, sparseEntry{k, 1 << v})
+ s.sparse[k] = len(s.dense) - 1
+}
+
func (s *sparseMap) remove(k ID) {
i := s.sparse[k]
if i < len(s.dense) && s.dense[i].key == k {
diff --git a/src/cmd/compile/internal/ssa/sparsetree.go b/src/cmd/compile/internal/ssa/sparsetree.go
index cae91e7ddb..45c7897496 100644
--- a/src/cmd/compile/internal/ssa/sparsetree.go
+++ b/src/cmd/compile/internal/ssa/sparsetree.go
@@ -116,6 +116,9 @@ func (t sparseTree) Child(x *Block) *Block {
// isAncestorEq reports whether x is an ancestor of or equal to y.
func (t sparseTree) isAncestorEq(x, y *Block) bool {
+ if x == y {
+ return true
+ }
xx := &t[x.ID]
yy := &t[y.ID]
return xx.entry <= yy.entry && yy.exit <= xx.exit
@@ -123,7 +126,16 @@ func (t sparseTree) isAncestorEq(x, y *Block) bool {
// isAncestor reports whether x is a strict ancestor of y.
func (t sparseTree) isAncestor(x, y *Block) bool {
+ if x == y {
+ return false
+ }
xx := &t[x.ID]
yy := &t[y.ID]
return xx.entry < yy.entry && yy.exit < xx.exit
}
+
+// maxdomorder returns a value to allow a maximal dominator first sort. maxdomorder(x) < maxdomorder(y) is true
+// if x may dominate y, and false if x cannot dominate y.
+func (t sparseTree) maxdomorder(x *Block) int32 {
+ return t[x.ID].entry
+}
diff --git a/src/cmd/compile/internal/ssa/stackalloc.go b/src/cmd/compile/internal/ssa/stackalloc.go
index 1de22dc96e..44f4096cb2 100644
--- a/src/cmd/compile/internal/ssa/stackalloc.go
+++ b/src/cmd/compile/internal/ssa/stackalloc.go
@@ -22,6 +22,13 @@ type stackAllocState struct {
names []LocalSlot
slots []int
used []bool
+
+ nArgSlot, // Number of Values sourced to arg slot
+ nNotNeed, // Number of Values not needing a stack slot
+ nNamedSlot, // Number of Values using a named stack slot
+ nReuse, // Number of values reusing a stack slot
+ nAuto, // Number of autos allocated for stack slots.
+ nSelfInterfere int32 // Number of self-interferences
}
func newStackAllocState(f *Func) *stackAllocState {
@@ -54,6 +61,7 @@ func putStackAllocState(s *stackAllocState) {
s.f.Config.stackAllocState = s
s.f = nil
s.live = nil
+ s.nArgSlot, s.nNotNeed, s.nNamedSlot, s.nReuse, s.nAuto, s.nSelfInterfere = 0, 0, 0, 0, 0, 0
}
type stackValState struct {
@@ -75,6 +83,13 @@ func stackalloc(f *Func, spillLive [][]ID) [][]ID {
defer putStackAllocState(s)
s.stackalloc()
+ if f.pass.stats > 0 {
+ f.logStat("stack_alloc_stats",
+ s.nArgSlot, "arg_slots", s.nNotNeed, "slot_not_needed",
+ s.nNamedSlot, "named_slots", s.nAuto, "auto_slots",
+ s.nReuse, "reused_slots", s.nSelfInterfere, "self_interfering")
+ }
+
return s.live
}
@@ -170,9 +185,11 @@ func (s *stackAllocState) stackalloc() {
for _, b := range f.Blocks {
for _, v := range b.Values {
if !s.values[v.ID].needSlot {
+ s.nNotNeed++
continue
}
if v.Op == OpArg {
+ s.nArgSlot++
continue // already picked
}
@@ -184,18 +201,20 @@ func (s *stackAllocState) stackalloc() {
} else {
name = names[v.ID]
}
- if name.N != nil && v.Type.Equal(name.Type) {
+ if name.N != nil && v.Type.Compare(name.Type) == CMPeq {
for _, id := range s.interfere[v.ID] {
h := f.getHome(id)
if h != nil && h.(LocalSlot).N == name.N && h.(LocalSlot).Off == name.Off {
// A variable can interfere with itself.
// It is rare, but but it can happen.
+ s.nSelfInterfere++
goto noname
}
}
if f.pass.debug > stackDebug {
fmt.Printf("stackalloc %s to %s\n", v, name.Name())
}
+ s.nNamedSlot++
f.setHome(v, name)
continue
}
@@ -217,11 +236,13 @@ func (s *stackAllocState) stackalloc() {
var i int
for i = 0; i < len(locs); i++ {
if !used[i] {
+ s.nReuse++
break
}
}
// If there is no unused stack slot, allocate a new one.
if i == len(locs) {
+ s.nAuto++
locs = append(locs, LocalSlot{N: f.Config.fe.Auto(v.Type), Type: v.Type, Off: 0})
locations[v.Type] = locs
}
@@ -351,7 +372,7 @@ func (s *stackAllocState) buildInterferenceGraph() {
if s.values[v.ID].needSlot {
live.remove(v.ID)
for _, id := range live.contents() {
- if s.values[v.ID].typ.Equal(s.values[id].typ) {
+ if s.values[v.ID].typ.Compare(s.values[id].typ) == CMPeq {
s.interfere[v.ID] = append(s.interfere[v.ID], id)
s.interfere[id] = append(s.interfere[id], v.ID)
}
diff --git a/src/cmd/compile/internal/ssa/type.go b/src/cmd/compile/internal/ssa/type.go
index 9643b07556..91a4efe78f 100644
--- a/src/cmd/compile/internal/ssa/type.go
+++ b/src/cmd/compile/internal/ssa/type.go
@@ -31,16 +31,16 @@ type Type interface {
ElemType() Type // given []T or *T or [n]T, return T
PtrTo() Type // given T, return *T
- NumFields() int // # of fields of a struct
- FieldType(i int) Type // type of ith field of the struct
- FieldOff(i int) int64 // offset of ith field of the struct
+ NumFields() int // # of fields of a struct
+ FieldType(i int) Type // type of ith field of the struct
+ FieldOff(i int) int64 // offset of ith field of the struct
+ FieldName(i int) string // name of ith field of the struct
NumElem() int64 // # of elements of an array
String() string
SimpleString() string // a coarser generic description of T, e.g. T's underlying type
- Equal(Type) bool
- Compare(Type) Cmp // compare types, returning one of CMPlt, CMPeq, CMPgt.
+ Compare(Type) Cmp // compare types, returning one of CMPlt, CMPeq, CMPgt.
}
// Special compiler-only types.
@@ -53,30 +53,31 @@ type CompilerType struct {
Int128 bool
}
-func (t *CompilerType) Size() int64 { return t.size } // Size in bytes
-func (t *CompilerType) Alignment() int64 { return 0 }
-func (t *CompilerType) IsBoolean() bool { return false }
-func (t *CompilerType) IsInteger() bool { return false }
-func (t *CompilerType) IsSigned() bool { return false }
-func (t *CompilerType) IsFloat() bool { return false }
-func (t *CompilerType) IsComplex() bool { return false }
-func (t *CompilerType) IsPtrShaped() bool { return false }
-func (t *CompilerType) IsString() bool { return false }
-func (t *CompilerType) IsSlice() bool { return false }
-func (t *CompilerType) IsArray() bool { return false }
-func (t *CompilerType) IsStruct() bool { return false }
-func (t *CompilerType) IsInterface() bool { return false }
-func (t *CompilerType) IsMemory() bool { return t.Memory }
-func (t *CompilerType) IsFlags() bool { return t.Flags }
-func (t *CompilerType) IsVoid() bool { return t.Void }
-func (t *CompilerType) String() string { return t.Name }
-func (t *CompilerType) SimpleString() string { return t.Name }
-func (t *CompilerType) ElemType() Type { panic("not implemented") }
-func (t *CompilerType) PtrTo() Type { panic("not implemented") }
-func (t *CompilerType) NumFields() int { panic("not implemented") }
-func (t *CompilerType) FieldType(i int) Type { panic("not implemented") }
-func (t *CompilerType) FieldOff(i int) int64 { panic("not implemented") }
-func (t *CompilerType) NumElem() int64 { panic("not implemented") }
+func (t *CompilerType) Size() int64 { return t.size } // Size in bytes
+func (t *CompilerType) Alignment() int64 { return 0 }
+func (t *CompilerType) IsBoolean() bool { return false }
+func (t *CompilerType) IsInteger() bool { return false }
+func (t *CompilerType) IsSigned() bool { return false }
+func (t *CompilerType) IsFloat() bool { return false }
+func (t *CompilerType) IsComplex() bool { return false }
+func (t *CompilerType) IsPtrShaped() bool { return false }
+func (t *CompilerType) IsString() bool { return false }
+func (t *CompilerType) IsSlice() bool { return false }
+func (t *CompilerType) IsArray() bool { return false }
+func (t *CompilerType) IsStruct() bool { return false }
+func (t *CompilerType) IsInterface() bool { return false }
+func (t *CompilerType) IsMemory() bool { return t.Memory }
+func (t *CompilerType) IsFlags() bool { return t.Flags }
+func (t *CompilerType) IsVoid() bool { return t.Void }
+func (t *CompilerType) String() string { return t.Name }
+func (t *CompilerType) SimpleString() string { return t.Name }
+func (t *CompilerType) ElemType() Type { panic("not implemented") }
+func (t *CompilerType) PtrTo() Type { panic("not implemented") }
+func (t *CompilerType) NumFields() int { panic("not implemented") }
+func (t *CompilerType) FieldType(i int) Type { panic("not implemented") }
+func (t *CompilerType) FieldOff(i int) int64 { panic("not implemented") }
+func (t *CompilerType) FieldName(i int) string { panic("not implemented") }
+func (t *CompilerType) NumElem() int64 { panic("not implemented") }
// Cmp is a comparison between values a and b.
// -1 if a < b
@@ -115,14 +116,6 @@ func (t *CompilerType) Compare(u Type) Cmp {
return CMPlt
}
-func (t *CompilerType) Equal(u Type) bool {
- x, ok := u.(*CompilerType)
- if !ok {
- return false
- }
- return x == t
-}
-
var (
TypeInvalid = &CompilerType{Name: "invalid"}
TypeMem = &CompilerType{Name: "mem", Memory: true}
diff --git a/src/cmd/compile/internal/ssa/type_test.go b/src/cmd/compile/internal/ssa/type_test.go
index cd80abf03f..3b1a892083 100644
--- a/src/cmd/compile/internal/ssa/type_test.go
+++ b/src/cmd/compile/internal/ssa/type_test.go
@@ -24,30 +24,31 @@ type TypeImpl struct {
Name string
}
-func (t *TypeImpl) Size() int64 { return t.Size_ }
-func (t *TypeImpl) Alignment() int64 { return t.Align }
-func (t *TypeImpl) IsBoolean() bool { return t.Boolean }
-func (t *TypeImpl) IsInteger() bool { return t.Integer }
-func (t *TypeImpl) IsSigned() bool { return t.Signed }
-func (t *TypeImpl) IsFloat() bool { return t.Float }
-func (t *TypeImpl) IsComplex() bool { return t.Complex }
-func (t *TypeImpl) IsPtrShaped() bool { return t.Ptr }
-func (t *TypeImpl) IsString() bool { return t.string }
-func (t *TypeImpl) IsSlice() bool { return t.slice }
-func (t *TypeImpl) IsArray() bool { return t.array }
-func (t *TypeImpl) IsStruct() bool { return t.struct_ }
-func (t *TypeImpl) IsInterface() bool { return t.inter }
-func (t *TypeImpl) IsMemory() bool { return false }
-func (t *TypeImpl) IsFlags() bool { return false }
-func (t *TypeImpl) IsVoid() bool { return false }
-func (t *TypeImpl) String() string { return t.Name }
-func (t *TypeImpl) SimpleString() string { return t.Name }
-func (t *TypeImpl) ElemType() Type { return t.Elem_ }
-func (t *TypeImpl) PtrTo() Type { panic("not implemented") }
-func (t *TypeImpl) NumFields() int { panic("not implemented") }
-func (t *TypeImpl) FieldType(i int) Type { panic("not implemented") }
-func (t *TypeImpl) FieldOff(i int) int64 { panic("not implemented") }
-func (t *TypeImpl) NumElem() int64 { panic("not implemented") }
+func (t *TypeImpl) Size() int64 { return t.Size_ }
+func (t *TypeImpl) Alignment() int64 { return t.Align }
+func (t *TypeImpl) IsBoolean() bool { return t.Boolean }
+func (t *TypeImpl) IsInteger() bool { return t.Integer }
+func (t *TypeImpl) IsSigned() bool { return t.Signed }
+func (t *TypeImpl) IsFloat() bool { return t.Float }
+func (t *TypeImpl) IsComplex() bool { return t.Complex }
+func (t *TypeImpl) IsPtrShaped() bool { return t.Ptr }
+func (t *TypeImpl) IsString() bool { return t.string }
+func (t *TypeImpl) IsSlice() bool { return t.slice }
+func (t *TypeImpl) IsArray() bool { return t.array }
+func (t *TypeImpl) IsStruct() bool { return t.struct_ }
+func (t *TypeImpl) IsInterface() bool { return t.inter }
+func (t *TypeImpl) IsMemory() bool { return false }
+func (t *TypeImpl) IsFlags() bool { return false }
+func (t *TypeImpl) IsVoid() bool { return false }
+func (t *TypeImpl) String() string { return t.Name }
+func (t *TypeImpl) SimpleString() string { return t.Name }
+func (t *TypeImpl) ElemType() Type { return t.Elem_ }
+func (t *TypeImpl) PtrTo() Type { panic("not implemented") }
+func (t *TypeImpl) NumFields() int { panic("not implemented") }
+func (t *TypeImpl) FieldType(i int) Type { panic("not implemented") }
+func (t *TypeImpl) FieldOff(i int) int64 { panic("not implemented") }
+func (t *TypeImpl) FieldName(i int) string { panic("not implemented") }
+func (t *TypeImpl) NumElem() int64 { panic("not implemented") }
func (t *TypeImpl) Equal(u Type) bool {
x, ok := u.(*TypeImpl)
diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go
index fd4eb64db1..6c364ad932 100644
--- a/src/cmd/compile/internal/ssa/value.go
+++ b/src/cmd/compile/internal/ssa/value.go
@@ -185,6 +185,7 @@ func (v *Value) resetArgs() {
}
v.argstorage[0] = nil
v.argstorage[1] = nil
+ v.argstorage[2] = nil
v.Args = v.argstorage[:0]
}
diff --git a/src/cmd/compile/internal/x86/cgen64.go b/src/cmd/compile/internal/x86/cgen64.go
index 61e191f87c..ea52d6951a 100644
--- a/src/cmd/compile/internal/x86/cgen64.go
+++ b/src/cmd/compile/internal/x86/cgen64.go
@@ -19,12 +19,12 @@ 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.Fatalf("cgen64 %v of %v", gc.Oconv(n.Op, 0), gc.Oconv(res.Op, 0))
+ gc.Fatalf("cgen64 %v of %v", n.Op, res.Op)
}
switch n.Op {
default:
- gc.Fatalf("cgen64 %v", gc.Oconv(n.Op, 0))
+ gc.Fatalf("cgen64 %v", n.Op)
case gc.OMINUS:
gc.Cgen(n.Left, res)
@@ -531,7 +531,7 @@ func cmp64(nl *gc.Node, nr *gc.Node, op gc.Op, likely int, to *obj.Prog) {
var br *obj.Prog
switch op {
default:
- gc.Fatalf("cmp64 %v %v", gc.Oconv(op, 0), t)
+ gc.Fatalf("cmp64 %v %v", op, t)
// cmp hi
// jne L
diff --git a/src/cmd/compile/internal/x86/galign.go b/src/cmd/compile/internal/x86/galign.go
index 4ab72b6da7..738d887742 100644
--- a/src/cmd/compile/internal/x86/galign.go
+++ b/src/cmd/compile/internal/x86/galign.go
@@ -13,15 +13,10 @@ import (
)
func betypeinit() {
- gc.Widthptr = 4
- gc.Widthint = 4
- gc.Widthreg = 4
}
func Main() {
- gc.Thearch.Thechar = '8'
- gc.Thearch.Thestring = "386"
- gc.Thearch.Thelinkarch = &x86.Link386
+ gc.Thearch.LinkArch = &x86.Link386
gc.Thearch.REGSP = x86.REGSP
gc.Thearch.REGCTXT = x86.REGCTXT
gc.Thearch.REGCALLX = x86.REG_BX
diff --git a/src/cmd/compile/internal/x86/ggen.go b/src/cmd/compile/internal/x86/ggen.go
index 38c3f8fc0e..21d989c98d 100644
--- a/src/cmd/compile/internal/x86/ggen.go
+++ b/src/cmd/compile/internal/x86/ggen.go
@@ -661,7 +661,7 @@ func cgen_floatsse(n *gc.Node, res *gc.Node) {
switch n.Op {
default:
gc.Dump("cgen_floatsse", n)
- gc.Fatalf("cgen_floatsse %v", gc.Oconv(n.Op, 0))
+ gc.Fatalf("cgen_floatsse %v", n.Op)
return
case gc.OMINUS,
diff --git a/src/cmd/compile/internal/x86/gsubr.go b/src/cmd/compile/internal/x86/gsubr.go
index 91c009116c..569ffd11bd 100644
--- a/src/cmd/compile/internal/x86/gsubr.go
+++ b/src/cmd/compile/internal/x86/gsubr.go
@@ -91,7 +91,7 @@ func optoas(op gc.Op, t *gc.Type) obj.As {
a := obj.AXXX
switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) {
default:
- gc.Fatalf("optoas: no entry %v-%v", gc.Oconv(op, 0), t)
+ gc.Fatalf("optoas: no entry %v-%v", op, t)
case OADDR_ | gc.TPTR32:
a = x86.ALEAL
@@ -454,7 +454,7 @@ func foptoas(op gc.Op, t *gc.Type, flg int) obj.As {
if !gc.Thearch.Use387 {
switch uint32(op)<<16 | uint32(et) {
default:
- gc.Fatalf("foptoas-sse: no entry %v-%v", gc.Oconv(op, 0), t)
+ gc.Fatalf("foptoas-sse: no entry %v-%v", op, t)
case OCMP_ | gc.TFLOAT32:
a = x86.AUCOMISS
@@ -587,7 +587,7 @@ func foptoas(op gc.Op, t *gc.Type, flg int) obj.As {
return x86.AFCHS
}
- gc.Fatalf("foptoas %v %v %#x", gc.Oconv(op, 0), t, flg)
+ gc.Fatalf("foptoas %v %v %#x", op, t, flg)
return 0
}
diff --git a/src/cmd/compile/internal/x86/reg.go b/src/cmd/compile/internal/x86/reg.go
index 76d90b8e89..d49a1aed9d 100644
--- a/src/cmd/compile/internal/x86/reg.go
+++ b/src/cmd/compile/internal/x86/reg.go
@@ -62,7 +62,7 @@ func regnames(n *int) []string {
}
func excludedregs() uint64 {
- if gc.Ctxt.Flag_shared != 0 {
+ if gc.Ctxt.Flag_shared {
return RtoB(x86.REG_SP) | RtoB(x86.REG_CX)
} else {
return RtoB(x86.REG_SP)
diff --git a/src/cmd/compile/main.go b/src/cmd/compile/main.go
index 49c2fb6263..8b8161efd1 100644
--- a/src/cmd/compile/main.go
+++ b/src/cmd/compile/main.go
@@ -10,6 +10,7 @@ import (
"cmd/compile/internal/arm64"
"cmd/compile/internal/mips64"
"cmd/compile/internal/ppc64"
+ "cmd/compile/internal/s390x"
"cmd/compile/internal/x86"
"cmd/internal/obj"
"fmt"
@@ -38,5 +39,7 @@ func main() {
mips64.Main()
case "ppc64", "ppc64le":
ppc64.Main()
+ case "s390x":
+ s390x.Main()
}
}
diff --git a/src/cmd/dist/buildgo.go b/src/cmd/dist/buildgo.go
index af33ecd66d..2b68fc2224 100644
--- a/src/cmd/dist/buildgo.go
+++ b/src/cmd/dist/buildgo.go
@@ -7,6 +7,7 @@ package main
import (
"bytes"
"fmt"
+ "sort"
)
/*
@@ -48,6 +49,15 @@ func mkzdefaultcc(dir, file string) {
//
// It is invoked to write go/build/zcgo.go.
func mkzcgo(dir, file string) {
+ // sort for deterministic zcgo.go file
+ var list []string
+ for plat, hasCgo := range cgoEnabled {
+ if hasCgo {
+ list = append(list, plat)
+ }
+ }
+ sort.Strings(list)
+
var buf bytes.Buffer
fmt.Fprintf(&buf,
@@ -56,10 +66,8 @@ func mkzcgo(dir, file string) {
"package build\n"+
"\n"+
"var cgoEnabled = map[string]bool{\n")
- for plat, hasCgo := range cgoEnabled {
- if hasCgo {
- fmt.Fprintf(&buf, "\t%q: true,\n", plat)
- }
+ for _, plat := range list {
+ fmt.Fprintf(&buf, "\t%q: true,\n", plat)
}
fmt.Fprintf(&buf, "}")
diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go
index df9d9f3eeb..a535316ca0 100644
--- a/src/cmd/dist/buildtool.go
+++ b/src/cmd/dist/buildtool.go
@@ -38,6 +38,8 @@ var bootstrapDirs = []string{
"compile/internal/ppc64",
"compile/internal/ssa",
"compile/internal/x86",
+ "compile/internal/s390x",
+ "internal/bio",
"internal/gcprog",
"internal/obj",
"internal/obj/arm",
@@ -46,6 +48,7 @@ var bootstrapDirs = []string{
"internal/obj/ppc64",
"internal/obj/s390x",
"internal/obj/x86",
+ "internal/sys",
"link",
"link/internal/amd64",
"link/internal/arm",
diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go
index 9a9cf2d7e4..11c22f4fd3 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -725,8 +725,10 @@ func (t *tester) cgoTest(dt *distTest) error {
cmd := t.addCmd(dt, "misc/cgo/test", "go", "test", t.tags(), "-ldflags", "-linkmode=auto")
cmd.Env = env
- if t.gohostos != "dragonfly" {
+ if t.gohostos != "dragonfly" && t.gohostarch != "ppc64le" {
// linkmode=internal fails on dragonfly since errno is a TLS relocation.
+ // linkmode=internal fails on ppc64le because cmd/link doesn't
+ // handle the TOC correctly (issue 15409).
cmd := t.addCmd(dt, "misc/cgo/test", "go", "test", "-ldflags", "-linkmode=internal")
cmd.Env = env
}
diff --git a/src/cmd/doc/pkg.go b/src/cmd/doc/pkg.go
index d0983d447d..efd681d514 100644
--- a/src/cmd/doc/pkg.go
+++ b/src/cmd/doc/pkg.go
@@ -268,7 +268,7 @@ func (pkg *Package) packageDoc() {
pkg.newlines(2) // Guarantee blank line before the components.
pkg.valueSummary(pkg.doc.Consts)
pkg.valueSummary(pkg.doc.Vars)
- pkg.funcSummary(pkg.doc.Funcs)
+ pkg.funcSummary(pkg.doc.Funcs, false)
pkg.typeSummary()
pkg.bugs()
}
@@ -308,24 +308,44 @@ func (pkg *Package) valueSummary(values []*doc.Value) {
}
}
-// funcSummary prints a one-line summary for each function.
-func (pkg *Package) funcSummary(funcs []*doc.Func) {
+// funcSummary prints a one-line summary for each function. Constructors
+// are printed by typeSummary, below, and so can be suppressed here.
+func (pkg *Package) funcSummary(funcs []*doc.Func, showConstructors bool) {
+ // First, identify the constructors. Don't bother figuring out if they're exported.
+ var isConstructor map[*doc.Func]bool
+ if !showConstructors {
+ isConstructor = make(map[*doc.Func]bool)
+ for _, typ := range pkg.doc.Types {
+ for _, constructor := range typ.Funcs {
+ isConstructor[constructor] = true
+ }
+ }
+ }
for _, fun := range funcs {
decl := fun.Decl
// Exported functions only. The go/doc package does not include methods here.
if isExported(fun.Name) {
- pkg.oneLineFunc(decl)
+ if !isConstructor[fun] {
+ pkg.oneLineFunc(decl)
+ }
}
}
}
-// typeSummary prints a one-line summary for each type.
+// typeSummary prints a one-line summary for each type, followed by its constructors.
func (pkg *Package) typeSummary() {
for _, typ := range pkg.doc.Types {
for _, spec := range typ.Decl.Specs {
typeSpec := spec.(*ast.TypeSpec) // Must succeed.
if isExported(typeSpec.Name.Name) {
pkg.oneLineTypeDecl(typeSpec)
+ // Now print the constructors.
+ for _, constructor := range typ.Funcs {
+ if isExported(constructor.Name) {
+ pkg.Printf(indent)
+ pkg.oneLineFunc(constructor.Decl)
+ }
+ }
}
}
}
@@ -453,8 +473,8 @@ func (pkg *Package) symbolDoc(symbol string) bool {
}
pkg.valueSummary(typ.Consts)
pkg.valueSummary(typ.Vars)
- pkg.funcSummary(typ.Funcs)
- pkg.funcSummary(typ.Methods)
+ pkg.funcSummary(typ.Funcs, true)
+ pkg.funcSummary(typ.Methods, true)
found = true
}
if !found {
diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go
index 1f5981055c..2b74cb59e3 100644
--- a/src/cmd/go/alldocs.go
+++ b/src/cmd/go/alldocs.go
@@ -69,6 +69,8 @@ When compiling multiple packages or a single non-main package,
build compiles the packages but discards the resulting object,
serving only as a check that the packages can be built.
+When compiling packages, build ignores files that end in '_test.go'.
+
The -o flag, only allowed when compiling a single package,
forces build to write the resulting executable or object
to the named output file, instead of the default behavior described
@@ -566,6 +568,7 @@ syntax of package template. The default output is equivalent to -f
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?
+ StaleReason string // explanation for Stale==true
Root string // Go root or Go path dir containing this package
// Source files
@@ -1346,7 +1349,10 @@ The following flags are recognized by the 'go test' command and
control the execution of any test:
-bench regexp
- Run benchmarks matching the regular expression.
+ Run (sub)benchmarks matching a regular expression.
+ The given regular expression is split into smaller ones by
+ top-level '/', where each must match the corresponding part of a
+ benchmark's identifier.
By default, no benchmarks run. To run all benchmarks,
use '-bench .' or '-bench=.'.
@@ -1434,8 +1440,10 @@ control the execution of any test:
(see 'go help build').
-run regexp
- Run only those tests and examples matching the regular
- expression.
+ Run only those tests and examples matching the regular expression.
+ For tests the regular expression is split into smaller ones by
+ top-level '/', where each must match the corresponding part of a
+ test's identifier.
-short
Tell long-running tests to shorten their run time.
diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go
index 01b32c30ed..b8c12db196 100644
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -48,6 +48,8 @@ When compiling multiple packages or a single non-main package,
build compiles the packages but discards the resulting object,
serving only as a check that the packages can be built.
+When compiling packages, build ignores files that end in '_test.go'.
+
The -o flag, only allowed when compiling a single package,
forces build to write the resulting executable or object
to the named output file, instead of the default behavior described
@@ -332,6 +334,11 @@ func buildModeInit() {
}
return p
}
+ switch platform {
+ case "darwin/arm":
+ codegenArg = "-shared"
+ default:
+ }
exeSuffix = ".a"
ldBuildmode = "c-archive"
case "c-shared":
@@ -463,6 +470,7 @@ func runBuild(cmd *Command, args []string) {
p := pkgs[0]
p.target = *buildO
p.Stale = true // must build - not up to date
+ p.StaleReason = "build -o flag in use"
a := b.action(modeInstall, depMode, p)
b.do(a)
return
@@ -834,6 +842,7 @@ func goFilesPackage(gofiles []string) *Package {
pkg.Target = pkg.target
pkg.Stale = true
+ pkg.StaleReason = "files named on command line"
computeStale(pkg)
return pkg
@@ -1424,6 +1433,9 @@ func (b *builder) build(a *action) (err error) {
if err != nil {
return err
}
+ if _, ok := buildToolchain.(gccgoToolchain); ok {
+ cgoObjects = append(cgoObjects, filepath.Join(a.objdir, "_cgo_flags"))
+ }
cgoObjects = append(cgoObjects, outObj...)
gofiles = append(gofiles, outGo...)
}
@@ -2599,10 +2611,9 @@ func (gccgoToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles
func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions []*action, mainpkg string, ofiles []string) error {
// gccgo needs explicit linking with all package dependencies,
// and all LDFLAGS from cgo dependencies.
- apackagesSeen := make(map[*Package]bool)
+ apackagePathsSeen := make(map[string]bool)
afiles := []string{}
shlibs := []string{}
- xfiles := []string{}
ldflags := b.gccArchArgs()
cgoldflags := []string{}
usesCgo := false
@@ -2610,12 +2621,73 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions
objc := len(root.p.MFiles) > 0
fortran := len(root.p.FFiles) > 0
+ readCgoFlags := func(flagsFile string) error {
+ flags, err := ioutil.ReadFile(flagsFile)
+ if err != nil {
+ return err
+ }
+ const ldflagsPrefix = "_CGO_LDFLAGS="
+ for _, line := range strings.Split(string(flags), "\n") {
+ if strings.HasPrefix(line, ldflagsPrefix) {
+ newFlags := strings.Fields(line[len(ldflagsPrefix):])
+ for _, flag := range newFlags {
+ // Every _cgo_flags file has -g and -O2 in _CGO_LDFLAGS
+ // but they don't mean anything to the linker so filter
+ // them out.
+ if flag != "-g" && !strings.HasPrefix(flag, "-O") {
+ cgoldflags = append(cgoldflags, flag)
+ }
+ }
+ }
+ }
+ return nil
+ }
+
+ readAndRemoveCgoFlags := func(archive string) (string, error) {
+ newa, err := ioutil.TempFile(b.work, filepath.Base(archive))
+ if err != nil {
+ return "", err
+ }
+ olda, err := os.Open(archive)
+ if err != nil {
+ return "", err
+ }
+ _, err = io.Copy(newa, olda)
+ if err != nil {
+ return "", err
+ }
+ err = olda.Close()
+ if err != nil {
+ return "", err
+ }
+ err = newa.Close()
+ if err != nil {
+ return "", err
+ }
+
+ newarchive := newa.Name()
+ err = b.run(b.work, root.p.ImportPath, nil, "ar", "x", newarchive, "_cgo_flags")
+ if err != nil {
+ return "", err
+ }
+ err = b.run(".", root.p.ImportPath, nil, "ar", "d", newarchive, "_cgo_flags")
+ if err != nil {
+ return "", err
+ }
+ err = readCgoFlags(filepath.Join(b.work, "_cgo_flags"))
+ if err != nil {
+ return "", err
+ }
+ return newarchive, nil
+ }
+
actionsSeen := make(map[*action]bool)
// Make a pre-order depth-first traversal of the action graph, taking note of
// whether a shared library action has been seen on the way to an action (the
// construction of the graph means that if any path to a node passes through
// a shared library action, they all do).
var walk func(a *action, seenShlib bool)
+ var err error
walk = func(a *action, seenShlib bool) {
if actionsSeen[a] {
return
@@ -2630,21 +2702,18 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions
// rather than the 'build' location (which may not exist any
// more). We still need to traverse the dependencies of the
// build action though so saying
- // if apackagesSeen[a.p] { return }
+ // if apackagePathsSeen[a.p.ImportPath] { return }
// doesn't work.
- if !apackagesSeen[a.p] {
- apackagesSeen[a.p] = true
- if a.p.fake && a.p.external {
- // external _tests, if present must come before
- // internal _tests. Store these on a separate list
- // and place them at the head after this loop.
- xfiles = append(xfiles, a.target)
- } else if a.p.fake {
- // move _test files to the top of the link order
- afiles = append([]string{a.target}, afiles...)
- } else {
- afiles = append(afiles, a.target)
+ if !apackagePathsSeen[a.p.ImportPath] {
+ apackagePathsSeen[a.p.ImportPath] = true
+ target := a.target
+ if len(a.p.CgoFiles) > 0 {
+ target, err = readAndRemoveCgoFlags(target)
+ if err != nil {
+ return
+ }
}
+ afiles = append(afiles, target)
}
}
if strings.HasSuffix(a.target, ".so") {
@@ -2653,12 +2722,17 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions
}
for _, a1 := range a.deps {
walk(a1, seenShlib)
+ if err != nil {
+ return
+ }
}
}
for _, a1 := range root.deps {
walk(a1, false)
+ if err != nil {
+ return err
+ }
}
- afiles = append(xfiles, afiles...)
for _, a := range allactions {
// Gather CgoLDFLAGS, but not from standard packages.
@@ -2688,6 +2762,14 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions
}
}
+ for i, o := range ofiles {
+ if filepath.Base(o) == "_cgo_flags" {
+ readCgoFlags(o)
+ ofiles = append(ofiles[:i], ofiles[i+1:]...)
+ break
+ }
+ }
+
ldflags = append(ldflags, "-Wl,--whole-archive")
ldflags = append(ldflags, afiles...)
ldflags = append(ldflags, "-Wl,--no-whole-archive")
diff --git a/src/cmd/go/get.go b/src/cmd/go/get.go
index 7e0045fb1d..b52991a5fc 100644
--- a/src/cmd/go/get.go
+++ b/src/cmd/go/get.go
@@ -45,7 +45,7 @@ missing packages but does not use it to look for updates to existing packages.
Get also accepts build flags to control the installation. See 'go help build'.
-When checking out a new package, get creates the target directory
+When checking out a new package, get creates the target directory
GOPATH/src/<import-path>. If the GOPATH contains multiple entries,
get uses the first one. See 'go help gopath'.
@@ -346,7 +346,7 @@ func downloadPackage(p *Package) error {
if p.build.SrcRoot != "" {
// Directory exists. Look for checkout along path to src.
- vcs, rootPath, err = vcsForDir(p)
+ vcs, rootPath, err = vcsFromDir(p.Dir, p.build.SrcRoot)
if err != nil {
return err
}
@@ -354,7 +354,7 @@ func downloadPackage(p *Package) error {
// Double-check where it came from.
if *getU && vcs.remoteRepo != nil {
- dir := filepath.Join(p.build.SrcRoot, rootPath)
+ dir := filepath.Join(p.build.SrcRoot, filepath.FromSlash(rootPath))
remote, err := vcs.remoteRepo(vcs, dir)
if err != nil {
return err
@@ -401,7 +401,7 @@ func downloadPackage(p *Package) error {
p.build.SrcRoot = filepath.Join(list[0], "src")
p.build.PkgRoot = filepath.Join(list[0], "pkg")
}
- root := filepath.Join(p.build.SrcRoot, rootPath)
+ root := filepath.Join(p.build.SrcRoot, filepath.FromSlash(rootPath))
// If we've considered this repository already, don't do it again.
if downloadRootCache[root] {
return nil
diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go
index 3e595d187f..fe3d47752c 100644
--- a/src/cmd/go/go_test.go
+++ b/src/cmd/go/go_test.go
@@ -524,32 +524,43 @@ func (tg *testgoData) wantArchive(path string) {
}
}
-// isStale returns whether pkg is stale.
-func (tg *testgoData) isStale(pkg string) bool {
- tg.run("list", "-f", "{{.Stale}}", pkg)
- switch v := strings.TrimSpace(tg.getStdout()); v {
- case "true":
- return true
- case "false":
- return false
- default:
- tg.t.Fatalf("unexpected output checking staleness of package %v: %v", pkg, v)
- panic("unreachable")
+// isStale reports whether pkg is stale, and why
+func (tg *testgoData) isStale(pkg string) (bool, string) {
+ tg.run("list", "-f", "{{.Stale}}:{{.StaleReason}}", pkg)
+ v := strings.TrimSpace(tg.getStdout())
+ f := strings.SplitN(v, ":", 2)
+ if len(f) == 2 {
+ switch f[0] {
+ case "true":
+ return true, f[1]
+ case "false":
+ return false, f[1]
+ }
}
+ tg.t.Fatalf("unexpected output checking staleness of package %v: %v", pkg, v)
+ panic("unreachable")
}
// wantStale fails with msg if pkg is not stale.
-func (tg *testgoData) wantStale(pkg, msg string) {
- if !tg.isStale(pkg) {
+func (tg *testgoData) wantStale(pkg, reason, msg string) {
+ stale, why := tg.isStale(pkg)
+ if !stale {
tg.t.Fatal(msg)
}
+ if reason == "" && why != "" || !strings.Contains(why, reason) {
+ tg.t.Errorf("wrong reason for Stale=true: %q, want %q", why, reason)
+ }
}
// wantNotStale fails with msg if pkg is stale.
-func (tg *testgoData) wantNotStale(pkg, msg string) {
- if tg.isStale(pkg) {
+func (tg *testgoData) wantNotStale(pkg, reason, msg string) {
+ stale, why := tg.isStale(pkg)
+ if stale {
tg.t.Fatal(msg)
}
+ if reason == "" && why != "" || !strings.Contains(why, reason) {
+ tg.t.Errorf("wrong reason for Stale=false: %q, want %q", why, reason)
+ }
}
// cleanup cleans up a test that runs testgo.
@@ -708,7 +719,7 @@ func TestNewReleaseRebuildsStalePackagesInGOPATH(t *testing.T) {
tg.tempFile("d1/src/p1/p1.go", `package p1`)
tg.setenv("GOPATH", tg.path("d1"))
tg.run("install", "-a", "p1")
- tg.wantNotStale("p1", "./testgo list claims p1 is stale, incorrectly")
+ tg.wantNotStale("p1", "", "./testgo list claims p1 is stale, incorrectly")
tg.sleep()
// Changing mtime and content of runtime/internal/sys/sys.go
@@ -717,28 +728,28 @@ func TestNewReleaseRebuildsStalePackagesInGOPATH(t *testing.T) {
sys := runtime.GOROOT() + "/src/runtime/internal/sys/sys.go"
restore := addNL(sys)
defer restore()
- tg.wantNotStale("p1", "./testgo list claims p1 is stale, incorrectly, after updating runtime/internal/sys/sys.go")
+ tg.wantNotStale("p1", "", "./testgo list claims p1 is stale, incorrectly, after updating runtime/internal/sys/sys.go")
restore()
- tg.wantNotStale("p1", "./testgo list claims p1 is stale, incorrectly, after restoring runtime/internal/sys/sys.go")
+ tg.wantNotStale("p1", "", "./testgo list claims p1 is stale, incorrectly, after restoring runtime/internal/sys/sys.go")
// But changing runtime/internal/sys/zversion.go should have an effect:
// that's how we tell when we flip from one release to another.
zversion := runtime.GOROOT() + "/src/runtime/internal/sys/zversion.go"
restore = addNL(zversion)
defer restore()
- tg.wantStale("p1", "./testgo list claims p1 is NOT stale, incorrectly, after changing to new release")
+ tg.wantStale("p1", "build ID mismatch", "./testgo list claims p1 is NOT stale, incorrectly, after changing to new release")
restore()
- tg.wantNotStale("p1", "./testgo list claims p1 is stale, incorrectly, after changing back to old release")
+ tg.wantNotStale("p1", "", "./testgo list claims p1 is stale, incorrectly, after changing back to old release")
addNL(zversion)
- tg.wantStale("p1", "./testgo list claims p1 is NOT stale, incorrectly, after changing again to new release")
+ tg.wantStale("p1", "build ID mismatch", "./testgo list claims p1 is NOT stale, incorrectly, after changing again to new release")
tg.run("install", "p1")
- tg.wantNotStale("p1", "./testgo list claims p1 is stale after building with new release")
+ tg.wantNotStale("p1", "", "./testgo list claims p1 is stale after building with new release")
// Restore to "old" release.
restore()
- tg.wantStale("p1", "./testgo list claims p1 is NOT stale, incorrectly, after changing to old release after new build")
+ tg.wantStale("p1", "build ID mismatch", "./testgo list claims p1 is NOT stale, incorrectly, after changing to old release after new build")
tg.run("install", "p1")
- tg.wantNotStale("p1", "./testgo list claims p1 is stale after building with old release")
+ tg.wantNotStale("p1", "", "./testgo list claims p1 is stale after building with old release")
// Everything is out of date. Rebuild to leave things in a better state.
tg.run("install", "std")
@@ -821,8 +832,8 @@ func TestGoInstallRebuildsStalePackagesInOtherGOPATH(t *testing.T) {
sep := string(filepath.ListSeparator)
tg.setenv("GOPATH", tg.path("d1")+sep+tg.path("d2"))
tg.run("install", "p1")
- tg.wantNotStale("p1", "./testgo list claims p1 is stale, incorrectly")
- tg.wantNotStale("p2", "./testgo list claims p2 is stale, incorrectly")
+ tg.wantNotStale("p1", "", "./testgo list claims p1 is stale, incorrectly")
+ tg.wantNotStale("p2", "", "./testgo list claims p2 is stale, incorrectly")
tg.sleep()
if f, err := os.OpenFile(tg.path("d2/src/p2/p2.go"), os.O_WRONLY|os.O_APPEND, 0); err != nil {
t.Fatal(err)
@@ -831,12 +842,12 @@ func TestGoInstallRebuildsStalePackagesInOtherGOPATH(t *testing.T) {
} else {
tg.must(f.Close())
}
- tg.wantStale("p2", "./testgo list claims p2 is NOT stale, incorrectly")
- tg.wantStale("p1", "./testgo list claims p1 is NOT stale, incorrectly")
+ tg.wantStale("p2", "newer source file", "./testgo list claims p2 is NOT stale, incorrectly")
+ tg.wantStale("p1", "stale dependency", "./testgo list claims p1 is NOT stale, incorrectly")
tg.run("install", "p1")
- tg.wantNotStale("p2", "./testgo list claims p2 is stale after reinstall, incorrectly")
- tg.wantNotStale("p1", "./testgo list claims p1 is stale after reinstall, incorrectly")
+ tg.wantNotStale("p2", "", "./testgo list claims p2 is stale after reinstall, incorrectly")
+ tg.wantNotStale("p1", "", "./testgo list claims p1 is stale after reinstall, incorrectly")
}
func TestGoInstallDetectsRemovedFiles(t *testing.T) {
@@ -850,13 +861,13 @@ func TestGoInstallDetectsRemovedFiles(t *testing.T) {
package mypkg`)
tg.setenv("GOPATH", tg.path("."))
tg.run("install", "mypkg")
- tg.wantNotStale("mypkg", "./testgo list mypkg claims mypkg is stale, incorrectly")
+ tg.wantNotStale("mypkg", "", "./testgo list mypkg claims mypkg is stale, incorrectly")
// z.go was not part of the build; removing it is okay.
tg.must(os.Remove(tg.path("src/mypkg/z.go")))
- tg.wantNotStale("mypkg", "./testgo list mypkg claims mypkg is stale after removing z.go; should not be stale")
+ tg.wantNotStale("mypkg", "", "./testgo list mypkg claims mypkg is stale after removing z.go; should not be stale")
// y.go was part of the package; removing it should be detected.
tg.must(os.Remove(tg.path("src/mypkg/y.go")))
- tg.wantStale("mypkg", "./testgo list mypkg claims mypkg is NOT stale after removing y.go; should be stale")
+ tg.wantStale("mypkg", "build ID mismatch", "./testgo list mypkg claims mypkg is NOT stale after removing y.go; should be stale")
}
func TestWildcardMatchesSyntaxErrorDirs(t *testing.T) {
@@ -919,13 +930,13 @@ func TestGoInstallDetectsRemovedFilesInPackageMain(t *testing.T) {
package main`)
tg.setenv("GOPATH", tg.path("."))
tg.run("install", "mycmd")
- tg.wantNotStale("mycmd", "./testgo list mypkg claims mycmd is stale, incorrectly")
+ tg.wantNotStale("mycmd", "", "./testgo list mypkg claims mycmd is stale, incorrectly")
// z.go was not part of the build; removing it is okay.
tg.must(os.Remove(tg.path("src/mycmd/z.go")))
- tg.wantNotStale("mycmd", "./testgo list mycmd claims mycmd is stale after removing z.go; should not be stale")
+ tg.wantNotStale("mycmd", "", "./testgo list mycmd claims mycmd is stale after removing z.go; should not be stale")
// y.go was part of the package; removing it should be detected.
tg.must(os.Remove(tg.path("src/mycmd/y.go")))
- tg.wantStale("mycmd", "./testgo list mycmd claims mycmd is NOT stale after removing y.go; should be stale")
+ tg.wantStale("mycmd", "build ID mismatch", "./testgo list mycmd claims mycmd is NOT stale after removing y.go; should be stale")
}
func testLocalRun(tg *testgoData, exepath, local, match string) {
@@ -1317,7 +1328,7 @@ func TestPackageMainTestImportsArchiveNotBinary(t *testing.T) {
tg.sleep()
tg.run("test", "main_test")
tg.run("install", "main_test")
- tg.wantNotStale("main_test", "after go install, main listed as stale")
+ tg.wantNotStale("main_test", "", "after go install, main listed as stale")
tg.run("test", "main_test")
}
@@ -1327,9 +1338,9 @@ func TestPackageNotStaleWithTrailingSlash(t *testing.T) {
defer tg.cleanup()
goroot := runtime.GOROOT()
tg.setenv("GOROOT", goroot+"/")
- tg.wantNotStale("runtime", "with trailing slash in GOROOT, runtime listed as stale")
- tg.wantNotStale("os", "with trailing slash in GOROOT, os listed as stale")
- tg.wantNotStale("io", "with trailing slash in GOROOT, io listed as stale")
+ tg.wantNotStale("runtime", "", "with trailing slash in GOROOT, runtime listed as stale")
+ tg.wantNotStale("os", "", "with trailing slash in GOROOT, os listed as stale")
+ tg.wantNotStale("io", "", "with trailing slash in GOROOT, io listed as stale")
}
// With $GOBIN set, binaries get installed to $GOBIN.
@@ -1377,28 +1388,6 @@ func TestInstallToGOBINCommandLinePackage(t *testing.T) {
tg.wantExecutable("testdata/bin1/helloworld"+exeSuffix, "go install testdata/src/go-cmd-test/helloworld.go did not write testdata/bin1/helloworld")
}
-func TestGodocInstalls(t *testing.T) {
- testenv.MustHaveExternalNetwork(t)
-
- // godoc installs into GOBIN
- tg := testgo(t)
- defer tg.cleanup()
- tg.parallel()
- tg.tempDir("gobin")
- tg.setenv("GOPATH", tg.path("."))
- tg.setenv("GOBIN", tg.path("gobin"))
- tg.run("get", "golang.org/x/tools/cmd/godoc")
- tg.wantExecutable(tg.path("gobin/godoc"), "did not install godoc to $GOBIN")
- tg.unsetenv("GOBIN")
-
- // godoc installs into GOROOT
- goroot := runtime.GOROOT()
- tg.setenv("GOROOT", goroot)
- tg.check(os.RemoveAll(filepath.Join(goroot, "bin", "godoc")))
- tg.run("install", "golang.org/x/tools/cmd/godoc")
- tg.wantExecutable(filepath.Join(goroot, "bin", "godoc"), "did not install godoc to $GOROOT/bin")
-}
-
func TestGoGetNonPkg(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
@@ -1488,7 +1477,7 @@ func TestGoTestWithPackageListedMultipleTimes(t *testing.T) {
defer tg.cleanup()
tg.parallel()
tg.run("test", "errors", "errors", "errors", "errors", "errors")
- if strings.Index(strings.TrimSpace(tg.getStdout()), "\n") != -1 {
+ if strings.Contains(strings.TrimSpace(tg.getStdout()), "\n") {
t.Error("go test errors errors errors errors errors tested the same package multiple times")
}
}
@@ -1517,7 +1506,7 @@ func TestGoListCmdOnlyShowsCommands(t *testing.T) {
tg.run("list", "cmd")
out := strings.TrimSpace(tg.getStdout())
for _, line := range strings.Split(out, "\n") {
- if strings.Index(line, "cmd/") == -1 {
+ if !strings.Contains(line, "cmd/") {
t.Error("go list cmd shows non-commands")
break
}
@@ -2320,8 +2309,7 @@ func TestGoVetWithExternalTests(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.makeTempdir()
- tg.setenv("GOPATH", tg.path("."))
- tg.run("get", "golang.org/x/tools/cmd/vet")
+ tg.run("install", "cmd/vet")
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
tg.runFail("vet", "vetpkg")
tg.grepBoth("missing argument for Printf", "go vet vetpkg did not find missing argument for Printf")
@@ -2333,8 +2321,7 @@ func TestGoVetWithTags(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.makeTempdir()
- tg.setenv("GOPATH", tg.path("."))
- tg.run("get", "golang.org/x/tools/cmd/vet")
+ tg.run("install", "cmd/vet")
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
tg.runFail("vet", "-tags", "tagtest", "vetpkg")
tg.grepBoth(`c\.go.*wrong number of args for format`, "go get vetpkg did not run scan tagged file")
@@ -2384,6 +2371,8 @@ func TestIssue4210(t *testing.T) {
func TestGoGetInsecure(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
+ t.Skip("golang.org/issue/15410")
+
tg := testgo(t)
defer tg.cleanup()
tg.makeTempdir()
diff --git a/src/cmd/go/http.go b/src/cmd/go/http.go
index 19e1fe4f77..05ea503049 100644
--- a/src/cmd/go/http.go
+++ b/src/cmd/go/http.go
@@ -30,7 +30,7 @@ var httpClient = http.DefaultClient
// when we're connecting to https servers that might not be there
// or might be using self-signed certificates.
var impatientInsecureHTTPClient = &http.Client{
- Timeout: time.Duration(5 * time.Second),
+ Timeout: 5 * time.Second,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
diff --git a/src/cmd/go/list.go b/src/cmd/go/list.go
index 9409f37154..49a63425bf 100644
--- a/src/cmd/go/list.go
+++ b/src/cmd/go/list.go
@@ -41,6 +41,7 @@ syntax of package template. The default output is equivalent to -f
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?
+ StaleReason string // explanation for Stale==true
Root string // Go root or Go path dir containing this package
// Source files
diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go
index fa923c8873..00e0d73153 100644
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -39,6 +39,7 @@ type Package struct {
Goroot bool `json:",omitempty"` // is this package found in the Go root?
Standard bool `json:",omitempty"` // is this package part of the standard Go library?
Stale bool `json:",omitempty"` // would 'go install' do anything for this package?
+ StaleReason string `json:",omitempty"` // why is Stale true?
Root string `json:",omitempty"` // Go root or Go path dir containing this package
ConflictDir string `json:",omitempty"` // Dir is hidden by this other directory
@@ -434,6 +435,12 @@ func vendoredImportPath(parent *Package, path string) (found string) {
}
targ := filepath.Join(dir[:i], vpath)
if isDir(targ) && hasGoFiles(targ) {
+ importPath := parent.ImportPath
+ if importPath == "command-line-arguments" {
+ // If parent.ImportPath is 'command-line-arguments'.
+ // set to relative directory to root (also chopped root directory)
+ importPath = dir[len(root)+1:]
+ }
// We started with parent's dir c:\gopath\src\foo\bar\baz\quux\xyzzy.
// We know the import path for parent's dir.
// We chopped off some number of path elements and
@@ -443,14 +450,14 @@ func vendoredImportPath(parent *Package, path string) (found string) {
// (actually the same number of bytes) from parent's import path
// and then append /vendor/path.
chopped := len(dir) - i
- if chopped == len(parent.ImportPath)+1 {
+ if chopped == len(importPath)+1 {
// We walked up from c:\gopath\src\foo\bar
// and found c:\gopath\src\vendor\path.
// We chopped \foo\bar (length 8) but the import path is "foo/bar" (length 7).
// Use "vendor/path" without any prefix.
return vpath
}
- return parent.ImportPath[:len(parent.ImportPath)-chopped] + "/" + vpath
+ return importPath[:len(importPath)-chopped] + "/" + vpath
}
}
return path
@@ -517,7 +524,7 @@ func disallowInternal(srcDir string, p *Package, stk *importStack) *Package {
return p
}
- // Check for "internal" element: four cases depending on begin of string and/or end of string.
+ // Check for "internal" element: three cases depending on begin of string and/or end of string.
i, ok := findInternal(p.ImportPath)
if !ok {
return p
@@ -554,7 +561,7 @@ func disallowInternal(srcDir string, p *Package, stk *importStack) *Package {
// If there isn't one, findInternal returns ok=false.
// Otherwise, findInternal returns ok=true and the index of the "internal".
func findInternal(path string) (index int, ok bool) {
- // Four cases, depending on internal at start/end of string or not.
+ // Three cases, depending on internal at start/end of string or not.
// The order matters: we must return the index of the final element,
// because the final one produces the most restrictive requirement
// on the importer.
@@ -652,7 +659,7 @@ func disallowVendorVisibility(srcDir string, p *Package, stk *importStack) *Pack
// findVendor looks for the last non-terminating "vendor" path element in the given import path.
// If there isn't one, findVendor returns ok=false.
-// Otherwise, findInternal returns ok=true and the index of the "vendor".
+// Otherwise, findVendor returns ok=true and the index of the "vendor".
//
// Note that terminating "vendor" elements don't count: "x/vendor" is its own package,
// not the vendored copy of an import "" (the empty import path).
@@ -676,7 +683,6 @@ type targetDir int
const (
toRoot targetDir = iota // to bin dir inside package root (default)
toTool // GOROOT/pkg/tool
- toBin // GOROOT/bin
stalePath // the old import path; fail to build
)
@@ -700,7 +706,6 @@ var goTools = map[string]targetDir{
"cmd/trace": toTool,
"cmd/vet": toTool,
"cmd/yacc": toTool,
- "golang.org/x/tools/cmd/godoc": toBin,
"code.google.com/p/go.tools/cmd/cover": stalePath,
"code.google.com/p/go.tools/cmd/godoc": stalePath,
"code.google.com/p/go.tools/cmd/vet": stalePath,
@@ -787,12 +792,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
// Install cross-compiled binaries to subdirectories of bin.
elem = full
}
- if p.build.BinDir != gobin && goTools[p.ImportPath] == toBin {
- // Override BinDir.
- // This is from a subrepo but installs to $GOROOT/bin
- // by default anyway (like godoc).
- p.target = filepath.Join(gorootBin, elem)
- } else if p.build.BinDir != "" {
+ if p.build.BinDir != "" {
// Install to GOBIN or bin of GOPATH entry.
p.target = filepath.Join(p.build.BinDir, elem)
if !p.Goroot && strings.Contains(elem, "/") && gobin != "" {
@@ -1086,7 +1086,7 @@ func packageList(roots []*Package) []*Package {
// at the named pkgs (command-line arguments).
func computeStale(pkgs ...*Package) {
for _, p := range packageList(pkgs) {
- p.Stale = isStale(p)
+ p.Stale, p.StaleReason = isStale(p)
}
}
@@ -1357,14 +1357,15 @@ var isGoRelease = strings.HasPrefix(runtime.Version(), "go1")
// standard library, even in release versions. This makes
// 'go build -tags netgo' work, among other things.
-// isStale reports whether package p needs to be rebuilt.
-func isStale(p *Package) bool {
+// isStale reports whether package p needs to be rebuilt,
+// along with the reason why.
+func isStale(p *Package) (bool, string) {
if p.Standard && (p.ImportPath == "unsafe" || buildContext.Compiler == "gccgo") {
// fake, builtin package
- return false
+ return false, "builtin package"
}
if p.Error != nil {
- return true
+ return true, "errors loading package"
}
// A package without Go sources means we only found
@@ -1374,23 +1375,26 @@ func isStale(p *Package) bool {
// only useful with the specific version of the toolchain that
// created them.
if len(p.gofiles) == 0 && !p.usesSwig() {
- return false
+ return false, "no source files"
}
// If the -a flag is given, rebuild everything.
if buildA {
- return true
+ return true, "build -a flag in use"
}
// If there's no install target or it's already marked stale, we have to rebuild.
- if p.target == "" || p.Stale {
- return true
+ if p.target == "" {
+ return true, "no install target"
+ }
+ if p.Stale {
+ return true, p.StaleReason
}
// Package is stale if completely unbuilt.
fi, err := os.Stat(p.target)
if err != nil {
- return true
+ return true, "cannot stat install target"
}
// Package is stale if the expected build ID differs from the
@@ -1403,13 +1407,13 @@ func isStale(p *Package) bool {
// See issue 8290 and issue 10702.
targetBuildID, err := readBuildID(p)
if err == nil && targetBuildID != p.buildID {
- return true
+ return true, "build ID mismatch"
}
// Package is stale if a dependency is.
for _, p1 := range p.deps {
if p1.Stale {
- return true
+ return true, "stale dependency"
}
}
@@ -1432,7 +1436,7 @@ func isStale(p *Package) bool {
// install is to run make.bash, which will remove the old package archives
// before rebuilding.)
if p.Standard && isGoRelease {
- return false
+ return false, "standard package in Go release distribution"
}
// Time-based staleness.
@@ -1447,7 +1451,7 @@ func isStale(p *Package) bool {
// Package is stale if a dependency is, or if a dependency is newer.
for _, p1 := range p.deps {
if p1.target != "" && olderThan(p1.target) {
- return true
+ return true, "newer dependency"
}
}
@@ -1466,10 +1470,10 @@ func isStale(p *Package) bool {
// taken care of above (at least when the installed Go is a released version).
if p.Root != goroot {
if olderThan(buildToolchain.compiler()) {
- return true
+ return true, "newer compiler"
}
if p.build.IsCommand() && olderThan(buildToolchain.linker()) {
- return true
+ return true, "newer linker"
}
}
@@ -1514,11 +1518,11 @@ func isStale(p *Package) bool {
srcs := stringList(p.GoFiles, p.CFiles, p.CXXFiles, p.MFiles, p.HFiles, p.FFiles, p.SFiles, p.CgoFiles, p.SysoFiles, p.SwigFiles, p.SwigCXXFiles)
for _, src := range srcs {
if olderThan(filepath.Join(p.Dir, src)) {
- return true
+ return true, "newer source file"
}
}
- return false
+ return false, ""
}
// computeBuildID computes the build ID for p, leaving it in p.buildID.
diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go
index a17bc4e982..5c21de5d9b 100644
--- a/src/cmd/go/test.go
+++ b/src/cmd/go/test.go
@@ -125,7 +125,10 @@ control the execution of any test:
const testFlag2 = `
-bench regexp
- Run benchmarks matching the regular expression.
+ Run (sub)benchmarks matching a regular expression.
+ The given regular expression is split into smaller ones by
+ top-level '/', where each must match the corresponding part of a
+ benchmark's identifier.
By default, no benchmarks run. To run all benchmarks,
use '-bench .' or '-bench=.'.
@@ -213,8 +216,10 @@ const testFlag2 = `
(see 'go help build').
-run regexp
- Run only those tests and examples matching the regular
- expression.
+ Run only those tests and examples matching the regular expression.
+ For tests the regular expression is split into smaller ones by
+ top-level '/', where each must match the corresponding part of a
+ test's identifier.
-short
Tell long-running tests to shorten their run time.
@@ -507,7 +512,8 @@ func runTest(cmd *Command, args []string) {
continue
}
p.Stale = true // rebuild
- p.fake = true // do not warn about rebuild
+ p.StaleReason = "rebuild for coverage"
+ p.fake = true // do not warn about rebuild
p.coverMode = testCoverMode
var coverFiles []string
coverFiles = append(coverFiles, p.GoFiles...)
@@ -744,6 +750,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
ptest.fake = true
ptest.forceLibrary = true
ptest.Stale = true
+ ptest.StaleReason = "rebuild for test"
ptest.build = new(build.Package)
*ptest.build = *p.build
m := map[string][]token.Position{}
@@ -1022,6 +1029,7 @@ func recompileForTest(pmain, preal, ptest *Package, testDir string) {
p.target = ""
p.fake = true
p.Stale = true
+ p.StaleReason = "depends on package being tested"
}
}
diff --git a/src/cmd/go/vcs.go b/src/cmd/go/vcs.go
index a9663b2185..4ff71f2168 100644
--- a/src/cmd/go/vcs.go
+++ b/src/cmd/go/vcs.go
@@ -253,7 +253,7 @@ func bzrResolveRepo(vcsBzr *vcsCmd, rootDir, remoteRepo string) (realRepo string
return "", fmt.Errorf("unable to parse output of bzr info")
}
out = out[:i]
- return strings.TrimSpace(string(out)), nil
+ return strings.TrimSpace(out), nil
}
// vcsSvn describes how to use Subversion.
@@ -294,7 +294,7 @@ func svnRemoteRepo(vcsSvn *vcsCmd, rootDir string) (remoteRepo string, err error
return "", fmt.Errorf("unable to parse output of svn info")
}
out = out[:i]
- return strings.TrimSpace(string(out)), nil
+ return strings.TrimSpace(out), nil
}
func (v *vcsCmd) String() string {
@@ -479,15 +479,14 @@ type vcsPath struct {
regexp *regexp.Regexp // cached compiled form of re
}
-// vcsForDir inspects dir and its parents to determine the
+// vcsFromDir inspects dir and its parents to determine the
// version control system and code repository to use.
// On return, root is the import path
-// corresponding to the root of the repository
-// (thus root is a prefix of importPath).
-func vcsForDir(p *Package) (vcs *vcsCmd, root string, err error) {
+// corresponding to the root of the repository.
+func vcsFromDir(dir, srcRoot string) (vcs *vcsCmd, root string, err error) {
// Clean and double-check that dir is in (a subdirectory of) srcRoot.
- dir := filepath.Clean(p.Dir)
- srcRoot := filepath.Clean(p.build.SrcRoot)
+ dir = filepath.Clean(dir)
+ srcRoot = filepath.Clean(srcRoot)
if len(dir) <= len(srcRoot) || dir[len(srcRoot)] != filepath.Separator {
return nil, "", fmt.Errorf("directory %q is outside source root %q", dir, srcRoot)
}
@@ -496,7 +495,7 @@ func vcsForDir(p *Package) (vcs *vcsCmd, root string, err error) {
for len(dir) > len(srcRoot) {
for _, vcs := range vcsList {
if fi, err := os.Stat(filepath.Join(dir, "."+vcs.cmd)); err == nil && fi.IsDir() {
- return vcs, dir[len(srcRoot)+1:], nil
+ return vcs, filepath.ToSlash(dir[len(srcRoot)+1:]), nil
}
}
diff --git a/src/cmd/go/vcs_test.go b/src/cmd/go/vcs_test.go
index 52a534a3a3..d951189459 100644
--- a/src/cmd/go/vcs_test.go
+++ b/src/cmd/go/vcs_test.go
@@ -6,6 +6,10 @@ package main
import (
"internal/testenv"
+ "io/ioutil"
+ "os"
+ "path"
+ "path/filepath"
"testing"
)
@@ -128,6 +132,37 @@ func TestRepoRootForImportPath(t *testing.T) {
}
}
+// Test that vcsFromDir correctly inspects a given directory and returns the right VCS and root.
+func TestFromDir(t *testing.T) {
+ tempDir, err := ioutil.TempDir("", "vcstest")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tempDir)
+
+ for _, vcs := range vcsList {
+ dir := filepath.Join(tempDir, "example.com", vcs.name, "."+vcs.cmd)
+ err := os.MkdirAll(dir, 0755)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ want := repoRoot{
+ vcs: vcs,
+ root: path.Join("example.com", vcs.name),
+ }
+ var got repoRoot
+ got.vcs, got.root, err = vcsFromDir(dir, tempDir)
+ if err != nil {
+ t.Errorf("FromDir(%q, %q): %v", dir, tempDir, err)
+ continue
+ }
+ if got.vcs.name != want.vcs.name || got.root != want.root {
+ t.Errorf("FromDir(%q, %q) = VCS(%s) Root(%s), want VCS(%s) Root(%s)", dir, tempDir, got.vcs, got.root, want.vcs, want.root)
+ }
+ }
+}
+
func TestIsSecure(t *testing.T) {
tests := []struct {
vcs *vcsCmd
diff --git a/src/cmd/go/vendor_test.go b/src/cmd/go/vendor_test.go
index 199eab4471..e3070e8e45 100644
--- a/src/cmd/go/vendor_test.go
+++ b/src/cmd/go/vendor_test.go
@@ -232,6 +232,32 @@ func TestVendorTest2(t *testing.T) {
tg.run("test", "github.com/rsc/go-get-issue-11864/vendor/vendor.org/tx2")
}
+func TestVendorTest3(t *testing.T) {
+ testenv.MustHaveExternalNetwork(t)
+
+ tg := testgo(t)
+ defer tg.cleanup()
+ tg.makeTempdir()
+ tg.setenv("GOPATH", tg.path("."))
+ tg.run("get", "github.com/clsung/go-vendor-issue-14613")
+
+ tg.run("build", "-o", tg.path("a.out"), "-i", "github.com/clsung/go-vendor-issue-14613")
+
+ // test folder should work
+ tg.run("test", "-i", "github.com/clsung/go-vendor-issue-14613")
+ tg.run("test", "github.com/clsung/go-vendor-issue-14613")
+
+ // test with specified _test.go should work too
+ tg.cd(filepath.Join(tg.path("."), "src"))
+ tg.run("test", "-i", "github.com/clsung/go-vendor-issue-14613/vendor_test.go")
+ tg.run("test", "github.com/clsung/go-vendor-issue-14613/vendor_test.go")
+
+ // test with imported and not used
+ tg.run("test", "-i", "github.com/clsung/go-vendor-issue-14613/vendor/mylibtesttest/myapp/myapp_test.go")
+ tg.runFail("test", "github.com/clsung/go-vendor-issue-14613/vendor/mylibtesttest/myapp/myapp_test.go")
+ tg.grepStderr("imported and not used:", `should say "imported and not used"`)
+}
+
func TestVendorList(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
diff --git a/src/cmd/gofmt/simplify.go b/src/cmd/gofmt/simplify.go
index 69f7bf23c0..2ebf4cde0b 100644
--- a/src/cmd/gofmt/simplify.go
+++ b/src/cmd/gofmt/simplify.go
@@ -10,11 +10,9 @@ import (
"reflect"
)
-type simplifier struct {
- hasDotImport bool // package file contains: import . "some/import/path"
-}
+type simplifier struct{}
-func (s *simplifier) Visit(node ast.Node) ast.Visitor {
+func (s simplifier) Visit(node ast.Node) ast.Visitor {
switch n := node.(type) {
case *ast.CompositeLit:
// array, slice, and map composite literals may be simplified
@@ -68,10 +66,13 @@ func (s *simplifier) Visit(node ast.Node) ast.Visitor {
// a slice expression of the form: s[a:len(s)]
// can be simplified to: s[a:]
// if s is "simple enough" (for now we only accept identifiers)
- if n.Max != nil || s.hasDotImport {
+ //
+ // Note: This may not be correct because len may have been redeclared in another
+ // file belonging to the same package. However, this is extremely unlikely
+ // and so far (April 2016, after years of supporting this rewrite feature)
+ // has never come up, so let's keep it working as is (see also #15153).
+ if n.Max != nil {
// - 3-index slices always require the 2nd and 3rd index
- // - if dot imports are present, we cannot be certain that an
- // unresolved "len" identifier refers to the predefined len()
break
}
if s, _ := n.X.(*ast.Ident); s != nil && s.Obj != nil {
@@ -118,20 +119,11 @@ func isBlank(x ast.Expr) bool {
}
func simplify(f *ast.File) {
- var s simplifier
-
- // determine if f contains dot imports
- for _, imp := range f.Imports {
- if imp.Name != nil && imp.Name.Name == "." {
- s.hasDotImport = true
- break
- }
- }
-
// remove empty declarations such as "const ()", etc
removeEmptyDeclGroups(f)
- ast.Walk(&s, f)
+ var s simplifier
+ ast.Walk(s, f)
}
func removeEmptyDeclGroups(f *ast.File) {
diff --git a/src/cmd/gofmt/testdata/slices2.golden b/src/cmd/gofmt/testdata/slices2.golden
deleted file mode 100644
index ab657004e6..0000000000
--- a/src/cmd/gofmt/testdata/slices2.golden
+++ /dev/null
@@ -1,63 +0,0 @@
-//gofmt -s
-
-// Test cases for slice expression simplification.
-// Because of a dot import, these slices must remain untouched.
-package p
-
-import . "math"
-
-var (
- a [10]byte
- b [20]float32
- s []int
- t struct {
- s []byte
- }
-
- _ = a[0:]
- _ = a[1:10]
- _ = a[2:len(a)]
- _ = a[3:(len(a))]
- _ = a[len(a) : len(a)-1]
- _ = a[0:len(b)]
-
- _ = a[:]
- _ = a[:10]
- _ = a[:len(a)]
- _ = a[:(len(a))]
- _ = a[:len(a)-1]
- _ = a[:len(b)]
-
- _ = s[0:]
- _ = s[1:10]
- _ = s[2:len(s)]
- _ = s[3:(len(s))]
- _ = s[len(a) : len(s)-1]
- _ = s[0:len(b)]
-
- _ = s[:]
- _ = s[:10]
- _ = s[:len(s)]
- _ = s[:(len(s))]
- _ = s[:len(s)-1]
- _ = s[:len(b)]
-
- _ = t.s[0:]
- _ = t.s[1:10]
- _ = t.s[2:len(t.s)]
- _ = t.s[3:(len(t.s))]
- _ = t.s[len(a) : len(t.s)-1]
- _ = t.s[0:len(b)]
-
- _ = t.s[:]
- _ = t.s[:10]
- _ = t.s[:len(t.s)]
- _ = t.s[:(len(t.s))]
- _ = t.s[:len(t.s)-1]
- _ = t.s[:len(b)]
-)
-
-func _() {
- s := s[0:len(s)]
- _ = s
-}
diff --git a/src/cmd/gofmt/testdata/slices2.input b/src/cmd/gofmt/testdata/slices2.input
deleted file mode 100644
index ab657004e6..0000000000
--- a/src/cmd/gofmt/testdata/slices2.input
+++ /dev/null
@@ -1,63 +0,0 @@
-//gofmt -s
-
-// Test cases for slice expression simplification.
-// Because of a dot import, these slices must remain untouched.
-package p
-
-import . "math"
-
-var (
- a [10]byte
- b [20]float32
- s []int
- t struct {
- s []byte
- }
-
- _ = a[0:]
- _ = a[1:10]
- _ = a[2:len(a)]
- _ = a[3:(len(a))]
- _ = a[len(a) : len(a)-1]
- _ = a[0:len(b)]
-
- _ = a[:]
- _ = a[:10]
- _ = a[:len(a)]
- _ = a[:(len(a))]
- _ = a[:len(a)-1]
- _ = a[:len(b)]
-
- _ = s[0:]
- _ = s[1:10]
- _ = s[2:len(s)]
- _ = s[3:(len(s))]
- _ = s[len(a) : len(s)-1]
- _ = s[0:len(b)]
-
- _ = s[:]
- _ = s[:10]
- _ = s[:len(s)]
- _ = s[:(len(s))]
- _ = s[:len(s)-1]
- _ = s[:len(b)]
-
- _ = t.s[0:]
- _ = t.s[1:10]
- _ = t.s[2:len(t.s)]
- _ = t.s[3:(len(t.s))]
- _ = t.s[len(a) : len(t.s)-1]
- _ = t.s[0:len(b)]
-
- _ = t.s[:]
- _ = t.s[:10]
- _ = t.s[:len(t.s)]
- _ = t.s[:(len(t.s))]
- _ = t.s[:len(t.s)-1]
- _ = t.s[:len(b)]
-)
-
-func _() {
- s := s[0:len(s)]
- _ = s
-}
diff --git a/src/cmd/internal/bio/buf.go b/src/cmd/internal/bio/buf.go
new file mode 100644
index 0000000000..54ce3c7681
--- /dev/null
+++ b/src/cmd/internal/bio/buf.go
@@ -0,0 +1,99 @@
+// 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 bio implements common I/O abstractions used within the Go toolchain.
+package bio
+
+import (
+ "bufio"
+ "log"
+ "os"
+)
+
+// Reader implements a seekable buffered io.Reader.
+type Reader struct {
+ f *os.File
+ *bufio.Reader
+}
+
+// Writer implements a seekable buffered io.Writer.
+type Writer struct {
+ f *os.File
+ *bufio.Writer
+}
+
+// Create creates the file named name and returns a Writer
+// for that file.
+func Create(name string) (*Writer, error) {
+ f, err := os.Create(name)
+ if err != nil {
+ return nil, err
+ }
+ return &Writer{f: f, Writer: bufio.NewWriter(f)}, nil
+}
+
+// Open returns a Reader for the file named name.
+func Open(name string) (*Reader, error) {
+ f, err := os.Open(name)
+ if err != nil {
+ return nil, err
+ }
+ return &Reader{f: f, Reader: bufio.NewReader(f)}, nil
+}
+
+func (r *Reader) Seek(offset int64, whence int) int64 {
+ if whence == 1 {
+ offset -= int64(r.Buffered())
+ }
+ off, err := r.f.Seek(offset, whence)
+ if err != nil {
+ log.Fatalf("seeking in output: %v", err)
+ }
+ r.Reset(r.f)
+ return off
+}
+
+func (w *Writer) Seek(offset int64, whence int) int64 {
+ if err := w.Flush(); err != nil {
+ log.Fatalf("writing output: %v", err)
+ }
+ off, err := w.f.Seek(offset, whence)
+ if err != nil {
+ log.Fatalf("seeking in output: %v", err)
+ }
+ return off
+}
+
+func (r *Reader) Offset() int64 {
+ off, err := r.f.Seek(0, 1)
+ if err != nil {
+ log.Fatalf("seeking in output [0, 1]: %v", err)
+ }
+ off -= int64(r.Buffered())
+ return off
+}
+
+func (w *Writer) Offset() int64 {
+ if err := w.Flush(); err != nil {
+ log.Fatalf("writing output: %v", err)
+ }
+ off, err := w.f.Seek(0, 1)
+ if err != nil {
+ log.Fatalf("seeking in output [0, 1]: %v", err)
+ }
+ return off
+}
+
+func (r *Reader) Close() error {
+ return r.f.Close()
+}
+
+func (w *Writer) Close() error {
+ err := w.Flush()
+ err1 := w.f.Close()
+ if err == nil {
+ err = err1
+ }
+ return err
+}
diff --git a/src/cmd/internal/bio/must.go b/src/cmd/internal/bio/must.go
new file mode 100644
index 0000000000..3604b29175
--- /dev/null
+++ b/src/cmd/internal/bio/must.go
@@ -0,0 +1,43 @@
+// Copyright 2016 The Go 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 bio
+
+import (
+ "io"
+ "log"
+)
+
+// MustClose closes Closer c and calls log.Fatal if it returns a non-nil error.
+func MustClose(c io.Closer) {
+ if err := c.Close(); err != nil {
+ log.Fatal(err)
+ }
+}
+
+// MustWriter returns a Writer that wraps the provided Writer,
+// except that it calls log.Fatal instead of returning a non-nil error.
+func MustWriter(w io.Writer) io.Writer {
+ return mustWriter{w}
+}
+
+type mustWriter struct {
+ w io.Writer
+}
+
+func (w mustWriter) Write(b []byte) (int, error) {
+ n, err := w.w.Write(b)
+ if err != nil {
+ log.Fatal(err)
+ }
+ return n, nil
+}
+
+func (w mustWriter) WriteString(s string) (int, error) {
+ n, err := io.WriteString(w.w, s)
+ if err != nil {
+ log.Fatal(err)
+ }
+ return n, nil
+}
diff --git a/src/cmd/internal/goobj/read.go b/src/cmd/internal/goobj/read.go
index 5434661384..698d58efe0 100644
--- a/src/cmd/internal/goobj/read.go
+++ b/src/cmd/internal/goobj/read.go
@@ -229,11 +229,8 @@ var (
errCorruptArchive = errors.New("corrupt archive")
errTruncatedArchive = errors.New("truncated archive")
- errNotArchive = errors.New("unrecognized archive format")
-
- errCorruptObject = errors.New("corrupt object file")
- errTruncatedObject = errors.New("truncated object file")
- errNotObject = errors.New("unrecognized object file format")
+ errCorruptObject = errors.New("corrupt object file")
+ errNotObject = errors.New("unrecognized object file format")
)
// An objReader is an object file reader.
diff --git a/src/cmd/internal/obj/arm/asm5.go b/src/cmd/internal/obj/arm/asm5.go
index f49ee65a04..564f96a94e 100644
--- a/src/cmd/internal/obj/arm/asm5.go
+++ b/src/cmd/internal/obj/arm/asm5.go
@@ -870,7 +870,7 @@ func addpool(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
t.To.Type = a.Type
t.To.Name = a.Name
- if ctxt.Flag_shared != 0 && t.To.Sym != nil {
+ if ctxt.Flag_shared && t.To.Sym != nil {
t.Rel = p
}
@@ -1015,7 +1015,7 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int {
ctxt.Instoffset = 0 // s.b. unused but just in case
if a.Sym.Type == obj.STLSBSS {
- if ctxt.Flag_shared != 0 {
+ if ctxt.Flag_shared {
return C_TLS_IE
} else {
return C_TLS_LE
@@ -1322,7 +1322,7 @@ func buildop(ctxt *obj.Link) {
}
for n = 0; optab[n].as != obj.AXXX; n++ {
if optab[n].flag&LPCREL != 0 {
- if ctxt.Flag_shared != 0 {
+ if ctxt.Flag_shared {
optab[n].size += int8(optab[n].pcrelsiz)
} else {
optab[n].flag &^= LPCREL
@@ -1633,7 +1633,7 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
rel.Sym = p.To.Sym
rel.Add = p.To.Offset
- if ctxt.Flag_shared != 0 {
+ if ctxt.Flag_shared {
if p.To.Name == obj.NAME_GOTREF {
rel.Type = obj.R_GOTPCREL
} else {
diff --git a/src/cmd/internal/obj/arm/obj5.go b/src/cmd/internal/obj/arm/obj5.go
index 92ffc7b2f3..042e6ab648 100644
--- a/src/cmd/internal/obj/arm/obj5.go
+++ b/src/cmd/internal/obj/arm/obj5.go
@@ -32,7 +32,7 @@ package arm
import (
"cmd/internal/obj"
- "encoding/binary"
+ "cmd/internal/sys"
"fmt"
"log"
"math"
@@ -412,7 +412,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
p.As = AMOVW
p.From.Type = obj.TYPE_MEM
p.From.Reg = REGG
- p.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic
+ p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
p.To.Type = obj.TYPE_REG
p.To.Reg = REG_R1
@@ -708,9 +708,9 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
p.As = AMOVW
p.From.Type = obj.TYPE_MEM
p.From.Reg = REGG
- p.From.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
+ p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
if ctxt.Cursym.Cfunc {
- p.From.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
+ p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
}
p.To.Type = obj.TYPE_REG
p.To.Reg = REG_R1
@@ -1032,15 +1032,10 @@ var unaryDst = map[obj.As]bool{
}
var Linkarm = obj.LinkArch{
- ByteOrder: binary.LittleEndian,
- Name: "arm",
- Thechar: '5',
+ Arch: sys.ArchARM,
Preprocess: preprocess,
Assemble: span5,
Follow: follow,
Progedit: progedit,
UnaryDst: unaryDst,
- Minlc: 4,
- Ptrsize: 4,
- Regsize: 4,
}
diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go
index ff8d4fdf60..28bebaa3f7 100644
--- a/src/cmd/internal/obj/arm64/asm7.go
+++ b/src/cmd/internal/obj/arm64/asm7.go
@@ -155,6 +155,7 @@ var optab = []Optab{
{AADC, C_REG, C_REG, C_REG, 1, 4, 0, 0, 0},
{AADC, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0},
{ANEG, C_REG, C_NONE, C_REG, 25, 4, 0, 0, 0},
+ {ANEG, C_NONE, C_NONE, C_REG, 25, 4, 0, 0, 0},
{ANGC, C_REG, C_NONE, C_REG, 17, 4, 0, 0, 0},
{ACMP, C_REG, C_REG, C_NONE, 1, 4, 0, 0, 0},
{AADD, C_ADDCON, C_RSP, C_RSP, 2, 4, 0, 0, 0},
@@ -972,7 +973,7 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int {
ctxt.Instoffset = a.Offset
if a.Sym != nil { // use relocation
if a.Sym.Type == obj.STLSBSS {
- if ctxt.Flag_shared != 0 {
+ if ctxt.Flag_shared {
return C_TLS_IE
} else {
return C_TLS_LE
@@ -1087,7 +1088,7 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int {
func oplook(ctxt *obj.Link, p *obj.Prog) *Optab {
a1 := int(p.Optab)
if a1 != 0 {
- return &optab[a1-1:][0]
+ return &optab[a1-1]
}
a1 = int(p.From.Class)
if a1 == 0 {
@@ -2198,6 +2199,9 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
o1 = oprrr(ctxt, p.As)
rf := int(p.From.Reg)
+ if rf == C_NONE {
+ rf = int(p.To.Reg)
+ }
rt := int(p.To.Reg)
o1 |= (uint32(rf&31) << 16) | (REGZERO & 31 << 5) | uint32(rt&31)
diff --git a/src/cmd/internal/obj/arm64/list7.go b/src/cmd/internal/obj/arm64/list7.go
index 36f544b53a..ad9ff0965c 100644
--- a/src/cmd/internal/obj/arm64/list7.go
+++ b/src/cmd/internal/obj/arm64/list7.go
@@ -71,7 +71,7 @@ func Rconv(r int) string {
case REG_F0 <= r && r <= REG_F31:
return fmt.Sprintf("F%d", r-REG_F0)
case REG_V0 <= r && r <= REG_V31:
- return fmt.Sprintf("V%d", r-REG_F0)
+ return fmt.Sprintf("V%d", r-REG_V0)
case COND_EQ <= r && r <= COND_NV:
return strcond[r-COND_EQ]
case r == REGSP:
diff --git a/src/cmd/internal/obj/arm64/obj7.go b/src/cmd/internal/obj/arm64/obj7.go
index b3de44c029..ffa1b416d6 100644
--- a/src/cmd/internal/obj/arm64/obj7.go
+++ b/src/cmd/internal/obj/arm64/obj7.go
@@ -32,7 +32,7 @@ package arm64
import (
"cmd/internal/obj"
- "encoding/binary"
+ "cmd/internal/sys"
"fmt"
"log"
"math"
@@ -56,9 +56,9 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
p.As = AMOVD
p.From.Type = obj.TYPE_MEM
p.From.Reg = REGG
- p.From.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
+ p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
if ctxt.Cursym.Cfunc {
- p.From.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
+ p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
}
p.To.Type = obj.TYPE_REG
p.To.Reg = REG_R1
@@ -250,7 +250,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
if p.From.Type == obj.TYPE_FCONST {
f32 := float32(p.From.Val.(float64))
i32 := math.Float32bits(f32)
- literal := fmt.Sprintf("$f32.%08x", uint32(i32))
+ literal := fmt.Sprintf("$f32.%08x", i32)
s := obj.Linklookup(ctxt, literal, 0)
s.Size = 4
p.From.Type = obj.TYPE_MEM
@@ -263,7 +263,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
case AFMOVD:
if p.From.Type == obj.TYPE_FCONST {
i64 := math.Float64bits(p.From.Val.(float64))
- literal := fmt.Sprintf("$f64.%016x", uint64(i64))
+ literal := fmt.Sprintf("$f64.%016x", i64)
s := obj.Linklookup(ctxt, literal, 0)
s.Size = 8
p.From.Type = obj.TYPE_MEM
@@ -778,7 +778,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
q.As = AMOVD
q.From.Type = obj.TYPE_MEM
q.From.Reg = REGG
- q.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic
+ q.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
q.To.Type = obj.TYPE_REG
q.To.Reg = REG_R1
@@ -941,15 +941,10 @@ var unaryDst = map[obj.As]bool{
}
var Linkarm64 = obj.LinkArch{
- ByteOrder: binary.LittleEndian,
- Name: "arm64",
- Thechar: '7',
+ Arch: sys.ArchARM64,
Preprocess: preprocess,
Assemble: span7,
Follow: follow,
Progedit: progedit,
UnaryDst: unaryDst,
- Minlc: 4,
- Ptrsize: 8,
- Regsize: 8,
}
diff --git a/src/cmd/internal/obj/data.go b/src/cmd/internal/obj/data.go
index a3cc178adc..5fe4cb10a5 100644
--- a/src/cmd/internal/obj/data.go
+++ b/src/cmd/internal/obj/data.go
@@ -75,7 +75,11 @@ func (s *LSym) prepwrite(ctxt *Link, off int64, siz int) {
if s.Type == SBSS || s.Type == STLSBSS {
ctxt.Diag("cannot supply data for BSS var")
}
- s.Grow(off + int64(siz))
+ l := off + int64(siz)
+ s.Grow(l)
+ if l > s.Size {
+ s.Size = l
+ }
}
// WriteFloat32 writes f into s at offset off.
@@ -110,18 +114,37 @@ func (s *LSym) WriteInt(ctxt *Link, off int64, siz int, i int64) {
// WriteAddr writes an address of size siz into s at offset off.
// rsym and roff specify the relocation for the address.
func (s *LSym) WriteAddr(ctxt *Link, off int64, siz int, rsym *LSym, roff int64) {
- if siz != ctxt.Arch.Ptrsize {
- ctxt.Diag("WriteAddr: bad address size: %d", siz)
+ if siz != ctxt.Arch.PtrSize {
+ ctxt.Diag("WriteAddr: bad address size %d in %s", siz, s.Name)
}
s.prepwrite(ctxt, off, siz)
r := Addrel(s)
r.Off = int32(off)
+ if int64(r.Off) != off {
+ ctxt.Diag("WriteAddr: off overflow %d in %s", off, s.Name)
+ }
r.Siz = uint8(siz)
r.Sym = rsym
r.Type = R_ADDR
r.Add = roff
}
+// WriteOff writes a 4 byte offset to rsym+roff into s at offset off.
+// After linking the 4 bytes stored at s+off will be
+// rsym+roff-(start of section that s is in).
+func (s *LSym) WriteOff(ctxt *Link, off int64, rsym *LSym, roff int64) {
+ s.prepwrite(ctxt, off, 4)
+ r := Addrel(s)
+ r.Off = int32(off)
+ if int64(r.Off) != off {
+ ctxt.Diag("WriteOff: off overflow %d in %s", off, s.Name)
+ }
+ r.Siz = 4
+ r.Sym = rsym
+ r.Type = R_ADDROFF
+ r.Add = roff
+}
+
// WriteString writes a string of size siz into s at offset off.
func (s *LSym) WriteString(ctxt *Link, off int64, siz int, str string) {
if siz < len(str) {
@@ -131,6 +154,13 @@ func (s *LSym) WriteString(ctxt *Link, off int64, siz int, str string) {
copy(s.P[off:off+int64(siz)], str)
}
+// WriteBytes writes a slice of bytes into s at offset off.
+func (s *LSym) WriteBytes(ctxt *Link, off int64, b []byte) int64 {
+ s.prepwrite(ctxt, off, len(b))
+ copy(s.P[off:], b)
+ return off + int64(len(b))
+}
+
func Addrel(s *LSym) *Reloc {
s.R = append(s.R, Reloc{})
return &s.R[len(s.R)-1]
@@ -153,7 +183,7 @@ func Setuintxx(ctxt *Link, s *LSym, off int64, v uint64, wid int64) int64 {
case 4:
ctxt.Arch.ByteOrder.PutUint32(s.P[off:], uint32(v))
case 8:
- ctxt.Arch.ByteOrder.PutUint64(s.P[off:], uint64(v))
+ ctxt.Arch.ByteOrder.PutUint64(s.P[off:], v)
}
return off + wid
diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go
index 24f028f737..5f257f60ab 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -30,7 +30,10 @@
package obj
-import "encoding/binary"
+import (
+ "bufio"
+ "cmd/internal/sys"
+)
// An Addr is an argument to an instruction.
// The general forms and their encodings are:
@@ -423,6 +426,8 @@ const (
SCONST
SDYNIMPORT
SHOSTOBJ
+ SDWARFSECT
+ SDWARFINFO
SSUB = 1 << 8
SMASK = SSUB - 1
SHIDDEN = 1 << 9
@@ -452,6 +457,9 @@ const (
// R_ADDRMIPS (only used on mips64) resolves to a 32-bit external address,
// by loading the address into a register with two instructions (lui, ori).
R_ADDRMIPS
+ // R_ADDROFF resolves to a 32-bit offset from the beginning of the section
+ // holding the data being relocated to the referenced symbol.
+ R_ADDROFF
R_SIZE
R_CALL
R_CALLARM
@@ -484,17 +492,20 @@ const (
// should be linked into the final binary, even if there are no other
// direct references. (This is used for types reachable by reflection.)
R_USETYPE
- // R_METHOD resolves to an *rtype for a method.
- // It is used when linking from the uncommonType of another *rtype, and
- // may be set to zero by the linker if it determines the method text is
- // unreachable by the linked program.
- R_METHOD
+ // R_METHODOFF resolves to a 32-bit offset from the beginning of the section
+ // holding the data being relocated to the referenced symbol.
+ // It is a variant of R_ADDROFF used when linking from the uncommonType of a
+ // *rtype, and may be set to zero by the linker if it determines the method
+ // text is unreachable by the linked program.
+ R_METHODOFF
R_POWER_TOC
R_GOTPCREL
// R_JMPMIPS (only used on mips64) resolves to non-PC-relative target address
// of a JMP instruction, by encoding the address into the instruction.
// The stack nosplit check ignores this since it is not a function call.
R_JMPMIPS
+ // R_DWARFREF resolves to the offset of the symbol from its section.
+ R_DWARFREF
// Platform dependent relocations. Architectures with fixed width instructions
// have the inherent issue that a 32-bit (or 64-bit!) displacement cannot be
@@ -590,19 +601,6 @@ type Pcdata struct {
P []byte
}
-// Pcdata iterator.
-// for(pciterinit(ctxt, &it, &pcd); !it.done; pciternext(&it)) { it.value holds in [it.pc, it.nextpc) }
-type Pciter struct {
- d Pcdata
- p []byte
- pc uint32
- nextpc uint32
- pcscale uint32
- value int32
- start int
- done int
-}
-
// symbol version, incremented each time a file is loaded.
// version==1 is reserved for savehist.
const (
@@ -619,10 +617,10 @@ type Link struct {
Debugvlog int32
Debugdivmod int32
Debugpcln int32
- Flag_shared int32
+ Flag_shared bool
Flag_dynlink bool
Flag_optimize bool
- Bso *Biobuf
+ Bso *bufio.Writer
Pathname string
Goroot string
Goroot_final string
@@ -678,15 +676,15 @@ func (ctxt *Link) Diag(format string, args ...interface{}) {
// on the stack in the function prologue and so always have a pointer between
// the hardware stack pointer and the local variable area.
func (ctxt *Link) FixedFrameSize() int64 {
- switch ctxt.Arch.Thechar {
- case '6', '8':
+ switch ctxt.Arch.Family {
+ case sys.AMD64, sys.I386:
return 0
- case '9':
+ case sys.PPC64:
// PIC code on ppc64le requires 32 bytes of stack, and it's easier to
// just use that much stack always on ppc64x.
- return int64(4 * ctxt.Arch.Ptrsize)
+ return int64(4 * ctxt.Arch.PtrSize)
default:
- return int64(ctxt.Arch.Ptrsize)
+ return int64(ctxt.Arch.PtrSize)
}
}
@@ -697,17 +695,12 @@ type SymVer struct {
// LinkArch is the definition of a single architecture.
type LinkArch struct {
- ByteOrder binary.ByteOrder
- Name string
- Thechar int
+ *sys.Arch
Preprocess func(*Link, *LSym)
Assemble func(*Link, *LSym)
Follow func(*Link, *LSym)
Progedit func(*Link, *Prog)
UnaryDst map[As]bool // Instruction takes one operand, a destination.
- Minlc int
- Ptrsize int
- Regsize int
}
/* executable header types */
@@ -725,27 +718,6 @@ const (
Hwindows
)
-type Plist struct {
- Name *LSym
- Firstpc *Prog
- Recur int
- Link *Plist
-}
-
-/*
- * start a new Prog list.
- */
-func Linknewplist(ctxt *Link) *Plist {
- pl := new(Plist)
- if ctxt.Plist == nil {
- ctxt.Plist = pl
- } else {
- ctxt.Plast.Link = pl
- }
- ctxt.Plast = pl
- return pl
-}
-
// AsmBuf is a simple buffer to assemble variable-length x86 instructions into.
type AsmBuf struct {
buf [100]byte
diff --git a/src/cmd/internal/obj/mips/asm0.go b/src/cmd/internal/obj/mips/asm0.go
index 521cb66dec..73d6cabbcb 100644
--- a/src/cmd/internal/obj/mips/asm0.go
+++ b/src/cmd/internal/obj/mips/asm0.go
@@ -611,7 +611,7 @@ func oplook(ctxt *obj.Link, p *obj.Prog) *Optab {
a1 := int(p.Optab)
if a1 != 0 {
- return &optab[a1-1:][0]
+ return &optab[a1-1]
}
a1 = int(p.From.Class)
if a1 == 0 {
@@ -974,10 +974,6 @@ func OP_JMP(op uint32, i uint32) uint32 {
return op | i&0x3FFFFFF
}
-func oclass(a *obj.Addr) int {
- return int(a.Class) - 1
-}
-
func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
o1 := uint32(0)
o2 := uint32(0)
@@ -1028,7 +1024,7 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
o1 = OP_IRR(opirr(ctxt, p.As), uint32(v), uint32(r), uint32(p.To.Reg))
case 5: /* syscall */
- o1 = uint32(oprrr(ctxt, p.As))
+ o1 = oprrr(ctxt, p.As)
case 6: /* beq r1,[r2],sbra */
v := int32(0)
diff --git a/src/cmd/internal/obj/mips/obj0.go b/src/cmd/internal/obj/mips/obj0.go
index ca7d4465c9..49fc2fb864 100644
--- a/src/cmd/internal/obj/mips/obj0.go
+++ b/src/cmd/internal/obj/mips/obj0.go
@@ -31,7 +31,7 @@ package mips
import (
"cmd/internal/obj"
- "encoding/binary"
+ "cmd/internal/sys"
"fmt"
"math"
)
@@ -336,7 +336,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
q.As = AMOVV
q.From.Type = obj.TYPE_MEM
q.From.Reg = REGG
- q.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic
+ q.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
q.To.Type = obj.TYPE_REG
q.To.Reg = REG_R1
@@ -559,9 +559,9 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
p.As = AMOVV
p.From.Type = obj.TYPE_MEM
p.From.Reg = REGG
- p.From.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
+ p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
if ctxt.Cursym.Cfunc {
- p.From.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
+ p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
}
p.To.Type = obj.TYPE_REG
p.To.Reg = REG_R1
@@ -1469,27 +1469,17 @@ loop:
}
var Linkmips64 = obj.LinkArch{
- ByteOrder: binary.BigEndian,
- Name: "mips64",
- Thechar: '0',
+ Arch: sys.ArchMIPS64,
Preprocess: preprocess,
Assemble: span0,
Follow: follow,
Progedit: progedit,
- Minlc: 4,
- Ptrsize: 8,
- Regsize: 8,
}
var Linkmips64le = obj.LinkArch{
- ByteOrder: binary.LittleEndian,
- Name: "mips64le",
- Thechar: '0',
+ Arch: sys.ArchMIPS64LE,
Preprocess: preprocess,
Assemble: span0,
Follow: follow,
Progedit: progedit,
- Minlc: 4,
- Ptrsize: 8,
- Regsize: 8,
}
diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go
index 30a380fadf..17175ebf06 100644
--- a/src/cmd/internal/obj/objfile.go
+++ b/src/cmd/internal/obj/objfile.go
@@ -109,207 +109,21 @@ package obj
import (
"bufio"
+ "cmd/internal/sys"
"fmt"
"log"
"path/filepath"
"sort"
- "strings"
)
// The Go and C compilers, and the assembler, call writeobj to write
// out a Go object file. The linker does not call this; the linker
// does not write out object files.
-func Writeobjdirect(ctxt *Link, b *Biobuf) {
+func Writeobjdirect(ctxt *Link, b *bufio.Writer) {
Flushplist(ctxt)
WriteObjFile(ctxt, b)
}
-func Flushplist(ctxt *Link) {
- flushplist(ctxt, ctxt.Debugasm == 0)
-}
-func FlushplistNoFree(ctxt *Link) {
- flushplist(ctxt, false)
-}
-func flushplist(ctxt *Link, freeProgs bool) {
- // Build list of symbols, and assign instructions to lists.
- // Ignore ctxt->plist boundaries. There are no guarantees there,
- // and the assemblers just use one big list.
- var curtext *LSym
- var etext *Prog
- var text []*LSym
-
- for pl := ctxt.Plist; pl != nil; pl = pl.Link {
- var plink *Prog
- for p := pl.Firstpc; p != nil; p = plink {
- if ctxt.Debugasm != 0 && ctxt.Debugvlog != 0 {
- fmt.Printf("obj: %v\n", p)
- }
- plink = p.Link
- p.Link = nil
-
- switch p.As {
- case AEND:
- continue
-
- case ATYPE:
- // Assume each TYPE instruction describes
- // a different local variable or parameter,
- // so no dedup.
- // Using only the TYPE instructions means
- // that we discard location information about local variables
- // in C and assembly functions; that information is inferred
- // from ordinary references, because there are no TYPE
- // instructions there. Without the type information, gdb can't
- // use the locations, so we don't bother to save them.
- // If something else could use them, we could arrange to
- // preserve them.
- if curtext == nil {
- continue
- }
- a := new(Auto)
- a.Asym = p.From.Sym
- a.Aoffset = int32(p.From.Offset)
- a.Name = int16(p.From.Name)
- a.Gotype = p.From.Gotype
- a.Link = curtext.Autom
- curtext.Autom = a
- continue
-
- case AGLOBL:
- s := p.From.Sym
- if s.Seenglobl {
- fmt.Printf("duplicate %v\n", p)
- }
- s.Seenglobl = true
- if s.Onlist {
- log.Fatalf("symbol %s listed multiple times", s.Name)
- }
- s.Onlist = true
- ctxt.Data = append(ctxt.Data, s)
- s.Size = p.To.Offset
- if s.Type == 0 || s.Type == SXREF {
- s.Type = SBSS
- }
- flag := int(p.From3.Offset)
- if flag&DUPOK != 0 {
- s.Dupok = true
- }
- if flag&RODATA != 0 {
- s.Type = SRODATA
- } else if flag&NOPTR != 0 {
- s.Type = SNOPTRBSS
- } else if flag&TLSBSS != 0 {
- s.Type = STLSBSS
- }
- continue
-
- case ATEXT:
- s := p.From.Sym
- if s == nil {
- // func _() { }
- curtext = nil
-
- continue
- }
-
- if s.Text != nil {
- log.Fatalf("duplicate TEXT for %s", s.Name)
- }
- if s.Onlist {
- log.Fatalf("symbol %s listed multiple times", s.Name)
- }
- s.Onlist = true
- text = append(text, s)
- flag := int(p.From3Offset())
- if flag&DUPOK != 0 {
- s.Dupok = true
- }
- if flag&NOSPLIT != 0 {
- s.Nosplit = true
- }
- if flag&REFLECTMETHOD != 0 {
- s.ReflectMethod = true
- }
- s.Type = STEXT
- s.Text = p
- etext = p
- curtext = s
- continue
-
- case AFUNCDATA:
- // Rewrite reference to go_args_stackmap(SB) to the Go-provided declaration information.
- if curtext == nil { // func _() {}
- continue
- }
- if p.To.Sym.Name == "go_args_stackmap" {
- if p.From.Type != TYPE_CONST || p.From.Offset != FUNCDATA_ArgsPointerMaps {
- ctxt.Diag("FUNCDATA use of go_args_stackmap(SB) without FUNCDATA_ArgsPointerMaps")
- }
- p.To.Sym = Linklookup(ctxt, fmt.Sprintf("%s.args_stackmap", curtext.Name), int(curtext.Version))
- }
-
- }
-
- if curtext == nil {
- etext = nil
- continue
- }
- etext.Link = p
- etext = p
- }
- }
-
- // Add reference to Go arguments for C or assembly functions without them.
- for _, s := range text {
- if !strings.HasPrefix(s.Name, "\"\".") {
- continue
- }
- found := false
- var p *Prog
- for p = s.Text; p != nil; p = p.Link {
- if p.As == AFUNCDATA && p.From.Type == TYPE_CONST && p.From.Offset == FUNCDATA_ArgsPointerMaps {
- found = true
- break
- }
- }
-
- if !found {
- p = Appendp(ctxt, s.Text)
- p.As = AFUNCDATA
- p.From.Type = TYPE_CONST
- p.From.Offset = FUNCDATA_ArgsPointerMaps
- p.To.Type = TYPE_MEM
- p.To.Name = NAME_EXTERN
- p.To.Sym = Linklookup(ctxt, fmt.Sprintf("%s.args_stackmap", s.Name), int(s.Version))
- }
- }
-
- // Turn functions into machine code images.
- for _, s := range text {
- mkfwd(s)
- linkpatch(ctxt, s)
- if ctxt.Flag_optimize {
- ctxt.Arch.Follow(ctxt, s)
- }
- ctxt.Arch.Preprocess(ctxt, s)
- ctxt.Arch.Assemble(ctxt, s)
- fieldtrack(ctxt, s)
- linkpcln(ctxt, s)
- if freeProgs {
- s.Text = nil
- }
- }
-
- // Add to running list in ctxt.
- ctxt.Text = append(ctxt.Text, text...)
- ctxt.Plist = nil
- ctxt.Plast = nil
- ctxt.Curp = nil
- if freeProgs {
- ctxt.freeProgs()
- }
-}
-
// objWriter writes Go object files.
type objWriter struct {
wr *bufio.Writer
@@ -372,16 +186,16 @@ func (w *objWriter) writeLengths() {
w.writeInt(int64(w.nFile))
}
-func newObjWriter(ctxt *Link, b *Biobuf) *objWriter {
+func newObjWriter(ctxt *Link, b *bufio.Writer) *objWriter {
return &objWriter{
ctxt: ctxt,
- wr: b.w,
+ wr: b,
vrefIdx: make(map[string]int),
refIdx: make(map[string]int),
}
}
-func WriteObjFile(ctxt *Link, b *Biobuf) {
+func WriteObjFile(ctxt *Link, b *bufio.Writer) {
w := newObjWriter(ctxt, b)
// Magic header
@@ -556,10 +370,10 @@ func (w *objWriter) writeSymDebug(s *LSym) {
} else if r.Type == R_TLS_LE {
name = "TLS"
}
- if ctxt.Arch.Thechar == '5' || ctxt.Arch.Thechar == '9' {
- fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%x\n", int(r.Off), r.Siz, r.Type, name, uint64(int64(r.Add)))
+ if ctxt.Arch.InFamily(sys.ARM, sys.PPC64) {
+ fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%x\n", int(r.Off), r.Siz, r.Type, name, uint64(r.Add))
} else {
- fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%d\n", int(r.Off), r.Siz, r.Type, name, int64(r.Add))
+ fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%d\n", int(r.Off), r.Siz, r.Type, name, r.Add)
}
}
}
@@ -659,7 +473,7 @@ func (w *objWriter) writeSym(s *LSym) {
func (w *objWriter) writeInt(sval int64) {
var v uint64
- uv := (uint64(sval) << 1) ^ uint64(int64(sval>>63))
+ uv := (uint64(sval) << 1) ^ uint64(sval>>63)
p := w.varintbuf[:]
for v = uv; v >= 0x80; v >>= 7 {
p[0] = uint8(v | 0x80)
diff --git a/src/cmd/internal/obj/pcln.go b/src/cmd/internal/obj/pcln.go
index 9770c96fcc..b1536eb224 100644
--- a/src/cmd/internal/obj/pcln.go
+++ b/src/cmd/internal/obj/pcln.go
@@ -64,7 +64,7 @@ func funcpctab(ctxt *Link, dst *Pcdata, func_ *LSym, desc string, valfunc func(*
if val == oldval && started != 0 {
val = valfunc(ctxt, func_, val, p, 1, arg)
if ctxt.Debugpcln != 0 {
- fmt.Fprintf(ctxt.Bso, "%6x %6s %v\n", uint64(int64(p.Pc)), "", p)
+ fmt.Fprintf(ctxt.Bso, "%6x %6s %v\n", uint64(p.Pc), "", p)
}
continue
}
@@ -76,7 +76,7 @@ func funcpctab(ctxt *Link, dst *Pcdata, func_ *LSym, desc string, valfunc func(*
if p.Link != nil && p.Link.Pc == p.Pc {
val = valfunc(ctxt, func_, val, p, 1, arg)
if ctxt.Debugpcln != 0 {
- fmt.Fprintf(ctxt.Bso, "%6x %6s %v\n", uint64(int64(p.Pc)), "", p)
+ fmt.Fprintf(ctxt.Bso, "%6x %6s %v\n", uint64(p.Pc), "", p)
}
continue
}
@@ -96,11 +96,11 @@ func funcpctab(ctxt *Link, dst *Pcdata, func_ *LSym, desc string, valfunc func(*
// where the 0x80 bit indicates that the integer continues.
if ctxt.Debugpcln != 0 {
- fmt.Fprintf(ctxt.Bso, "%6x %6d %v\n", uint64(int64(p.Pc)), val, p)
+ fmt.Fprintf(ctxt.Bso, "%6x %6d %v\n", uint64(p.Pc), val, p)
}
if started != 0 {
- addvarint(ctxt, dst, uint32((p.Pc-pc)/int64(ctxt.Arch.Minlc)))
+ addvarint(ctxt, dst, uint32((p.Pc-pc)/int64(ctxt.Arch.MinLC)))
pc = p.Pc
}
@@ -118,9 +118,9 @@ func funcpctab(ctxt *Link, dst *Pcdata, func_ *LSym, desc string, valfunc func(*
if started != 0 {
if ctxt.Debugpcln != 0 {
- fmt.Fprintf(ctxt.Bso, "%6x done\n", uint64(int64(func_.Text.Pc)+func_.Size))
+ fmt.Fprintf(ctxt.Bso, "%6x done\n", uint64(func_.Text.Pc+func_.Size))
}
- addvarint(ctxt, dst, uint32((func_.Size-pc)/int64(ctxt.Arch.Minlc)))
+ addvarint(ctxt, dst, uint32((func_.Size-pc)/int64(ctxt.Arch.MinLC)))
addvarint(ctxt, dst, 0) // terminator
}
@@ -158,19 +158,18 @@ func pctofileline(ctxt *Link, sym *LSym, oldval int32, p *Prog, phase int32, arg
return int32(pcln.Lastindex)
}
- var i int32
- for i = 0; i < int32(len(pcln.File)); i++ {
- file := pcln.File[i]
+ for i, file := range pcln.File {
if file == f {
pcln.Lastfile = f
- pcln.Lastindex = int(i)
+ pcln.Lastindex = i
return int32(i)
}
}
+ i := len(pcln.File)
pcln.File = append(pcln.File, f)
pcln.Lastfile = f
- pcln.Lastindex = int(i)
- return i
+ pcln.Lastindex = i
+ return int32(i)
}
// pctospadj computes the sp adjustment in effect.
diff --git a/src/cmd/internal/obj/plist.go b/src/cmd/internal/obj/plist.go
new file mode 100644
index 0000000000..e55dbeca1e
--- /dev/null
+++ b/src/cmd/internal/obj/plist.go
@@ -0,0 +1,218 @@
+// 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 obj
+
+import (
+ "fmt"
+ "log"
+ "strings"
+)
+
+type Plist struct {
+ Name *LSym
+ Firstpc *Prog
+ Recur int
+ Link *Plist
+}
+
+/*
+ * start a new Prog list.
+ */
+func Linknewplist(ctxt *Link) *Plist {
+ pl := new(Plist)
+ if ctxt.Plist == nil {
+ ctxt.Plist = pl
+ } else {
+ ctxt.Plast.Link = pl
+ }
+ ctxt.Plast = pl
+ return pl
+}
+
+func Flushplist(ctxt *Link) {
+ flushplist(ctxt, ctxt.Debugasm == 0)
+}
+func FlushplistNoFree(ctxt *Link) {
+ flushplist(ctxt, false)
+}
+func flushplist(ctxt *Link, freeProgs bool) {
+ // Build list of symbols, and assign instructions to lists.
+ // Ignore ctxt->plist boundaries. There are no guarantees there,
+ // and the assemblers just use one big list.
+ var curtext *LSym
+ var etext *Prog
+ var text []*LSym
+
+ for pl := ctxt.Plist; pl != nil; pl = pl.Link {
+ var plink *Prog
+ for p := pl.Firstpc; p != nil; p = plink {
+ if ctxt.Debugasm != 0 && ctxt.Debugvlog != 0 {
+ fmt.Printf("obj: %v\n", p)
+ }
+ plink = p.Link
+ p.Link = nil
+
+ switch p.As {
+ case AEND:
+ continue
+
+ case ATYPE:
+ // Assume each TYPE instruction describes
+ // a different local variable or parameter,
+ // so no dedup.
+ // Using only the TYPE instructions means
+ // that we discard location information about local variables
+ // in C and assembly functions; that information is inferred
+ // from ordinary references, because there are no TYPE
+ // instructions there. Without the type information, gdb can't
+ // use the locations, so we don't bother to save them.
+ // If something else could use them, we could arrange to
+ // preserve them.
+ if curtext == nil {
+ continue
+ }
+ a := new(Auto)
+ a.Asym = p.From.Sym
+ a.Aoffset = int32(p.From.Offset)
+ a.Name = int16(p.From.Name)
+ a.Gotype = p.From.Gotype
+ a.Link = curtext.Autom
+ curtext.Autom = a
+ continue
+
+ case AGLOBL:
+ s := p.From.Sym
+ if s.Seenglobl {
+ fmt.Printf("duplicate %v\n", p)
+ }
+ s.Seenglobl = true
+ if s.Onlist {
+ log.Fatalf("symbol %s listed multiple times", s.Name)
+ }
+ s.Onlist = true
+ ctxt.Data = append(ctxt.Data, s)
+ s.Size = p.To.Offset
+ if s.Type == 0 || s.Type == SXREF {
+ s.Type = SBSS
+ }
+ flag := int(p.From3.Offset)
+ if flag&DUPOK != 0 {
+ s.Dupok = true
+ }
+ if flag&RODATA != 0 {
+ s.Type = SRODATA
+ } else if flag&NOPTR != 0 {
+ s.Type = SNOPTRBSS
+ } else if flag&TLSBSS != 0 {
+ s.Type = STLSBSS
+ }
+ continue
+
+ case ATEXT:
+ s := p.From.Sym
+ if s == nil {
+ // func _() { }
+ curtext = nil
+
+ continue
+ }
+
+ if s.Text != nil {
+ log.Fatalf("duplicate TEXT for %s", s.Name)
+ }
+ if s.Onlist {
+ log.Fatalf("symbol %s listed multiple times", s.Name)
+ }
+ s.Onlist = true
+ text = append(text, s)
+ flag := int(p.From3Offset())
+ if flag&DUPOK != 0 {
+ s.Dupok = true
+ }
+ if flag&NOSPLIT != 0 {
+ s.Nosplit = true
+ }
+ if flag&REFLECTMETHOD != 0 {
+ s.ReflectMethod = true
+ }
+ s.Type = STEXT
+ s.Text = p
+ etext = p
+ curtext = s
+ continue
+
+ case AFUNCDATA:
+ // Rewrite reference to go_args_stackmap(SB) to the Go-provided declaration information.
+ if curtext == nil { // func _() {}
+ continue
+ }
+ if p.To.Sym.Name == "go_args_stackmap" {
+ if p.From.Type != TYPE_CONST || p.From.Offset != FUNCDATA_ArgsPointerMaps {
+ ctxt.Diag("FUNCDATA use of go_args_stackmap(SB) without FUNCDATA_ArgsPointerMaps")
+ }
+ p.To.Sym = Linklookup(ctxt, fmt.Sprintf("%s.args_stackmap", curtext.Name), int(curtext.Version))
+ }
+
+ }
+
+ if curtext == nil {
+ etext = nil
+ continue
+ }
+ etext.Link = p
+ etext = p
+ }
+ }
+
+ // Add reference to Go arguments for C or assembly functions without them.
+ for _, s := range text {
+ if !strings.HasPrefix(s.Name, "\"\".") {
+ continue
+ }
+ found := false
+ var p *Prog
+ for p = s.Text; p != nil; p = p.Link {
+ if p.As == AFUNCDATA && p.From.Type == TYPE_CONST && p.From.Offset == FUNCDATA_ArgsPointerMaps {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ p = Appendp(ctxt, s.Text)
+ p.As = AFUNCDATA
+ p.From.Type = TYPE_CONST
+ p.From.Offset = FUNCDATA_ArgsPointerMaps
+ p.To.Type = TYPE_MEM
+ p.To.Name = NAME_EXTERN
+ p.To.Sym = Linklookup(ctxt, fmt.Sprintf("%s.args_stackmap", s.Name), int(s.Version))
+ }
+ }
+
+ // Turn functions into machine code images.
+ for _, s := range text {
+ mkfwd(s)
+ linkpatch(ctxt, s)
+ if ctxt.Flag_optimize {
+ ctxt.Arch.Follow(ctxt, s)
+ }
+ ctxt.Arch.Preprocess(ctxt, s)
+ ctxt.Arch.Assemble(ctxt, s)
+ fieldtrack(ctxt, s)
+ linkpcln(ctxt, s)
+ if freeProgs {
+ s.Text = nil
+ }
+ }
+
+ // Add to running list in ctxt.
+ ctxt.Text = append(ctxt.Text, text...)
+ ctxt.Plist = nil
+ ctxt.Plast = nil
+ ctxt.Curp = nil
+ if freeProgs {
+ ctxt.freeProgs()
+ }
+}
diff --git a/src/cmd/internal/obj/ppc64/asm9.go b/src/cmd/internal/obj/ppc64/asm9.go
index 0497d3b678..f786f3c443 100644
--- a/src/cmd/internal/obj/ppc64/asm9.go
+++ b/src/cmd/internal/obj/ppc64/asm9.go
@@ -585,7 +585,7 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int {
ctxt.Instoffset = a.Offset
if a.Sym != nil { // use relocation
if a.Sym.Type == obj.STLSBSS {
- if ctxt.Flag_shared != 0 {
+ if ctxt.Flag_shared {
return C_TLS_IE
} else {
return C_TLS_LE
@@ -1384,7 +1384,7 @@ const (
// which relocation to use with a load or store and only supports the needed
// instructions.
func opform(ctxt *obj.Link, insn uint32) int {
- switch uint32(insn) {
+ switch insn {
default:
ctxt.Diag("bad insn in loadform: %x", insn)
case OPVCC(58, 0, 0, 0), // ld
@@ -1413,7 +1413,7 @@ func opform(ctxt *obj.Link, insn uint32) int {
func symbolAccess(ctxt *obj.Link, s *obj.LSym, d int64, reg int16, op uint32) (o1, o2 uint32) {
var base uint32
form := opform(ctxt, op)
- if ctxt.Flag_shared != 0 {
+ if ctxt.Flag_shared {
base = REG_R2
} else {
base = REG_R0
@@ -1425,7 +1425,7 @@ func symbolAccess(ctxt *obj.Link, s *obj.LSym, d int64, reg int16, op uint32) (o
rel.Siz = 8
rel.Sym = s
rel.Add = d
- if ctxt.Flag_shared != 0 {
+ if ctxt.Flag_shared {
switch form {
case D_FORM:
rel.Type = obj.R_ADDRPOWER_TOCREL
@@ -1646,7 +1646,7 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
if v != 0 {
ctxt.Diag("illegal indexed instruction\n%v", p)
}
- if ctxt.Flag_shared != 0 && r == REG_R13 {
+ if ctxt.Flag_shared && r == REG_R13 {
rel := obj.Addrel(ctxt.Cursym)
rel.Off = int32(ctxt.Pc)
rel.Siz = 4
@@ -1677,7 +1677,7 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
if v != 0 {
ctxt.Diag("illegal indexed instruction\n%v", p)
}
- if ctxt.Flag_shared != 0 && r == REG_R13 {
+ if ctxt.Flag_shared && r == REG_R13 {
rel := obj.Addrel(ctxt.Cursym)
rel.Off = int32(ctxt.Pc)
rel.Siz = 4
@@ -2198,9 +2198,9 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
}
v := oprrr(ctxt, p.As)
t := v & (1<<10 | 1) /* OE|Rc */
- o1 = AOP_RRR(uint32(v)&^uint32(t), REGTMP, uint32(r), uint32(p.From.Reg))
+ o1 = AOP_RRR(v&^t, REGTMP, uint32(r), uint32(p.From.Reg))
o2 = AOP_RRR(OP_MULLW, REGTMP, REGTMP, uint32(p.From.Reg))
- o3 = AOP_RRR(OP_SUBF|uint32(t), uint32(p.To.Reg), REGTMP, uint32(r))
+ o3 = AOP_RRR(OP_SUBF|t, uint32(p.To.Reg), REGTMP, uint32(r))
if p.As == AREMU {
o4 = o3
@@ -2216,9 +2216,9 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
}
v := oprrr(ctxt, p.As)
t := v & (1<<10 | 1) /* OE|Rc */
- o1 = AOP_RRR(uint32(v)&^uint32(t), REGTMP, uint32(r), uint32(p.From.Reg))
+ o1 = AOP_RRR(v&^t, REGTMP, uint32(r), uint32(p.From.Reg))
o2 = AOP_RRR(OP_MULLD, REGTMP, REGTMP, uint32(p.From.Reg))
- o3 = AOP_RRR(OP_SUBF|uint32(t), uint32(p.To.Reg), REGTMP, uint32(r))
+ o3 = AOP_RRR(OP_SUBF|t, uint32(p.To.Reg), REGTMP, uint32(r))
case 52: /* mtfsbNx cr(n) */
v := regoff(ctxt, &p.From) & 31
@@ -2485,7 +2485,7 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
ctxt.Diag("invalid offset against tls var %v", p)
}
o1 = AOP_IRR(OP_ADDIS, uint32(p.To.Reg), REG_R2, 0)
- o2 = AOP_IRR(uint32(opload(ctxt, AMOVD)), uint32(p.To.Reg), uint32(p.To.Reg), 0)
+ o2 = AOP_IRR(opload(ctxt, AMOVD), uint32(p.To.Reg), uint32(p.To.Reg), 0)
rel := obj.Addrel(ctxt.Cursym)
rel.Off = int32(ctxt.Pc)
rel.Siz = 8
@@ -2499,7 +2499,7 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
}
o1 = AOP_IRR(OP_ADDIS, uint32(p.To.Reg), REG_R2, 0)
- o2 = AOP_IRR(uint32(opload(ctxt, AMOVD)), uint32(p.To.Reg), uint32(p.To.Reg), 0)
+ o2 = AOP_IRR(opload(ctxt, AMOVD), uint32(p.To.Reg), uint32(p.To.Reg), 0)
rel := obj.Addrel(ctxt.Cursym)
rel.Off = int32(ctxt.Pc)
rel.Siz = 8
diff --git a/src/cmd/internal/obj/ppc64/obj9.go b/src/cmd/internal/obj/ppc64/obj9.go
index 483df3a2b3..4f9b3943cf 100644
--- a/src/cmd/internal/obj/ppc64/obj9.go
+++ b/src/cmd/internal/obj/ppc64/obj9.go
@@ -31,7 +31,7 @@ package ppc64
import (
"cmd/internal/obj"
- "encoding/binary"
+ "cmd/internal/sys"
"fmt"
"math"
)
@@ -470,7 +470,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
q = p
- if ctxt.Flag_shared != 0 && cursym.Name != "runtime.duffzero" && cursym.Name != "runtime.duffcopy" && cursym.Name != "runtime.stackBarrier" {
+ if ctxt.Flag_shared && cursym.Name != "runtime.duffzero" && cursym.Name != "runtime.duffcopy" && cursym.Name != "runtime.stackBarrier" {
// When compiling Go into PIC, all functions must start
// with instructions to load the TOC pointer into r2:
//
@@ -558,7 +558,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
q.Spadj = int32(-aoffset)
}
- if ctxt.Flag_shared != 0 {
+ if ctxt.Flag_shared {
q = obj.Appendp(ctxt, q)
q.As = AMOVD
q.Lineno = p.Lineno
@@ -592,7 +592,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
q.As = AMOVD
q.From.Type = obj.TYPE_MEM
q.From.Reg = REGG
- q.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic
+ q.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
q.To.Type = obj.TYPE_REG
q.To.Reg = REG_R3
@@ -827,9 +827,9 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
p.As = AMOVD
p.From.Type = obj.TYPE_MEM
p.From.Reg = REGG
- p.From.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
+ p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
if ctxt.Cursym.Cfunc {
- p.From.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
+ p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
}
p.To.Type = obj.TYPE_REG
p.To.Reg = REG_R3
@@ -1181,27 +1181,17 @@ loop:
}
var Linkppc64 = obj.LinkArch{
- ByteOrder: binary.BigEndian,
- Name: "ppc64",
- Thechar: '9',
+ Arch: sys.ArchPPC64,
Preprocess: preprocess,
Assemble: span9,
Follow: follow,
Progedit: progedit,
- Minlc: 4,
- Ptrsize: 8,
- Regsize: 8,
}
var Linkppc64le = obj.LinkArch{
- ByteOrder: binary.LittleEndian,
- Name: "ppc64le",
- Thechar: '9',
+ Arch: sys.ArchPPC64LE,
Preprocess: preprocess,
Assemble: span9,
Follow: follow,
Progedit: progedit,
- Minlc: 4,
- Ptrsize: 8,
- Regsize: 8,
}
diff --git a/src/cmd/internal/obj/s390x/a.out.go b/src/cmd/internal/obj/s390x/a.out.go
index 2cb03ae603..e7256d1d41 100644
--- a/src/cmd/internal/obj/s390x/a.out.go
+++ b/src/cmd/internal/obj/s390x/a.out.go
@@ -218,6 +218,7 @@ const (
ADIVDU
AMULLW
AMULLD
+ AMULHD
AMULHDU
ASUB
ASUBC
diff --git a/src/cmd/internal/obj/s390x/anames.go b/src/cmd/internal/obj/s390x/anames.go
index e79a147a90..62dd181eda 100644
--- a/src/cmd/internal/obj/s390x/anames.go
+++ b/src/cmd/internal/obj/s390x/anames.go
@@ -17,6 +17,7 @@ var Anames = []string{
"DIVDU",
"MULLW",
"MULLD",
+ "MULHD",
"MULHDU",
"SUB",
"SUBC",
diff --git a/src/cmd/internal/obj/s390x/asmz.go b/src/cmd/internal/obj/s390x/asmz.go
index bccd7c3bd8..9b26580d11 100644
--- a/src/cmd/internal/obj/s390x/asmz.go
+++ b/src/cmd/internal/obj/s390x/asmz.go
@@ -150,6 +150,8 @@ var optab = []Optab{
Optab{AMULLW, C_REG, C_NONE, C_NONE, C_REG, 2, 0},
Optab{AMULLW, C_LCON, C_REG, C_NONE, C_REG, 22, 0},
Optab{AMULLW, C_LCON, C_NONE, C_NONE, C_REG, 22, 0},
+ Optab{AMULHD, C_REG, C_NONE, C_NONE, C_REG, 4, 0},
+ Optab{AMULHD, C_REG, C_REG, C_NONE, C_REG, 4, 0},
Optab{ASUBC, C_REG, C_REG, C_NONE, C_REG, 10, 0},
Optab{ASUBC, C_REG, C_NONE, C_NONE, C_REG, 10, 0},
Optab{ADIVW, C_REG, C_REG, C_NONE, C_REG, 2, 0},
@@ -471,7 +473,7 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int {
}
ctxt.Instoffset = a.Offset
if a.Sym.Type == obj.STLSBSS {
- if ctxt.Flag_shared != 0 {
+ if ctxt.Flag_shared {
return C_TLS_IE // initial exec model
}
return C_TLS_LE // local exec model
@@ -604,7 +606,7 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int {
func oplook(ctxt *obj.Link, p *obj.Prog) *Optab {
a1 := int(p.Optab)
if a1 != 0 {
- return &optab[a1-1:][0]
+ return &optab[a1-1]
}
a1 = int(p.From.Class)
if a1 == 0 {
@@ -793,10 +795,11 @@ func buildop(ctxt *obj.Link) {
case ADIVW:
opset(AADDE, r)
opset(AMULLD, r)
- opset(AMULHDU, r)
opset(ADIVD, r)
opset(ADIVDU, r)
opset(ADIVWU, r)
+ case AMULHD:
+ opset(AMULHDU, r)
case AMOVBZ:
opset(AMOVH, r)
opset(AMOVHZ, r)
@@ -2580,8 +2583,6 @@ func asmout(ctxt *obj.Link, asm *[]byte) {
opcode = op_MSGFR
case AMULLD:
opcode = op_MSGR
- case AMULHDU:
- opcode = op_MLGR
case ADIVW:
opcode = op_DSGFR
case ADIVWU:
@@ -2628,11 +2629,6 @@ func asmout(ctxt *obj.Link, asm *[]byte) {
zRRE(opcode, REGTMP, uint32(p.From.Reg), asm)
zRRE(op_LGR, uint32(p.To.Reg), REGTMP2, asm)
- case AMULHDU:
- zRRE(op_LGR, REGTMP2, uint32(r), asm)
- zRRE(opcode, REGTMP, uint32(p.From.Reg), asm)
- zRRE(op_LGR, uint32(p.To.Reg), REGTMP, asm)
-
case AFADD, AFADDS:
if r == int(p.To.Reg) {
zRRE(opcode, uint32(p.To.Reg), uint32(p.From.Reg), asm)
@@ -2695,6 +2691,28 @@ func asmout(ctxt *obj.Link, asm *[]byte) {
zRIL(_a, op_IIHF, uint32(p.To.Reg), uint32(v>>32), asm)
}
+ case 4: // multiply high (a*b)>>64
+ r := p.Reg
+ if r == 0 {
+ r = p.To.Reg
+ }
+ zRRE(op_LGR, REGTMP2, uint32(r), asm)
+ zRRE(op_MLGR, REGTMP, uint32(p.From.Reg), asm)
+ switch p.As {
+ case AMULHDU:
+ // Unsigned: move result into correct register.
+ zRRE(op_LGR, uint32(p.To.Reg), REGTMP, asm)
+ case AMULHD:
+ // Signed: need to convert result.
+ // See Hacker's Delight 8-3.
+ zRSY(op_SRAG, REGTMP2, uint32(p.From.Reg), 0, 63, asm)
+ zRRE(op_NGR, REGTMP2, uint32(r), asm)
+ zRRE(op_SGR, REGTMP, REGTMP2, asm)
+ zRSY(op_SRAG, REGTMP2, uint32(r), 0, 63, asm)
+ zRRE(op_NGR, REGTMP2, uint32(p.From.Reg), asm)
+ zRRF(op_SGRK, REGTMP2, 0, uint32(p.To.Reg), REGTMP, asm)
+ }
+
case 5: // syscall
zI(op_SVC, 0, asm)
diff --git a/src/cmd/internal/obj/s390x/objz.go b/src/cmd/internal/obj/s390x/objz.go
index 239deec6a1..454a0d76ec 100644
--- a/src/cmd/internal/obj/s390x/objz.go
+++ b/src/cmd/internal/obj/s390x/objz.go
@@ -31,7 +31,7 @@ package s390x
import (
"cmd/internal/obj"
- "encoding/binary"
+ "cmd/internal/sys"
"fmt"
"math"
)
@@ -460,7 +460,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
q.As = AMOVD
q.From.Type = obj.TYPE_MEM
q.From.Reg = REGG
- q.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic
+ q.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
q.To.Type = obj.TYPE_REG
q.To.Reg = REG_R3
@@ -664,9 +664,9 @@ func stacksplitPre(ctxt *obj.Link, p *obj.Prog, framesize int32) (*obj.Prog, *ob
p.As = AMOVD
p.From.Type = obj.TYPE_MEM
p.From.Reg = REGG
- p.From.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
+ p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
if ctxt.Cursym.Cfunc {
- p.From.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
+ p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
}
p.To.Type = obj.TYPE_REG
p.To.Reg = REG_R3
@@ -999,15 +999,10 @@ var unaryDst = map[obj.As]bool{
}
var Links390x = obj.LinkArch{
- ByteOrder: binary.BigEndian,
- Name: "s390x",
- Thechar: 'z',
+ Arch: sys.ArchS390X,
Preprocess: preprocess,
Assemble: spanz,
Follow: follow,
Progedit: progedit,
UnaryDst: unaryDst,
- Minlc: 2,
- Ptrsize: 8,
- Regsize: 8,
}
diff --git a/src/cmd/internal/obj/sym.go b/src/cmd/internal/obj/sym.go
index 64df62a2ae..beacc94ee8 100644
--- a/src/cmd/internal/obj/sym.go
+++ b/src/cmd/internal/obj/sym.go
@@ -32,6 +32,7 @@
package obj
import (
+ "cmd/internal/sys"
"log"
"os"
"path/filepath"
@@ -100,7 +101,7 @@ func Linknew(arch *LinkArch) *Link {
}
// On arm, record goarm.
- if ctxt.Arch.Thechar == '5' {
+ if ctxt.Arch.Family == sys.ARM {
ctxt.Goarm = Getgoarm()
}
diff --git a/src/cmd/internal/obj/util.go b/src/cmd/internal/obj/util.go
index 245fab9690..294cedcb0a 100644
--- a/src/cmd/internal/obj/util.go
+++ b/src/cmd/internal/obj/util.go
@@ -5,10 +5,8 @@
package obj
import (
- "bufio"
"bytes"
"fmt"
- "io"
"log"
"os"
"strings"
@@ -26,144 +24,6 @@ func Cputime() float64 {
return time.Since(start).Seconds()
}
-type Biobuf struct {
- f *os.File
- r *bufio.Reader
- w *bufio.Writer
- linelen int
-}
-
-func (b *Biobuf) Reader() *bufio.Reader { return b.r }
-
-func Bopenw(name string) (*Biobuf, error) {
- f, err := os.Create(name)
- if err != nil {
- return nil, err
- }
- return &Biobuf{f: f, w: bufio.NewWriter(f)}, nil
-}
-
-func Bopenr(name string) (*Biobuf, error) {
- f, err := os.Open(name)
- if err != nil {
- return nil, err
- }
- return &Biobuf{f: f, r: bufio.NewReader(f)}, nil
-}
-
-func Binitw(w io.Writer) *Biobuf {
- return &Biobuf{w: bufio.NewWriter(w)}
-}
-
-func Binitr(r io.Reader) *Biobuf {
- return &Biobuf{r: bufio.NewReader(r)}
-}
-
-func (b *Biobuf) Write(p []byte) (int, error) {
- return b.w.Write(p)
-}
-
-func Bwritestring(b *Biobuf, p string) (int, error) {
- return b.w.WriteString(p)
-}
-
-func Bseek(b *Biobuf, offset int64, whence int) int64 {
- if b.w != nil {
- if err := b.w.Flush(); err != nil {
- log.Fatalf("writing output: %v", err)
- }
- } else if b.r != nil {
- if whence == 1 {
- offset -= int64(b.r.Buffered())
- }
- }
- off, err := b.f.Seek(offset, whence)
- if err != nil {
- log.Fatalf("seeking in output: %v", err)
- }
- if b.r != nil {
- b.r.Reset(b.f)
- }
- return off
-}
-
-func Boffset(b *Biobuf) int64 {
- if b.w != nil {
- if err := b.w.Flush(); err != nil {
- log.Fatalf("writing output: %v", err)
- }
- }
- off, err := b.f.Seek(0, 1)
- if err != nil {
- log.Fatalf("seeking in output [0, 1]: %v", err)
- }
- if b.r != nil {
- off -= int64(b.r.Buffered())
- }
- return off
-}
-
-func (b *Biobuf) Flush() error {
- return b.w.Flush()
-}
-
-func Bputc(b *Biobuf, c byte) {
- b.w.WriteByte(c)
-}
-
-const Beof = -1
-
-func Bread(b *Biobuf, p []byte) int {
- n, err := io.ReadFull(b.r, p)
- if n == 0 {
- if err != nil && err != io.EOF {
- n = -1
- }
- }
- return n
-}
-
-func Bgetc(b *Biobuf) int {
- c, err := b.r.ReadByte()
- if err != nil {
- return -1
- }
- return int(c)
-}
-
-func (b *Biobuf) Read(p []byte) (int, error) {
- return b.r.Read(p)
-}
-
-func (b *Biobuf) Peek(n int) ([]byte, error) {
- return b.r.Peek(n)
-}
-
-func Brdline(b *Biobuf, delim int) string {
- s, err := b.r.ReadBytes(byte(delim))
- if err != nil {
- log.Fatalf("reading input: %v", err)
- }
- b.linelen = len(s)
- return string(s)
-}
-
-func Blinelen(b *Biobuf) int {
- return b.linelen
-}
-
-func Bterm(b *Biobuf) error {
- var err error
- if b.w != nil {
- err = b.w.Flush()
- }
- err1 := b.f.Close()
- if err == nil {
- err = err1
- }
- return err
-}
-
func envOr(key, value string) string {
if x := os.Getenv(key); x != "" {
return x
@@ -419,7 +279,7 @@ func Dconv(p *Prog, a *Addr) string {
case TYPE_SHIFT:
v := int(a.Offset)
- op := string("<<>>->@>"[((v>>5)&3)<<1:])
+ op := "<<>>->@>"[((v>>5)&3)<<1:]
if v&(1<<4) != 0 {
str = fmt.Sprintf("R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15)
} else {
diff --git a/src/cmd/internal/obj/x86/a.out.go b/src/cmd/internal/obj/x86/a.out.go
index 64bd865e42..c41fd953e7 100644
--- a/src/cmd/internal/obj/x86/a.out.go
+++ b/src/cmd/internal/obj/x86/a.out.go
@@ -739,6 +739,8 @@ const (
AUNPCKLPS
AXORPD
AXORPS
+ APCMPESTRI
+
ARETFW
ARETFL
ARETFQ
diff --git a/src/cmd/internal/obj/x86/anames.go b/src/cmd/internal/obj/x86/anames.go
index 3b59e2f36f..e3fef54e71 100644
--- a/src/cmd/internal/obj/x86/anames.go
+++ b/src/cmd/internal/obj/x86/anames.go
@@ -682,6 +682,7 @@ var Anames = []string{
"UNPCKLPS",
"XORPD",
"XORPS",
+ "PCMPESTRI",
"RETFW",
"RETFL",
"RETFQ",
diff --git a/src/cmd/internal/obj/x86/asm6.go b/src/cmd/internal/obj/x86/asm6.go
index 773494b7af..1c7fcf37be 100644
--- a/src/cmd/internal/obj/x86/asm6.go
+++ b/src/cmd/internal/obj/x86/asm6.go
@@ -884,11 +884,6 @@ var yvex_vpbroadcast = []ytab{
{Yxm, Ynone, Yyr, Zvex_rm_v_r, 2},
}
-var yvex_xxmyxm = []ytab{
- {Yxr, Ynone, Yxm, Zvex_r_v_rm, 2},
- {Yyr, Ynone, Yxm, Zvex_r_v_rm, 2},
-}
-
var ymmxmm0f38 = []ytab{
{Ymm, Ynone, Ymr, Zlitm_r, 3},
{Yxm, Ynone, Yxr, Zlitm_r, 5},
@@ -1653,6 +1648,7 @@ var optab =
{AROUNDSS, yaes2, Pq, [23]uint8{0x3a, 0x0a, 0}},
{APSHUFD, yxshuf, Pq, [23]uint8{0x70, 0}},
{APCLMULQDQ, yxshuf, Pq, [23]uint8{0x3a, 0x44, 0}},
+ {APCMPESTRI, yxshuf, Pq, [23]uint8{0x3a, 0x61, 0}},
{AANDNL, yvex_r3, Pvex, [23]uint8{VEX_LZ_0F38_W0, 0xF2}},
{AANDNQ, yvex_r3, Pvex, [23]uint8{VEX_LZ_0F38_W1, 0xF2}},
@@ -1754,7 +1750,7 @@ func naclpad(ctxt *obj.Link, s *obj.LSym, c int32, pad int32) int32 {
}
func spadjop(ctxt *obj.Link, p *obj.Prog, l, q obj.As) obj.As {
- if p.Mode != 64 || ctxt.Arch.Ptrsize == 4 {
+ if p.Mode != 64 || ctxt.Arch.PtrSize == 4 {
return l
}
return q
@@ -2170,7 +2166,7 @@ func prefixof(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) int {
return 0x64 // FS
}
- if ctxt.Flag_shared != 0 {
+ if ctxt.Flag_shared {
log.Fatalf("unknown TLS base register for linux with -shared")
} else {
return 0x64 // FS
@@ -2190,7 +2186,7 @@ func prefixof(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) int {
}
if p.Mode == 32 {
- if a.Index == REG_TLS && ctxt.Flag_shared != 0 {
+ if a.Index == REG_TLS && ctxt.Flag_shared {
// When building for inclusion into a shared library, an instruction of the form
// MOVL 0(CX)(TLS*1), AX
// becomes
@@ -2219,7 +2215,7 @@ func prefixof(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) int {
return 0x26
case REG_TLS:
- if ctxt.Flag_shared != 0 {
+ if ctxt.Flag_shared {
// When building for inclusion into a shared library, an instruction of the form
// MOV 0(CX)(TLS*1), AX
// becomes
@@ -2293,7 +2289,7 @@ func oclass(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) int {
case obj.NAME_EXTERN,
obj.NAME_STATIC:
- if a.Sym != nil && isextern(a.Sym) || (p.Mode == 32 && ctxt.Flag_shared == 0) {
+ if a.Sym != nil && isextern(a.Sym) || (p.Mode == 32 && !ctxt.Flag_shared) {
return Yi32
}
return Yiauto // use pc-relative addressing
@@ -2712,7 +2708,7 @@ func vaddr(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r *obj.Reloc) int64 {
if a.Name == obj.NAME_GOTREF {
r.Siz = 4
r.Type = obj.R_GOTPCREL
- } else if isextern(s) || (p.Mode != 64 && ctxt.Flag_shared == 0) {
+ } else if isextern(s) || (p.Mode != 64 && !ctxt.Flag_shared) {
r.Siz = 4
r.Type = obj.R_ADDR
} else {
@@ -2733,7 +2729,7 @@ func vaddr(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r *obj.Reloc) int64 {
log.Fatalf("reloc")
}
- if ctxt.Flag_shared == 0 || isAndroid {
+ if !ctxt.Flag_shared || isAndroid {
r.Type = obj.R_TLS_LE
r.Siz = 4
r.Off = -1 // caller must fill in
@@ -2798,7 +2794,7 @@ func asmandsz(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r int, rex int, m64 int)
if !isextern(a.Sym) && p.Mode == 64 {
goto bad
}
- if p.Mode == 32 && ctxt.Flag_shared != 0 {
+ if p.Mode == 32 && ctxt.Flag_shared {
base = REG_CX
} else {
base = REG_NONE
@@ -2843,7 +2839,7 @@ func asmandsz(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r int, rex int, m64 int)
if a.Sym == nil {
ctxt.Diag("bad addr: %v", p)
}
- if p.Mode == 32 && ctxt.Flag_shared != 0 {
+ if p.Mode == 32 && ctxt.Flag_shared {
base = REG_CX
} else {
base = REG_NONE
@@ -2897,7 +2893,7 @@ func asmandsz(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r int, rex int, m64 int)
}
if REG_AX <= base && base <= REG_R15 {
- if a.Index == REG_TLS && ctxt.Flag_shared == 0 {
+ if a.Index == REG_TLS && !ctxt.Flag_shared {
rel = obj.Reloc{}
rel.Type = obj.R_TLS_LE
rel.Siz = 4
@@ -3313,7 +3309,7 @@ func doasm(ctxt *obj.Link, p *obj.Prog) {
case Pf2, /* xmm opcode escape */
Pf3:
- ctxt.AsmBuf.Put2(byte(o.prefix), Pm)
+ ctxt.AsmBuf.Put2(o.prefix, Pm)
case Pef3:
ctxt.AsmBuf.Put3(Pe, Pf3, Pm)
@@ -3426,7 +3422,7 @@ func doasm(ctxt *obj.Link, p *obj.Prog) {
asmand(ctxt, p, &p.From, &p.To)
case Zm2_r:
- ctxt.AsmBuf.Put2(byte(op), byte(o.op[z+1]))
+ ctxt.AsmBuf.Put2(byte(op), o.op[z+1])
asmand(ctxt, p, &p.From, &p.To)
case Zm_r_xm:
@@ -3536,7 +3532,7 @@ func doasm(ctxt *obj.Link, p *obj.Prog) {
}
ctxt.AsmBuf.Put1(byte(op))
if p.As == AXABORT {
- ctxt.AsmBuf.Put1(byte(o.op[z+1]))
+ ctxt.AsmBuf.Put1(o.op[z+1])
}
ctxt.AsmBuf.Put1(byte(vaddr(ctxt, p, a, nil)))
@@ -3662,7 +3658,7 @@ func doasm(ctxt *obj.Link, p *obj.Prog) {
if yt.zcase == Zcallcon {
ctxt.AsmBuf.Put1(byte(op))
} else {
- ctxt.AsmBuf.Put1(byte(o.op[z+1]))
+ ctxt.AsmBuf.Put1(o.op[z+1])
}
r = obj.Addrel(ctxt.Cursym)
r.Off = int32(p.Pc + int64(ctxt.AsmBuf.Len()))
@@ -3672,7 +3668,7 @@ func doasm(ctxt *obj.Link, p *obj.Prog) {
ctxt.AsmBuf.PutInt32(0)
case Zcallind:
- ctxt.AsmBuf.Put2(byte(op), byte(o.op[z+1]))
+ ctxt.AsmBuf.Put2(byte(op), o.op[z+1])
r = obj.Addrel(ctxt.Cursym)
r.Off = int32(p.Pc + int64(ctxt.AsmBuf.Len()))
r.Type = obj.R_ADDR
@@ -3727,7 +3723,7 @@ func doasm(ctxt *obj.Link, p *obj.Prog) {
log.Fatalf("bad code")
}
- ctxt.AsmBuf.Put1(byte(o.op[z+1]))
+ ctxt.AsmBuf.Put1(o.op[z+1])
r = obj.Addrel(ctxt.Cursym)
r.Off = int32(p.Pc + int64(ctxt.AsmBuf.Len()))
r.Sym = p.To.Sym
@@ -3767,7 +3763,7 @@ func doasm(ctxt *obj.Link, p *obj.Prog) {
v--
}
- ctxt.AsmBuf.Put1(byte(o.op[z+1]))
+ ctxt.AsmBuf.Put1(o.op[z+1])
ctxt.AsmBuf.PutInt32(int32(v))
}
@@ -3789,7 +3785,7 @@ func doasm(ctxt *obj.Link, p *obj.Prog) {
if yt.zcase == Zbr {
ctxt.AsmBuf.Put1(0x0f)
}
- ctxt.AsmBuf.Put1(byte(o.op[z+1]))
+ ctxt.AsmBuf.Put1(o.op[z+1])
ctxt.AsmBuf.PutInt32(0)
}
@@ -3950,7 +3946,7 @@ func doasm(ctxt *obj.Link, p *obj.Prog) {
case obj.Hlinux,
obj.Hnacl:
- if ctxt.Flag_shared != 0 {
+ if ctxt.Flag_shared {
// Note that this is not generating the same insns as the other cases.
// MOV TLS, R_to
// becomes
@@ -4024,7 +4020,7 @@ func doasm(ctxt *obj.Link, p *obj.Prog) {
log.Fatalf("unknown TLS base location for %s", obj.Headstr(ctxt.Headtype))
case obj.Hlinux:
- if ctxt.Flag_shared == 0 {
+ if !ctxt.Flag_shared {
log.Fatalf("unknown TLS base location for linux without -shared")
}
// Note that this is not generating the same insn as the other cases.
@@ -4457,9 +4453,8 @@ func asmins(ctxt *obj.Link, p *obj.Prog) {
}
n := ctxt.AsmBuf.Len()
- var r *obj.Reloc
for i := len(ctxt.Cursym.R) - 1; i >= 0; i-- {
- r = &ctxt.Cursym.R[i:][0]
+ r := &ctxt.Cursym.R[i]
if int64(r.Off) < p.Pc {
break
}
diff --git a/src/cmd/internal/obj/x86/obj6.go b/src/cmd/internal/obj/x86/obj6.go
index 0e8aeca4d3..b638c048e8 100644
--- a/src/cmd/internal/obj/x86/obj6.go
+++ b/src/cmd/internal/obj/x86/obj6.go
@@ -32,7 +32,7 @@ package x86
import (
"cmd/internal/obj"
- "encoding/binary"
+ "cmd/internal/sys"
"fmt"
"log"
"math"
@@ -49,7 +49,7 @@ func CanUse1InsnTLS(ctxt *obj.Link) bool {
return true
}
- if ctxt.Arch.Regsize == 4 {
+ if ctxt.Arch.RegSize == 4 {
switch ctxt.Headtype {
case obj.Hlinux,
obj.Hnacl,
@@ -66,7 +66,7 @@ func CanUse1InsnTLS(ctxt *obj.Link) bool {
obj.Hwindows:
return false
case obj.Hlinux:
- return ctxt.Flag_shared == 0
+ return !ctxt.Flag_shared
}
return true
@@ -75,7 +75,7 @@ func CanUse1InsnTLS(ctxt *obj.Link) bool {
func progedit(ctxt *obj.Link, p *obj.Prog) {
// Maintain information about code generation mode.
if ctxt.Mode == 0 {
- ctxt.Mode = ctxt.Arch.Regsize * 8
+ ctxt.Mode = ctxt.Arch.RegSize * 8
}
p.Mode = int8(ctxt.Mode)
@@ -207,7 +207,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
}
// Rewrite MOVL/MOVQ $XXX(FP/SP) as LEAL/LEAQ.
- if p.From.Type == obj.TYPE_ADDR && (ctxt.Arch.Thechar == '6' || p.From.Name != obj.NAME_EXTERN && p.From.Name != obj.NAME_STATIC) {
+ if p.From.Type == obj.TYPE_ADDR && (ctxt.Arch.Family == sys.AMD64 || p.From.Name != obj.NAME_EXTERN && p.From.Name != obj.NAME_STATIC) {
switch p.As {
case AMOVL:
p.As = ALEAL
@@ -314,7 +314,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
rewriteToUseGot(ctxt, p)
}
- if ctxt.Flag_shared != 0 && p.Mode == 32 {
+ if ctxt.Flag_shared && p.Mode == 32 {
rewriteToPcrel(ctxt, p)
}
}
@@ -614,7 +614,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
// Make room for to save a base pointer. If autoffset == 0,
// this might do something special like a tail jump to
// another function, so in that case we omit this.
- bpsize = ctxt.Arch.Ptrsize
+ bpsize = ctxt.Arch.PtrSize
autoffset += int32(bpsize)
p.To.Offset += int64(bpsize)
@@ -656,7 +656,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
}
if autoffset != 0 {
- if autoffset%int32(ctxt.Arch.Regsize) != 0 {
+ if autoffset%int32(ctxt.Arch.RegSize) != 0 {
ctxt.Diag("unaligned stack size %d", autoffset)
}
p = obj.Appendp(ctxt, p)
@@ -671,10 +671,10 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
p = obj.Appendp(ctxt, p)
p.As = obj.ANOP
- p.Spadj = int32(-ctxt.Arch.Ptrsize)
+ p.Spadj = int32(-ctxt.Arch.PtrSize)
p = obj.Appendp(ctxt, p)
p.As = obj.ANOP
- p.Spadj = int32(ctxt.Arch.Ptrsize)
+ p.Spadj = int32(ctxt.Arch.PtrSize)
}
deltasp := autoffset
@@ -724,7 +724,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
p.As = AMOVQ
p.From.Type = obj.TYPE_MEM
p.From.Reg = REG_CX
- p.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic
+ p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
p.To.Type = obj.TYPE_REG
p.To.Reg = REG_BX
if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
@@ -757,7 +757,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
p.As = ALEAQ
p.From.Type = obj.TYPE_MEM
p.From.Reg = REG_SP
- p.From.Offset = int64(autoffset) + int64(ctxt.Arch.Regsize)
+ p.From.Offset = int64(autoffset) + int64(ctxt.Arch.RegSize)
p.To.Type = obj.TYPE_REG
p.To.Reg = REG_DI
if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
@@ -935,7 +935,7 @@ func indir_cx(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
// Returns last new instruction.
func load_g_cx(ctxt *obj.Link, p *obj.Prog) *obj.Prog {
p.As = AMOVQ
- if ctxt.Arch.Ptrsize == 4 {
+ if ctxt.Arch.PtrSize == 4 {
p.As = AMOVL
}
p.From.Type = obj.TYPE_MEM
@@ -984,9 +984,9 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *ob
p.From.Type = obj.TYPE_REG
p.From.Reg = REG_SP
indir_cx(ctxt, p, &p.To)
- p.To.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
+ p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
if ctxt.Cursym.Cfunc {
- p.To.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
+ p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
}
} else if framesize <= obj.StackBig {
// large stack: SP-framesize <= stackguard-StackSmall
@@ -1006,9 +1006,9 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *ob
p.From.Type = obj.TYPE_REG
p.From.Reg = REG_AX
indir_cx(ctxt, p, &p.To)
- p.To.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
+ p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
if ctxt.Cursym.Cfunc {
- p.To.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
+ p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
}
} else {
// Such a large stack we need to protect against wraparound.
@@ -1030,9 +1030,9 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *ob
p.As = mov
indir_cx(ctxt, p, &p.From)
- p.From.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
+ p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
if ctxt.Cursym.Cfunc {
- p.From.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
+ p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
}
p.To.Type = obj.TYPE_REG
p.To.Reg = REG_SI
@@ -1402,43 +1402,28 @@ var unaryDst = map[obj.As]bool{
}
var Linkamd64 = obj.LinkArch{
- ByteOrder: binary.LittleEndian,
- Name: "amd64",
- Thechar: '6',
+ Arch: sys.ArchAMD64,
Preprocess: preprocess,
Assemble: span6,
Follow: follow,
Progedit: progedit,
UnaryDst: unaryDst,
- Minlc: 1,
- Ptrsize: 8,
- Regsize: 8,
}
var Linkamd64p32 = obj.LinkArch{
- ByteOrder: binary.LittleEndian,
- Name: "amd64p32",
- Thechar: '6',
+ Arch: sys.ArchAMD64P32,
Preprocess: preprocess,
Assemble: span6,
Follow: follow,
Progedit: progedit,
UnaryDst: unaryDst,
- Minlc: 1,
- Ptrsize: 4,
- Regsize: 8,
}
var Link386 = obj.LinkArch{
- ByteOrder: binary.LittleEndian,
- Name: "386",
- Thechar: '8',
+ Arch: sys.Arch386,
Preprocess: preprocess,
Assemble: span6,
Follow: follow,
Progedit: progedit,
UnaryDst: unaryDst,
- Minlc: 1,
- Ptrsize: 4,
- Regsize: 4,
}
diff --git a/src/cmd/internal/obj/x86/obj6_test.go b/src/cmd/internal/obj/x86/obj6_test.go
index a5c80cea3b..fe1f95cc0d 100644
--- a/src/cmd/internal/obj/x86/obj6_test.go
+++ b/src/cmd/internal/obj/x86/obj6_test.go
@@ -76,7 +76,6 @@ func parseTestData(t *testing.T) *ParsedTestData {
}
var spaces_re *regexp.Regexp = regexp.MustCompile("\\s+")
-var marker_re *regexp.Regexp = regexp.MustCompile("MOVQ \\$([0-9]+), AX")
func normalize(s string) string {
return spaces_re.ReplaceAllLiteralString(strings.TrimSpace(s), " ")
diff --git a/src/cmd/internal/objfile/pe.go b/src/cmd/internal/objfile/pe.go
index 1b319941ac..c024762371 100644
--- a/src/cmd/internal/objfile/pe.go
+++ b/src/cmd/internal/objfile/pe.go
@@ -69,8 +69,6 @@ func (f *peFile) symbols() ([]Sym, error) {
text = 0x20
data = 0x40
bss = 0x80
- permX = 0x20000000
- permR = 0x40000000
permW = 0x80000000
)
ch := sect.Characteristics
diff --git a/src/cmd/internal/objfile/plan9obj.go b/src/cmd/internal/objfile/plan9obj.go
index 1d808f77eb..6ee389dc2e 100644
--- a/src/cmd/internal/objfile/plan9obj.go
+++ b/src/cmd/internal/objfile/plan9obj.go
@@ -59,7 +59,7 @@ func (f *plan9File) symbols() ([]Sym, error) {
if !validSymType[s.Type] {
continue
}
- sym := Sym{Addr: s.Value, Name: s.Name, Code: rune(s.Type)}
+ sym := Sym{Addr: s.Value, Name: s.Name, Code: s.Type}
i := sort.Search(len(addrs), func(x int) bool { return addrs[x] > s.Value })
if i < len(addrs) {
sym.Size = int64(addrs[i] - s.Value)
diff --git a/src/cmd/pprof/internal/commands/commands.go b/src/cmd/internal/pprof/commands/commands.go
index 9aeee5762e..5018c02af1 100644
--- a/src/cmd/pprof/internal/commands/commands.go
+++ b/src/cmd/internal/pprof/commands/commands.go
@@ -16,10 +16,10 @@ import (
"strings"
"time"
- "cmd/pprof/internal/plugin"
- "cmd/pprof/internal/report"
- "cmd/pprof/internal/svg"
- "cmd/pprof/internal/tempfile"
+ "cmd/internal/pprof/plugin"
+ "cmd/internal/pprof/report"
+ "cmd/internal/pprof/svg"
+ "cmd/internal/pprof/tempfile"
)
// Commands describes the commands accepted by pprof.
diff --git a/src/cmd/pprof/internal/driver/driver.go b/src/cmd/internal/pprof/driver/driver.go
index 7cd1ddc928..782acfdf32 100644
--- a/src/cmd/pprof/internal/driver/driver.go
+++ b/src/cmd/internal/pprof/driver/driver.go
@@ -21,11 +21,11 @@ import (
"sync"
"time"
- "cmd/pprof/internal/commands"
- "cmd/pprof/internal/plugin"
- "cmd/pprof/internal/profile"
- "cmd/pprof/internal/report"
- "cmd/pprof/internal/tempfile"
+ "cmd/internal/pprof/commands"
+ "cmd/internal/pprof/plugin"
+ "cmd/internal/pprof/profile"
+ "cmd/internal/pprof/report"
+ "cmd/internal/pprof/tempfile"
)
// PProf acquires a profile, and symbolizes it using a profile
diff --git a/src/cmd/pprof/internal/driver/interactive.go b/src/cmd/internal/pprof/driver/interactive.go
index 13009bf7e9..1b08226527 100644
--- a/src/cmd/pprof/internal/driver/interactive.go
+++ b/src/cmd/internal/pprof/driver/interactive.go
@@ -12,9 +12,9 @@ import (
"strconv"
"strings"
- "cmd/pprof/internal/commands"
- "cmd/pprof/internal/plugin"
- "cmd/pprof/internal/profile"
+ "cmd/internal/pprof/commands"
+ "cmd/internal/pprof/plugin"
+ "cmd/internal/pprof/profile"
)
var profileFunctionNames = []string{}
diff --git a/src/cmd/pprof/internal/fetch/fetch.go b/src/cmd/internal/pprof/fetch/fetch.go
index ec4a6383c6..ffd282e74d 100644
--- a/src/cmd/pprof/internal/fetch/fetch.go
+++ b/src/cmd/internal/pprof/fetch/fetch.go
@@ -16,8 +16,8 @@ import (
"strings"
"time"
- "cmd/pprof/internal/plugin"
- "cmd/pprof/internal/profile"
+ "cmd/internal/pprof/plugin"
+ "cmd/internal/pprof/profile"
)
// FetchProfile reads from a data source (network, file) and generates a
diff --git a/src/cmd/pprof/internal/plugin/plugin.go b/src/cmd/internal/pprof/plugin/plugin.go
index a22ec5f3c5..d5025d5517 100644
--- a/src/cmd/pprof/internal/plugin/plugin.go
+++ b/src/cmd/internal/pprof/plugin/plugin.go
@@ -13,7 +13,7 @@ import (
"strings"
"time"
- "cmd/pprof/internal/profile"
+ "cmd/internal/pprof/profile"
)
// A FlagSet creates and parses command-line flags.
diff --git a/src/cmd/pprof/internal/profile/encode.go b/src/cmd/internal/pprof/profile/encode.go
index 6b879a84ac..6b879a84ac 100644
--- a/src/cmd/pprof/internal/profile/encode.go
+++ b/src/cmd/internal/pprof/profile/encode.go
diff --git a/src/cmd/pprof/internal/profile/filter.go b/src/cmd/internal/pprof/profile/filter.go
index 1baa096a49..1baa096a49 100644
--- a/src/cmd/pprof/internal/profile/filter.go
+++ b/src/cmd/internal/pprof/profile/filter.go
diff --git a/src/cmd/pprof/internal/profile/legacy_profile.go b/src/cmd/internal/pprof/profile/legacy_profile.go
index e1f24c4c6d..8ccfe45176 100644
--- a/src/cmd/pprof/internal/profile/legacy_profile.go
+++ b/src/cmd/internal/pprof/profile/legacy_profile.go
@@ -74,7 +74,7 @@ func parseGoCount(b []byte) (*Profile, error) {
if m == nil {
return nil, errUnrecognized
}
- profileType := string(m[1])
+ profileType := m[1]
p := &Profile{
PeriodType: &ValueType{Type: profileType, Unit: "count"},
Period: 1,
@@ -99,22 +99,19 @@ func parseGoCount(b []byte) (*Profile, error) {
if m == nil {
return nil, errMalformed
}
- n, err := strconv.ParseInt(string(m[1]), 0, 64)
+ n, err := strconv.ParseInt(m[1], 0, 64)
if err != nil {
return nil, errMalformed
}
- fields := strings.Fields(string(m[2]))
+ fields := strings.Fields(m[2])
locs := make([]*Location, 0, len(fields))
for _, stk := range fields {
addr, err := strconv.ParseUint(stk, 0, 64)
if err != nil {
return nil, errMalformed
}
- // Adjust all frames by -1 (except the leaf) to land on top of
- // the call instruction.
- if len(locs) > 0 {
- addr--
- }
+ // Adjust all frames by -1 to land on the call instruction.
+ addr--
loc := locations[addr]
if loc == nil {
loc = &Location{
@@ -291,11 +288,8 @@ func ParseTracebacks(b []byte) (*Profile, error) {
if s, addrs := extractHexAddresses(l); len(s) > 0 {
for _, addr := range addrs {
// Addresses from stack traces point to the next instruction after
- // each call. Adjust by -1 to land somewhere on the actual call
- // (except for the leaf, which is not a call).
- if len(sloc) > 0 {
- addr--
- }
+ // each call. Adjust by -1 to land somewhere on the actual call.
+ addr--
loc := locs[addr]
if locs[addr] == nil {
loc = &Location{
@@ -464,7 +458,7 @@ func parseCPUSamples(b []byte, parse func(b []byte) (uint64, []byte), adjust boo
}
p.Sample = append(p.Sample,
&Sample{
- Value: []int64{int64(count), int64(count) * int64(p.Period)},
+ Value: []int64{int64(count), int64(count) * p.Period},
Location: sloc,
})
}
@@ -494,7 +488,7 @@ func parseHeap(b []byte) (p *Profile, err error) {
var period int64
if len(header[6]) > 0 {
- if period, err = strconv.ParseInt(string(header[6]), 10, 64); err != nil {
+ if period, err = strconv.ParseInt(header[6], 10, 64); err != nil {
return nil, errUnrecognized
}
}
@@ -568,13 +562,10 @@ func parseHeap(b []byte) (p *Profile, err error) {
return nil, err
}
var sloc []*Location
- for i, addr := range addrs {
+ for _, addr := range addrs {
// Addresses from stack traces point to the next instruction after
- // each call. Adjust by -1 to land somewhere on the actual call
- // (except for the leaf, which is not a call).
- if i > 0 {
- addr--
- }
+ // each call. Adjust by -1 to land somewhere on the actual call.
+ addr--
loc := locs[addr]
if locs[addr] == nil {
loc = &Location{
@@ -776,13 +767,10 @@ func parseContention(b []byte) (p *Profile, err error) {
return nil, err
}
var sloc []*Location
- for i, addr := range addrs {
+ for _, addr := range addrs {
// Addresses from stack traces point to the next instruction after
- // each call. Adjust by -1 to land somewhere on the actual call
- // (except for the leaf, which is not a call).
- if i > 0 {
- addr--
- }
+ // each call. Adjust by -1 to land somewhere on the actual call.
+ addr--
loc := locs[addr]
if locs[addr] == nil {
loc = &Location{
@@ -919,13 +907,10 @@ func parseThread(b []byte) (*Profile, error) {
}
var sloc []*Location
- for i, addr := range addrs {
+ for _, addr := range addrs {
// Addresses from stack traces point to the next instruction after
- // each call. Adjust by -1 to land somewhere on the actual call
- // (except for the leaf, which is not a call).
- if i > 0 {
- addr--
- }
+ // each call. Adjust by -1 to land somewhere on the actual call.
+ addr--
loc := locs[addr]
if locs[addr] == nil {
loc = &Location{
diff --git a/src/cmd/pprof/internal/profile/profile.go b/src/cmd/internal/pprof/profile/profile.go
index 28e713d7be..28e713d7be 100644
--- a/src/cmd/pprof/internal/profile/profile.go
+++ b/src/cmd/internal/pprof/profile/profile.go
diff --git a/src/cmd/pprof/internal/profile/profile_test.go b/src/cmd/internal/pprof/profile/profile_test.go
index 09b11a456f..09b11a456f 100644
--- a/src/cmd/pprof/internal/profile/profile_test.go
+++ b/src/cmd/internal/pprof/profile/profile_test.go
diff --git a/src/cmd/pprof/internal/profile/proto.go b/src/cmd/internal/pprof/profile/proto.go
index 11d7f9ff9b..11d7f9ff9b 100644
--- a/src/cmd/pprof/internal/profile/proto.go
+++ b/src/cmd/internal/pprof/profile/proto.go
diff --git a/src/cmd/pprof/internal/profile/proto_test.go b/src/cmd/internal/pprof/profile/proto_test.go
index c2613fc375..c2613fc375 100644
--- a/src/cmd/pprof/internal/profile/proto_test.go
+++ b/src/cmd/internal/pprof/profile/proto_test.go
diff --git a/src/cmd/pprof/internal/profile/prune.go b/src/cmd/internal/pprof/profile/prune.go
index 1924fada7a..1924fada7a 100644
--- a/src/cmd/pprof/internal/profile/prune.go
+++ b/src/cmd/internal/pprof/profile/prune.go
diff --git a/src/cmd/pprof/internal/report/report.go b/src/cmd/internal/pprof/report/report.go
index 86bd4a280b..c492b752b9 100644
--- a/src/cmd/pprof/internal/report/report.go
+++ b/src/cmd/internal/pprof/report/report.go
@@ -17,8 +17,8 @@ import (
"strings"
"time"
- "cmd/pprof/internal/plugin"
- "cmd/pprof/internal/profile"
+ "cmd/internal/pprof/plugin"
+ "cmd/internal/pprof/profile"
)
// Generate generates a report as directed by the Report.
diff --git a/src/cmd/pprof/internal/report/source.go b/src/cmd/internal/pprof/report/source.go
index 908be21424..608e4d561d 100644
--- a/src/cmd/pprof/internal/report/source.go
+++ b/src/cmd/internal/pprof/report/source.go
@@ -18,7 +18,7 @@ import (
"strconv"
"strings"
- "cmd/pprof/internal/plugin"
+ "cmd/internal/pprof/plugin"
)
// printSource prints an annotated source listing, include all
@@ -257,7 +257,7 @@ func printHeader(w io.Writer, rpt *Report) {
// printFunctionHeader prints a function header for a weblist report.
func printFunctionHeader(w io.Writer, name, path string, flatSum, cumSum int64, rpt *Report) {
fmt.Fprintf(w, `<h1>%s</h1>%s
-<pre onClick="pprof_toggle_asm()">
+<pre onClick="pprof_toggle_asm(event)">
Total: %10s %10s (flat, cum) %s
`,
template.HTMLEscapeString(name), template.HTMLEscapeString(path),
diff --git a/src/cmd/pprof/internal/report/source_html.go b/src/cmd/internal/pprof/report/source_html.go
index 267fabdc4b..267fabdc4b 100644
--- a/src/cmd/pprof/internal/report/source_html.go
+++ b/src/cmd/internal/pprof/report/source_html.go
diff --git a/src/cmd/pprof/internal/svg/svg.go b/src/cmd/internal/pprof/svg/svg.go
index 04f6ff1870..04f6ff1870 100644
--- a/src/cmd/pprof/internal/svg/svg.go
+++ b/src/cmd/internal/pprof/svg/svg.go
diff --git a/src/cmd/pprof/internal/svg/svgpan.go b/src/cmd/internal/pprof/svg/svgpan.go
index 4975b103e3..4975b103e3 100644
--- a/src/cmd/pprof/internal/svg/svgpan.go
+++ b/src/cmd/internal/pprof/svg/svgpan.go
diff --git a/src/cmd/pprof/internal/symbolizer/symbolizer.go b/src/cmd/internal/pprof/symbolizer/symbolizer.go
index 86de5640d2..bc22800530 100644
--- a/src/cmd/pprof/internal/symbolizer/symbolizer.go
+++ b/src/cmd/internal/pprof/symbolizer/symbolizer.go
@@ -13,8 +13,8 @@ import (
"path/filepath"
"strings"
- "cmd/pprof/internal/plugin"
- "cmd/pprof/internal/profile"
+ "cmd/internal/pprof/plugin"
+ "cmd/internal/pprof/profile"
)
// Symbolize adds symbol and line number information to all locations
diff --git a/src/cmd/pprof/internal/symbolz/symbolz.go b/src/cmd/internal/pprof/symbolz/symbolz.go
index 15b3b6df26..2f2850afeb 100644
--- a/src/cmd/pprof/internal/symbolz/symbolz.go
+++ b/src/cmd/internal/pprof/symbolz/symbolz.go
@@ -15,7 +15,7 @@ import (
"strconv"
"strings"
- "cmd/pprof/internal/profile"
+ "cmd/internal/pprof/profile"
)
var (
diff --git a/src/cmd/pprof/internal/tempfile/tempfile.go b/src/cmd/internal/pprof/tempfile/tempfile.go
index 31c117690a..31c117690a 100644
--- a/src/cmd/pprof/internal/tempfile/tempfile.go
+++ b/src/cmd/internal/pprof/tempfile/tempfile.go
diff --git a/src/cmd/internal/sys/arch.go b/src/cmd/internal/sys/arch.go
new file mode 100644
index 0000000000..18accdeb0c
--- /dev/null
+++ b/src/cmd/internal/sys/arch.go
@@ -0,0 +1,148 @@
+// Copyright 2016 The Go 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 sys
+
+import "encoding/binary"
+
+// ArchFamily represents a family of one or more related architectures.
+// For example, amd64 and amd64p32 are both members of the AMD64 family,
+// and ppc64 and ppc64le are both members of the PPC64 family.
+type ArchFamily byte
+
+const (
+ AMD64 ArchFamily = iota
+ ARM
+ ARM64
+ I386
+ MIPS64
+ PPC64
+ S390X
+)
+
+// Arch represents an individual architecture.
+type Arch struct {
+ Name string
+ Family ArchFamily
+
+ ByteOrder binary.ByteOrder
+
+ IntSize int
+ PtrSize int
+ RegSize int
+
+ // MinLC is the minimum length of an instruction code.
+ MinLC int
+}
+
+// InFamily reports whether a is a member of any of the specified
+// architecture families.
+func (a *Arch) InFamily(xs ...ArchFamily) bool {
+ for _, x := range xs {
+ if a.Family == x {
+ return true
+ }
+ }
+ return false
+}
+
+var Arch386 = &Arch{
+ Name: "386",
+ Family: I386,
+ ByteOrder: binary.LittleEndian,
+ IntSize: 4,
+ PtrSize: 4,
+ RegSize: 4,
+ MinLC: 1,
+}
+
+var ArchAMD64 = &Arch{
+ Name: "amd64",
+ Family: AMD64,
+ ByteOrder: binary.LittleEndian,
+ IntSize: 8,
+ PtrSize: 8,
+ RegSize: 8,
+ MinLC: 1,
+}
+
+var ArchAMD64P32 = &Arch{
+ Name: "amd64p32",
+ Family: AMD64,
+ ByteOrder: binary.LittleEndian,
+ IntSize: 4,
+ PtrSize: 4,
+ RegSize: 8,
+ MinLC: 1,
+}
+
+var ArchARM = &Arch{
+ Name: "arm",
+ Family: ARM,
+ ByteOrder: binary.LittleEndian,
+ IntSize: 4,
+ PtrSize: 4,
+ RegSize: 4,
+ MinLC: 4,
+}
+
+var ArchARM64 = &Arch{
+ Name: "arm64",
+ Family: ARM64,
+ ByteOrder: binary.LittleEndian,
+ IntSize: 8,
+ PtrSize: 8,
+ RegSize: 8,
+ MinLC: 4,
+}
+
+var ArchMIPS64 = &Arch{
+ Name: "mips64",
+ Family: MIPS64,
+ ByteOrder: binary.BigEndian,
+ IntSize: 8,
+ PtrSize: 8,
+ RegSize: 8,
+ MinLC: 4,
+}
+
+var ArchMIPS64LE = &Arch{
+ Name: "mips64le",
+ Family: MIPS64,
+ ByteOrder: binary.LittleEndian,
+ IntSize: 8,
+ PtrSize: 8,
+ RegSize: 8,
+ MinLC: 4,
+}
+
+var ArchPPC64 = &Arch{
+ Name: "ppc64",
+ Family: PPC64,
+ ByteOrder: binary.BigEndian,
+ IntSize: 8,
+ PtrSize: 8,
+ RegSize: 8,
+ MinLC: 4,
+}
+
+var ArchPPC64LE = &Arch{
+ Name: "ppc64le",
+ Family: PPC64,
+ ByteOrder: binary.LittleEndian,
+ IntSize: 8,
+ PtrSize: 8,
+ RegSize: 8,
+ MinLC: 4,
+}
+
+var ArchS390X = &Arch{
+ Name: "s390x",
+ Family: S390X,
+ ByteOrder: binary.BigEndian,
+ IntSize: 8,
+ PtrSize: 8,
+ RegSize: 8,
+ MinLC: 2,
+}
diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/decode.go b/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/decode.go
index 6b4d73841b..cc81dc3f50 100644
--- a/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/decode.go
+++ b/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/decode.go
@@ -233,9 +233,9 @@ func decodeArg(aop instArg, x uint32) Arg {
typ, count := decodeShift(x)
// ROR #0 here means ROR #0, but decodeShift rewrites to RRX #1.
if typ == RotateRightExt {
- return Reg(Rm)
+ return Rm
}
- return RegShift{Rm, typ, uint8(count)}
+ return RegShift{Rm, typ, count}
case arg_R_shift_R:
Rm := Reg(x & (1<<4 - 1))
@@ -247,9 +247,9 @@ func decodeArg(aop instArg, x uint32) Arg {
Rm := Reg(x & (1<<4 - 1))
typ, count := decodeShift(x)
if typ == ShiftLeft && count == 0 {
- return Reg(Rm)
+ return Rm
}
- return RegShift{Rm, typ, uint8(count)}
+ return RegShift{Rm, typ, count}
case arg_R1_0:
return Reg((x & (1<<4 - 1)))
diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/decode.go b/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/decode.go
index e4122c1e6d..9b3597300e 100644
--- a/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/decode.go
+++ b/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/decode.go
@@ -1041,7 +1041,7 @@ Decode:
case xArgMoffs8, xArgMoffs16, xArgMoffs32, xArgMoffs64:
// TODO(rsc): Can address be 64 bits?
- mem = Mem{Disp: int64(immc)}
+ mem = Mem{Disp: immc}
if segIndex >= 0 {
mem.Segment = prefixToSegment(inst.Prefix[segIndex])
inst.Prefix[segIndex] |= PrefixImplicit
diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go
index d809f6e8ed..cdb0354579 100644
--- a/src/cmd/link/internal/amd64/asm.go
+++ b/src/cmd/link/internal/amd64/asm.go
@@ -86,12 +86,7 @@ func gentext() {
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
+ ld.Ctxt.Textp = append(ld.Ctxt.Textp, initfunc)
initarray_entry := ld.Linklookup(ld.Ctxt, "go.link.addmoduledatainit", 0)
initarray_entry.Attr |= ld.AttrReachable
initarray_entry.Attr |= ld.AttrLocal
@@ -99,12 +94,6 @@ func gentext() {
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
@@ -285,7 +274,7 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
return
}
- if ld.HEADTYPE == obj.Hdarwin && s.Size == int64(ld.Thearch.Ptrsize) && r.Off == 0 {
+ if ld.HEADTYPE == obj.Hdarwin && s.Size == int64(ld.SysArch.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.
@@ -611,12 +600,12 @@ func addgotsym(s *ld.LSym) {
func asmb() {
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime())
+ 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())
+ fmt.Fprintf(ld.Bso, "%5.2f codeblk\n", obj.Cputime())
}
ld.Bso.Flush()
@@ -634,7 +623,7 @@ func asmb() {
if ld.Segrodata.Filelen > 0 {
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime())
+ fmt.Fprintf(ld.Bso, "%5.2f rodatblk\n", obj.Cputime())
}
ld.Bso.Flush()
@@ -643,26 +632,18 @@ func asmb() {
}
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime())
+ 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))
+ ld.Cseek(int64(ld.Segdwarf.Fileoff))
+ ld.Dwarfblk(int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.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()
}
@@ -696,7 +677,7 @@ func asmb() {
symo := int64(0)
if ld.Debug['s'] == 0 {
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime())
+ fmt.Fprintf(ld.Bso, "%5.2f sym\n", obj.Cputime())
}
ld.Bso.Flush()
switch ld.HEADTYPE {
@@ -715,11 +696,11 @@ func asmb() {
obj.Hdragonfly,
obj.Hsolaris,
obj.Hnacl:
- symo = int64(ld.Segdata.Fileoff + ld.Segdata.Filelen)
+ symo = int64(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
symo = ld.Rnd(symo, int64(ld.INITRND))
case obj.Hwindows:
- symo = int64(ld.Segdata.Fileoff + ld.Segdata.Filelen)
+ symo = int64(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
symo = ld.Rnd(symo, ld.PEFILEALIGN)
}
@@ -733,11 +714,9 @@ func asmb() {
ld.Cwrite(ld.Elfstrdat)
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime())
+ fmt.Fprintf(ld.Bso, "%5.2f dwarf\n", obj.Cputime())
}
- ld.Dwarfemitdebugsections()
-
if ld.Linkmode == ld.LinkExternal {
ld.Elfemitreloc()
}
@@ -751,7 +730,7 @@ func asmb() {
if sym != nil {
ld.Lcsize = int32(len(sym.P))
for i := 0; int32(i) < ld.Lcsize; i++ {
- ld.Cput(uint8(sym.P[i]))
+ ld.Cput(sym.P[i])
}
ld.Cflush()
@@ -759,11 +738,9 @@ func asmb() {
case obj.Hwindows:
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime())
+ fmt.Fprintf(ld.Bso, "%5.2f dwarf\n", obj.Cputime())
}
- ld.Dwarfemitdebugsections()
-
case obj.Hdarwin:
if ld.Linkmode == ld.LinkExternal {
ld.Machoemitreloc()
@@ -772,7 +749,7 @@ func asmb() {
}
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f headr\n", obj.Cputime())
+ fmt.Fprintf(ld.Bso, "%5.2f headr\n", obj.Cputime())
}
ld.Bso.Flush()
ld.Cseek(0)
diff --git a/src/cmd/link/internal/amd64/l.go b/src/cmd/link/internal/amd64/l.go
index 4ec8610afb..2fbc8adc51 100644
--- a/src/cmd/link/internal/amd64/l.go
+++ b/src/cmd/link/internal/amd64/l.go
@@ -31,16 +31,11 @@
package amd64
const (
- thechar = '6'
MaxAlign = 32 // max data alignment
MinAlign = 1 // min data alignment
FuncAlign = 16
)
-const (
- MINLC = 1
-)
-
/* Used by ../internal/ld/dwarf.go */
const (
DWARFREGSP = 7
diff --git a/src/cmd/link/internal/amd64/obj.go b/src/cmd/link/internal/amd64/obj.go
index f9e13f2e18..860f588224 100644
--- a/src/cmd/link/internal/amd64/obj.go
+++ b/src/cmd/link/internal/amd64/obj.go
@@ -32,6 +32,7 @@ package amd64
import (
"cmd/internal/obj"
+ "cmd/internal/sys"
"cmd/link/internal/ld"
"fmt"
"log"
@@ -45,20 +46,14 @@ func Main() {
}
func linkarchinit() {
- ld.Thestring = "amd64"
- ld.Thelinkarch = &ld.Linkamd64
+ ld.SysArch = sys.ArchAMD64
if obj.Getgoarch() == "amd64p32" {
- ld.Thelinkarch = &ld.Linkamd64p32
+ ld.SysArch = sys.ArchAMD64P32
}
- 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.Minalign = MinAlign
- ld.Thearch.Minlc = MINLC
ld.Thearch.Dwarfregsp = DWARFREGSP
ld.Thearch.Dwarfreglr = DWARFREGLR
diff --git a/src/cmd/link/internal/amd64/z.go b/src/cmd/link/internal/amd64/z.go
deleted file mode 100644
index f70035b9e3..0000000000
--- a/src/cmd/link/internal/amd64/z.go
+++ /dev/null
@@ -1 +0,0 @@
-package amd64
diff --git a/src/cmd/link/internal/arm/asm.go b/src/cmd/link/internal/arm/asm.go
index bb90cf77b6..aafdd9bc3d 100644
--- a/src/cmd/link/internal/arm/asm.go
+++ b/src/cmd/link/internal/arm/asm.go
@@ -95,12 +95,7 @@ func gentext() {
rel.Type = obj.R_PCREL
rel.Add = 4
- if ld.Ctxt.Etextp != nil {
- ld.Ctxt.Etextp.Next = initfunc
- } else {
- ld.Ctxt.Textp = initfunc
- }
- ld.Ctxt.Etextp = initfunc
+ ld.Ctxt.Textp = append(ld.Ctxt.Textp, initfunc)
initarray_entry := ld.Linklookup(ld.Ctxt, "go.link.addmoduledatainit", 0)
initarray_entry.Attr |= ld.AttrReachable
initarray_entry.Attr |= ld.AttrLocal
@@ -114,11 +109,6 @@ 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
@@ -340,6 +330,36 @@ func machoreloc1(r *ld.Reloc, sectoff int64) int {
rs := r.Xsym
+ if r.Type == obj.R_PCREL {
+ if rs.Type == obj.SHOSTOBJ {
+ ld.Diag("pc-relative relocation of external symbol is not supported")
+ return -1
+ }
+ if r.Siz != 4 {
+ return -1
+ }
+
+ // emit a pair of "scattered" relocations that
+ // resolve to the difference of section addresses of
+ // the symbol and the instruction
+ // this value is added to the field being relocated
+ o1 := uint32(sectoff)
+ o1 |= 1 << 31 // scattered bit
+ o1 |= ld.MACHO_ARM_RELOC_SECTDIFF << 24
+ o1 |= 2 << 28 // size = 4
+
+ o2 := uint32(0)
+ o2 |= 1 << 31 // scattered bit
+ o2 |= ld.MACHO_ARM_RELOC_PAIR << 24
+ o2 |= 2 << 28 // size = 4
+
+ ld.Thearch.Lput(o1)
+ ld.Thearch.Lput(uint32(ld.Symaddr(rs)))
+ ld.Thearch.Lput(o2)
+ ld.Thearch.Lput(uint32(ld.Ctxt.Cursym.Value + int64(r.Off)))
+ return 0
+ }
+
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)
@@ -563,7 +583,7 @@ func addgotsym(ctxt *ld.Link, s *ld.LSym) {
func asmb() {
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime())
+ fmt.Fprintf(ld.Bso, "%5.2f asmb\n", obj.Cputime())
}
ld.Bso.Flush()
@@ -581,7 +601,7 @@ func asmb() {
if ld.Segrodata.Filelen > 0 {
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime())
+ fmt.Fprintf(ld.Bso, "%5.2f rodatblk\n", obj.Cputime())
}
ld.Bso.Flush()
@@ -590,26 +610,18 @@ func asmb() {
}
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime())
+ 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))
+ ld.Cseek(int64(ld.Segdwarf.Fileoff))
+ ld.Dwarfblk(int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.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())
}
@@ -621,13 +633,13 @@ func asmb() {
if ld.Debug['s'] == 0 {
// TODO: rationalize
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime())
+ 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.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND)))
}
@@ -643,17 +655,12 @@ func asmb() {
default:
if ld.Iself {
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f elfsym\n", obj.Cputime())
+ 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()
}
@@ -667,7 +674,7 @@ func asmb() {
if sym != nil {
ld.Lcsize = int32(len(sym.P))
for i := 0; int32(i) < ld.Lcsize; i++ {
- ld.Cput(uint8(sym.P[i]))
+ ld.Cput(sym.P[i])
}
ld.Cflush()
@@ -682,7 +689,7 @@ func asmb() {
ld.Ctxt.Cursym = nil
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f header\n", obj.Cputime())
+ fmt.Fprintf(ld.Bso, "%5.2f header\n", obj.Cputime())
}
ld.Bso.Flush()
ld.Cseek(0)
diff --git a/src/cmd/link/internal/arm/l.go b/src/cmd/link/internal/arm/l.go
index 58aecc4b64..2b73a7b172 100644
--- a/src/cmd/link/internal/arm/l.go
+++ b/src/cmd/link/internal/arm/l.go
@@ -63,11 +63,9 @@ package arm
// THE SOFTWARE.
const (
- thechar = '5'
MaxAlign = 8 // max data alignment
MinAlign = 1 // min data alignment
FuncAlign = 4 // single-instruction alignment
- MINLC = 4
)
/* Used by ../internal/ld/dwarf.go */
diff --git a/src/cmd/link/internal/arm/obj.go b/src/cmd/link/internal/arm/obj.go
index bcd61fda9b..9125a1fa32 100644
--- a/src/cmd/link/internal/arm/obj.go
+++ b/src/cmd/link/internal/arm/obj.go
@@ -32,6 +32,7 @@ package arm
import (
"cmd/internal/obj"
+ "cmd/internal/sys"
"cmd/link/internal/ld"
"fmt"
"log"
@@ -45,17 +46,11 @@ func Main() {
}
func linkarchinit() {
- ld.Thestring = "arm"
- ld.Thelinkarch = &ld.Linkarm
+ ld.SysArch = sys.ArchARM
- 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.Minalign = MinAlign
- ld.Thearch.Minlc = MINLC
ld.Thearch.Dwarfregsp = DWARFREGSP
ld.Thearch.Dwarfreglr = DWARFREGLR
diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go
index 250f0afb16..97803c9d03 100644
--- a/src/cmd/link/internal/arm64/asm.go
+++ b/src/cmd/link/internal/arm64/asm.go
@@ -78,12 +78,7 @@ func gentext() {
rel.Sym = ld.Linklookup(ld.Ctxt, "runtime.addmoduledata", 0)
rel.Type = obj.R_CALLARM64 // Really should be R_AARCH64_JUMP26 but doesn't seem to make any difference
- if ld.Ctxt.Etextp != nil {
- ld.Ctxt.Etextp.Next = initfunc
- } else {
- ld.Ctxt.Textp = initfunc
- }
- ld.Ctxt.Etextp = initfunc
+ ld.Ctxt.Textp = append(ld.Ctxt.Textp, initfunc)
initarray_entry := ld.Linklookup(ld.Ctxt, "go.link.addmoduledatainit", 0)
initarray_entry.Attr |= ld.AttrReachable
initarray_entry.Attr |= ld.AttrLocal
@@ -91,10 +86,6 @@ func gentext() {
ld.Addaddr(ld.Ctxt, initarray_entry, initfunc)
}
-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")
}
@@ -375,7 +366,7 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
}
// The TCB is two pointers. This is not documented anywhere, but is
// de facto part of the ABI.
- v := r.Sym.Value + int64(2*ld.Thearch.Ptrsize)
+ v := r.Sym.Value + int64(2*ld.SysArch.PtrSize)
if v < 0 || v >= 32678 {
ld.Diag("TLS offset out of range %d", v)
}
@@ -401,7 +392,7 @@ func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 {
func asmb() {
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime())
+ fmt.Fprintf(ld.Bso, "%5.2f asmb\n", obj.Cputime())
}
ld.Bso.Flush()
@@ -419,7 +410,7 @@ func asmb() {
if ld.Segrodata.Filelen > 0 {
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime())
+ fmt.Fprintf(ld.Bso, "%5.2f rodatblk\n", obj.Cputime())
}
ld.Bso.Flush()
@@ -428,26 +419,18 @@ func asmb() {
}
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime())
+ 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))
+ ld.Cseek(int64(ld.Segdwarf.Fileoff))
+ ld.Dwarfblk(int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.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())
}
@@ -459,13 +442,13 @@ func asmb() {
if ld.Debug['s'] == 0 {
// TODO: rationalize
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime())
+ 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.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND)))
}
@@ -481,17 +464,12 @@ func asmb() {
default:
if ld.Iself {
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f elfsym\n", obj.Cputime())
+ 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()
}
@@ -505,7 +483,7 @@ func asmb() {
if sym != nil {
ld.Lcsize = int32(len(sym.P))
for i := 0; int32(i) < ld.Lcsize; i++ {
- ld.Cput(uint8(sym.P[i]))
+ ld.Cput(sym.P[i])
}
ld.Cflush()
@@ -520,7 +498,7 @@ func asmb() {
ld.Ctxt.Cursym = nil
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f header\n", obj.Cputime())
+ fmt.Fprintf(ld.Bso, "%5.2f header\n", obj.Cputime())
}
ld.Bso.Flush()
ld.Cseek(0)
diff --git a/src/cmd/link/internal/arm64/l.go b/src/cmd/link/internal/arm64/l.go
index b9b7ea50e3..67ad5c977f 100644
--- a/src/cmd/link/internal/arm64/l.go
+++ b/src/cmd/link/internal/arm64/l.go
@@ -62,11 +62,9 @@ package arm64
// THE SOFTWARE.
const (
- thechar = '7'
MaxAlign = 32 // max data alignment
MinAlign = 1 // min data alignment
FuncAlign = 8
- MINLC = 4
)
/* Used by ../internal/ld/dwarf.go */
diff --git a/src/cmd/link/internal/arm64/obj.go b/src/cmd/link/internal/arm64/obj.go
index 693e106ff1..1169e79a58 100644
--- a/src/cmd/link/internal/arm64/obj.go
+++ b/src/cmd/link/internal/arm64/obj.go
@@ -32,6 +32,7 @@ package arm64
import (
"cmd/internal/obj"
+ "cmd/internal/sys"
"cmd/link/internal/ld"
"fmt"
"log"
@@ -45,17 +46,11 @@ func Main() {
}
func linkarchinit() {
- ld.Thestring = obj.Getgoarch()
- ld.Thelinkarch = &ld.Linkarm64
+ ld.SysArch = sys.ArchARM64
- 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.Minalign = MinAlign
- ld.Thearch.Minlc = MINLC
ld.Thearch.Dwarfregsp = DWARFREGSP
ld.Thearch.Dwarfreglr = DWARFREGLR
diff --git a/src/cmd/link/internal/ld/ar.go b/src/cmd/link/internal/ld/ar.go
index d07756071d..323dfbefc5 100644
--- a/src/cmd/link/internal/ld/ar.go
+++ b/src/cmd/link/internal/ld/ar.go
@@ -31,9 +31,11 @@
package ld
import (
+ "cmd/internal/bio"
"cmd/internal/obj"
"encoding/binary"
"fmt"
+ "io"
"os"
)
@@ -62,26 +64,26 @@ type ArHdr struct {
// define them. This is used for the compiler support library
// libgcc.a.
func hostArchive(name string) {
- f, err := obj.Bopenr(name)
+ f, err := bio.Open(name)
if err != nil {
if os.IsNotExist(err) {
// It's OK if we don't have a libgcc file at all.
if Debug['v'] != 0 {
- fmt.Fprintf(&Bso, "skipping libgcc file: %v\n", err)
+ fmt.Fprintf(Bso, "skipping libgcc file: %v\n", err)
}
return
}
Exitf("cannot open file %s: %v", name, err)
}
- defer obj.Bterm(f)
+ defer f.Close()
- magbuf := make([]byte, len(ARMAG))
- if obj.Bread(f, magbuf) != len(magbuf) {
+ var magbuf [len(ARMAG)]byte
+ if _, err := io.ReadFull(f, magbuf[:]); err != nil {
Exitf("file %s too short", name)
}
var arhdr ArHdr
- l := nextar(f, obj.Boffset(f), &arhdr)
+ l := nextar(f, f.Offset(), &arhdr)
if l <= 0 {
Exitf("%s missing armap", name)
}
@@ -117,7 +119,7 @@ func hostArchive(name string) {
l = atolwhex(arhdr.size)
h := ldobj(f, "libgcc", l, pname, name, ArchiveObj)
- obj.Bseek(f, h.off, 0)
+ f.Seek(h.off, 0)
h.ld(f, h.pkg, h.length, h.pn)
}
@@ -130,16 +132,15 @@ func hostArchive(name string) {
type archiveMap map[string]uint64
// readArmap reads the archive symbol map.
-func readArmap(filename string, f *obj.Biobuf, arhdr ArHdr) archiveMap {
+func readArmap(filename string, f *bio.Reader, arhdr ArHdr) archiveMap {
is64 := arhdr.name == "/SYM64/"
wordSize := 4
if is64 {
wordSize = 8
}
- l := atolwhex(arhdr.size)
- contents := make([]byte, l)
- if obj.Bread(f, contents) != int(l) {
+ contents := make([]byte, atolwhex(arhdr.size))
+ if _, err := io.ReadFull(f, contents); err != nil {
Exitf("short read from %s", filename)
}
diff --git a/src/cmd/link/internal/ld/arch.go b/src/cmd/link/internal/ld/arch.go
deleted file mode 100644
index d28f37fa02..0000000000
--- a/src/cmd/link/internal/ld/arch.go
+++ /dev/null
@@ -1,97 +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,
-}
-
-var Linkmips64 = LinkArch{
- ByteOrder: binary.BigEndian,
- Name: "mips64",
- Thechar: '0',
- Minlc: 4,
- Ptrsize: 8,
- Regsize: 8,
-}
-
-var Linkmips64le = LinkArch{
- ByteOrder: binary.LittleEndian,
- Name: "mips64le",
- Thechar: '0',
- Minlc: 4,
- Ptrsize: 8,
- Regsize: 8,
-}
-
-var Links390x = LinkArch{
- ByteOrder: binary.BigEndian,
- Name: "s390x",
- Thechar: 'z',
- Minlc: 2,
- Ptrsize: 8,
- Regsize: 8,
-}
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index fe74cc9208..8964757846 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -34,12 +34,14 @@ package ld
import (
"cmd/internal/gcprog"
"cmd/internal/obj"
+ "cmd/internal/sys"
"fmt"
"log"
"os"
"sort"
"strconv"
"strings"
+ "sync"
)
func Symgrow(ctxt *Link, s *LSym, siz int64) {
@@ -49,8 +51,9 @@ func Symgrow(ctxt *Link, s *LSym, siz int64) {
if int64(len(s.P)) >= siz {
return
}
- for cap(s.P) < int(siz) {
- s.P = append(s.P[:len(s.P)], 0)
+ if cap(s.P) < int(siz) {
+ p := make([]byte, 2*(siz+1))
+ s.P = append(p[:0], s.P...)
}
s.P = s.P[:siz]
}
@@ -78,12 +81,23 @@ func setuintxx(ctxt *Link, s *LSym, off int64, v uint64, wid int64) int64 {
case 4:
ctxt.Arch.ByteOrder.PutUint32(s.P[off:], uint32(v))
case 8:
- ctxt.Arch.ByteOrder.PutUint64(s.P[off:], uint64(v))
+ ctxt.Arch.ByteOrder.PutUint64(s.P[off:], v)
}
return off + wid
}
+func Addbytes(ctxt *Link, s *LSym, bytes []byte) int64 {
+ if s.Type == 0 {
+ s.Type = obj.SDATA
+ }
+ s.Attr |= AttrReachable
+ s.P = append(s.P, bytes...)
+ s.Size = int64(len(s.P))
+
+ return s.Size
+}
+
func adduintxx(ctxt *Link, s *LSym, v uint64, wid int) int64 {
off := s.Size
setuintxx(ctxt, s, off, v, int64(wid))
@@ -91,7 +105,15 @@ func adduintxx(ctxt *Link, s *LSym, v uint64, wid int) int64 {
}
func Adduint8(ctxt *Link, s *LSym, v uint8) int64 {
- return adduintxx(ctxt, s, uint64(v), 1)
+ off := s.Size
+ if s.Type == 0 {
+ s.Type = obj.SDATA
+ }
+ s.Attr |= AttrReachable
+ s.Size++
+ s.P = append(s.P, v)
+
+ return off
}
func Adduint16(ctxt *Link, s *LSym, v uint16) int64 {
@@ -107,7 +129,7 @@ func Adduint64(ctxt *Link, s *LSym, v uint64) int64 {
}
func adduint(ctxt *Link, s *LSym, v uint64) int64 {
- return adduintxx(ctxt, s, v, Thearch.Intsize)
+ return adduintxx(ctxt, s, v, SysArch.IntSize)
}
func setuint8(ctxt *Link, s *LSym, r int64, v uint8) int64 {
@@ -124,12 +146,12 @@ func Addaddrplus(ctxt *Link, s *LSym, t *LSym, add int64) int64 {
}
s.Attr |= AttrReachable
i := s.Size
- s.Size += int64(ctxt.Arch.Ptrsize)
+ 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.Siz = uint8(ctxt.Arch.PtrSize)
r.Type = obj.R_ADDR
r.Add = add
return i + int64(r.Siz)
@@ -149,7 +171,7 @@ func Addpcrelplus(ctxt *Link, s *LSym, t *LSym, add int64) int64 {
r.Add = add
r.Type = obj.R_PCREL
r.Siz = 4
- if Thearch.Thechar == 'z' {
+ if SysArch.Family == sys.S390X {
r.Variant = RV_390_DBL
}
return i + int64(r.Siz)
@@ -164,15 +186,15 @@ func setaddrplus(ctxt *Link, s *LSym, off int64, t *LSym, add int64) int64 {
s.Type = obj.SDATA
}
s.Attr |= AttrReachable
- if off+int64(ctxt.Arch.Ptrsize) > s.Size {
- s.Size = off + int64(ctxt.Arch.Ptrsize)
+ 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.Siz = uint8(ctxt.Arch.PtrSize)
r.Type = obj.R_ADDR
r.Add = add
return off + int64(r.Siz)
@@ -188,12 +210,12 @@ func addsize(ctxt *Link, s *LSym, t *LSym) int64 {
}
s.Attr |= AttrReachable
i := s.Size
- s.Size += int64(ctxt.Arch.Ptrsize)
+ 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.Siz = uint8(ctxt.Arch.PtrSize)
r.Type = obj.R_SIZE
return i + int64(r.Siz)
}
@@ -221,10 +243,6 @@ func addaddrplus4(ctxt *Link, s *LSym, t *LSym, add int64) int64 {
* Used for the data block.
*/
-func listnextp(s *LSym) **LSym {
- return &s.Next
-}
-
func listsubp(s *LSym) **LSym {
return &s.Sub
}
@@ -342,7 +360,7 @@ func relocsym(s *LSym) {
// 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() {
- if !(Thearch.Thechar == '9' && Linkmode == LinkExternal && r.Sym.Name == ".TOC.") {
+ if !(SysArch.Family == sys.PPC64 && Linkmode == LinkExternal && r.Sym.Name == ".TOC.") {
Diag("unhandled relocation for %s (type %d rtype %d)", r.Sym.Name, r.Sym.Type, r.Type)
}
}
@@ -351,7 +369,7 @@ func relocsym(s *LSym) {
}
// TODO(mundaym): remove this special case - see issue 14218.
- if Thearch.Thechar == 'z' {
+ if SysArch.Family == sys.S390X {
switch r.Type {
case obj.R_PCRELDBL:
r.Type = obj.R_PCREL
@@ -380,7 +398,7 @@ func relocsym(s *LSym) {
}
case obj.R_TLS_LE:
- isAndroidX86 := goos == "android" && (Thearch.Thechar == '6' || Thearch.Thechar == '8')
+ isAndroidX86 := goos == "android" && (SysArch.InFamily(sys.AMD64, sys.I386))
if Linkmode == LinkExternal && Iself && HEADTYPE != obj.Hopenbsd && !isAndroidX86 {
r.Done = 0
@@ -390,13 +408,13 @@ func relocsym(s *LSym) {
r.Xsym = r.Sym
r.Xadd = r.Add
o = 0
- if Thearch.Thechar != '6' {
+ if SysArch.Family != sys.AMD64 {
o = r.Add
}
break
}
- if Iself && Thearch.Thechar == '5' {
+ if Iself && SysArch.Family == sys.ARM {
// 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).
@@ -414,7 +432,7 @@ func relocsym(s *LSym) {
}
case obj.R_TLS_IE:
- isAndroidX86 := goos == "android" && (Thearch.Thechar == '6' || Thearch.Thechar == '8')
+ isAndroidX86 := goos == "android" && (SysArch.InFamily(sys.AMD64, sys.I386))
if Linkmode == LinkExternal && Iself && HEADTYPE != obj.Hopenbsd && !isAndroidX86 {
r.Done = 0
@@ -424,7 +442,7 @@ func relocsym(s *LSym) {
r.Xsym = r.Sym
r.Xadd = r.Add
o = 0
- if Thearch.Thechar != '6' {
+ if SysArch.Family != sys.AMD64 {
o = r.Add
}
break
@@ -451,7 +469,7 @@ func relocsym(s *LSym) {
o = r.Xadd
if Iself {
- if Thearch.Thechar == '6' {
+ if SysArch.Family == sys.AMD64 {
o = 0
}
} else if HEADTYPE == obj.Hdarwin {
@@ -461,10 +479,10 @@ func relocsym(s *LSym) {
// 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 {
+ if SysArch.Family == sys.ARM64 && rs.Dynid < 0 {
Diag("R_ADDR reloc to %s+%d is not supported on darwin/arm64", rs.Name, o)
}
- if Thearch.Thechar != '7' {
+ if SysArch.Family != sys.ARM64 {
o += Symaddr(rs)
}
}
@@ -484,11 +502,33 @@ func relocsym(s *LSym) {
// fail at runtime. See https://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 {
+ if int32(o) < 0 && SysArch.PtrSize > 4 && siz == 4 {
Diag("non-pc-relative relocation address is too big: %#x (%#x + %#x)", uint64(o), Symaddr(r.Sym), r.Add)
errorexit()
}
+ case obj.R_DWARFREF:
+ if r.Sym.Sect == nil {
+ Diag("missing DWARF section: %s from %s", r.Sym.Name, s.Name)
+ }
+ if Linkmode == LinkExternal {
+ r.Done = 0
+ r.Type = obj.R_ADDR
+
+ r.Xsym = Linkrlookup(Ctxt, r.Sym.Sect.Name, 0)
+ r.Xadd = r.Add + Symaddr(r.Sym) - int64(r.Sym.Sect.Vaddr)
+ o = r.Xadd
+ rs = r.Xsym
+ if Iself && SysArch.Family == sys.AMD64 {
+ o = 0
+ }
+ break
+ }
+ o = Symaddr(r.Sym) + r.Add - int64(r.Sym.Sect.Vaddr)
+
+ case obj.R_ADDROFF:
+ o = Symaddr(r.Sym) - int64(r.Sym.Sect.Vaddr) + r.Add
+
// 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) {
@@ -511,7 +551,7 @@ func relocsym(s *LSym) {
o = r.Xadd
if Iself {
- if Thearch.Thechar == '6' {
+ if SysArch.Family == sys.AMD64 {
o = 0
}
} else if HEADTYPE == obj.Hdarwin {
@@ -520,10 +560,13 @@ func relocsym(s *LSym) {
o += int64(uint64(Symaddr(rs)) - rs.Sect.Vaddr)
}
o -= int64(r.Off) // relative to section offset, not symbol
+ } else if SysArch.Family == sys.ARM {
+ // see ../arm/asm.go:/machoreloc1
+ o += Symaddr(rs) - int64(Ctxt.Cursym.Value) - int64(r.Off)
} else {
o += int64(r.Siz)
}
- } else if HEADTYPE == obj.Hwindows && Thearch.Thechar == '6' { // only amd64 needs PCREL
+ } else if HEADTYPE == obj.Hwindows && SysArch.Family == sys.AMD64 { // 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)
@@ -604,14 +647,17 @@ func relocsym(s *LSym) {
func reloc() {
if Debug['v'] != 0 {
- fmt.Fprintf(&Bso, "%5.2f reloc\n", obj.Cputime())
+ fmt.Fprintf(Bso, "%5.2f reloc\n", obj.Cputime())
}
Bso.Flush()
- for s := Ctxt.Textp; s != nil; s = s.Next {
+ for _, s := range Ctxt.Textp {
relocsym(s)
}
- for s := datap; s != nil; s = s.Next {
+ for _, sym := range datap {
+ relocsym(sym)
+ }
+ for s := dwarfp; s != nil; s = s.Next {
relocsym(s)
}
}
@@ -622,11 +668,9 @@ func dynrelocsym(s *LSym) {
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
+ r := &s.R[ri]
+ targ := r.Sym
if targ == nil {
continue
}
@@ -639,7 +683,7 @@ func dynrelocsym(s *LSym) {
r.Add = int64(targ.Plt)
// jmp *addr
- if Thearch.Thechar == '8' {
+ if SysArch.Family == sys.I386 {
Adduint8(Ctxt, rel, 0xff)
Adduint8(Ctxt, rel, 0x25)
Addaddr(Ctxt, rel, targ)
@@ -661,9 +705,8 @@ func dynrelocsym(s *LSym) {
return
}
- var r *Reloc
for ri := 0; ri < len(s.R); ri++ {
- r = &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.Attr.Reachable() {
Diag("internal inconsistency: dynamic symbol %s is not reachable.", r.Sym.Name)
@@ -673,22 +716,24 @@ func dynrelocsym(s *LSym) {
}
}
-func dynreloc() {
+func dynreloc(data *[obj.SXREF][]*LSym) {
// -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())
+ fmt.Fprintf(Bso, "%5.2f reloc\n", obj.Cputime())
}
Bso.Flush()
- for s := Ctxt.Textp; s != nil; s = s.Next {
+ for _, s := range Ctxt.Textp {
dynrelocsym(s)
}
- for s := datap; s != nil; s = s.Next {
- dynrelocsym(s)
+ for _, syms := range data {
+ for _, sym := range syms {
+ dynrelocsym(sym)
+ }
}
if Iself {
elfdynhash()
@@ -705,7 +750,6 @@ func blk(start *LSym, addr int64, size int64) {
}
eaddr := addr + size
- var p []byte
for ; sym != nil; sym = sym.Next {
if sym.Type&obj.SSUB != 0 {
continue
@@ -715,7 +759,7 @@ func blk(start *LSym, addr int64, size int64) {
}
Ctxt.Cursym = sym
if sym.Value < addr {
- Diag("phase error: addr=%#x but sym=%#x type=%d", int64(addr), int64(sym.Value), sym.Type)
+ Diag("phase error: addr=%#x but sym=%#x type=%d", addr, sym.Value, sym.Type)
errorexit()
}
@@ -723,15 +767,14 @@ func blk(start *LSym, addr int64, size int64) {
strnput("", int(sym.Value-addr))
addr = sym.Value
}
- p = sym.P
- Cwrite(p)
+ Cwrite(sym.P)
addr += int64(len(sym.P))
if addr < sym.Value+sym.Size {
strnput("", int(sym.Value+sym.Size-addr))
addr = sym.Value + sym.Size
}
if addr != sym.Value+sym.Size {
- Diag("phase error: addr=%#x value+size=%#x", int64(addr), int64(sym.Value)+sym.Size)
+ Diag("phase error: addr=%#x value+size=%#x", addr, sym.Value+sym.Size)
errorexit()
}
@@ -748,29 +791,30 @@ func blk(start *LSym, addr int64, size int64) {
func Codeblk(addr int64, size int64) {
if Debug['a'] != 0 {
- fmt.Fprintf(&Bso, "codeblk [%#x,%#x) at offset %#x\n", addr, addr+size, Cpos())
+ fmt.Fprintf(Bso, "codeblk [%#x,%#x) at offset %#x\n", addr, addr+size, Cpos())
}
- blk(Ctxt.Textp, addr, size)
+ blkSlice(Ctxt.Textp, addr, size)
/* again for printing */
if Debug['a'] == 0 {
return
}
- var sym *LSym
- for sym = Ctxt.Textp; sym != nil; sym = sym.Next {
+ syms := Ctxt.Textp
+ for i, sym := range syms {
if !sym.Attr.Reachable() {
continue
}
if sym.Value >= addr {
+ syms = syms[i:]
break
}
}
eaddr := addr + size
var q []byte
- for ; sym != nil; sym = sym.Next {
+ for _, sym := range syms {
if !sym.Attr.Reachable() {
continue
}
@@ -779,118 +823,164 @@ func Codeblk(addr int64, size int64) {
}
if addr < sym.Value {
- fmt.Fprintf(&Bso, "%-20s %.8x|", "_", uint64(int64(addr)))
+ fmt.Fprintf(Bso, "%-20s %.8x|", "_", uint64(addr))
for ; addr < sym.Value; addr++ {
- fmt.Fprintf(&Bso, " %.2x", 0)
+ fmt.Fprintf(Bso, " %.2x", 0)
}
- fmt.Fprintf(&Bso, "\n")
+ fmt.Fprintf(Bso, "\n")
}
- fmt.Fprintf(&Bso, "%.6x\t%-20s\n", uint64(int64(addr)), sym.Name)
+ fmt.Fprintf(Bso, "%.6x\t%-20s\n", uint64(addr), sym.Name)
q = sym.P
for len(q) >= 16 {
- fmt.Fprintf(&Bso, "%.6x\t% x\n", uint64(addr), q[:16])
+ fmt.Fprintf(Bso, "%.6x\t% x\n", uint64(addr), q[:16])
addr += 16
q = q[16:]
}
if len(q) > 0 {
- fmt.Fprintf(&Bso, "%.6x\t% x\n", uint64(addr), q)
+ fmt.Fprintf(Bso, "%.6x\t% x\n", uint64(addr), q)
addr += int64(len(q))
}
}
if addr < eaddr {
- fmt.Fprintf(&Bso, "%-20s %.8x|", "_", uint64(int64(addr)))
+ fmt.Fprintf(Bso, "%-20s %.8x|", "_", uint64(addr))
for ; addr < eaddr; addr++ {
- fmt.Fprintf(&Bso, " %.2x", 0)
+ fmt.Fprintf(Bso, " %.2x", 0)
}
}
Bso.Flush()
}
+// blkSlice is a variant of blk that processes slices.
+// After text symbols are converted from a linked list to a slice,
+// delete blk and give this function its name.
+func blkSlice(syms []*LSym, addr, size int64) {
+ for i, s := range syms {
+ if s.Type&obj.SSUB == 0 && s.Value >= addr {
+ syms = syms[i:]
+ break
+ }
+ }
+
+ eaddr := addr + size
+ for _, s := range syms {
+ if s.Type&obj.SSUB != 0 {
+ continue
+ }
+ if s.Value >= eaddr {
+ break
+ }
+ Ctxt.Cursym = s
+ if s.Value < addr {
+ Diag("phase error: addr=%#x but sym=%#x type=%d", addr, s.Value, s.Type)
+ errorexit()
+ }
+ if addr < s.Value {
+ strnput("", int(s.Value-addr))
+ addr = s.Value
+ }
+ Cwrite(s.P)
+ addr += int64(len(s.P))
+ if addr < s.Value+s.Size {
+ strnput("", int(s.Value+s.Size-addr))
+ addr = s.Value + s.Size
+ }
+ if addr != s.Value+s.Size {
+ Diag("phase error: addr=%#x value+size=%#x", addr, s.Value+s.Size)
+ errorexit()
+ }
+ if s.Value+s.Size >= eaddr {
+ break
+ }
+ }
+
+ if addr < eaddr {
+ strnput("", int(eaddr-addr))
+ }
+ Cflush()
+}
+
func Datblk(addr int64, size int64) {
if Debug['a'] != 0 {
- fmt.Fprintf(&Bso, "datblk [%#x,%#x) at offset %#x\n", addr, addr+size, Cpos())
+ fmt.Fprintf(Bso, "datblk [%#x,%#x) at offset %#x\n", addr, addr+size, Cpos())
}
- blk(datap, addr, size)
+ blkSlice(datap, addr, size)
/* again for printing */
if Debug['a'] == 0 {
return
}
- var sym *LSym
- for sym = datap; sym != nil; sym = sym.Next {
+ syms := datap
+ for i, sym := range syms {
if sym.Value >= addr {
+ syms = syms[i:]
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 {
+ for _, sym := range syms {
if sym.Value >= eaddr {
break
}
if addr < sym.Value {
- fmt.Fprintf(&Bso, "\t%.8x| 00 ...\n", uint64(addr))
+ 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, "%s\n\t%.8x|", sym.Name, uint64(addr))
+ for i, b := range sym.P {
+ if i > 0 && i%16 == 0 {
+ fmt.Fprintf(Bso, "\n\t%.8x|", uint64(addr)+uint64(i))
}
- fmt.Fprintf(&Bso, " %.2x", p[0])
- p = p[1:]
+ fmt.Fprintf(Bso, " %.2x", b)
}
addr += int64(len(sym.P))
for ; addr < sym.Value+sym.Size; addr++ {
- fmt.Fprintf(&Bso, " %.2x", 0)
+ 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"
+ fmt.Fprintf(Bso, "\n")
- 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 Linkmode != LinkExternal {
+ continue
+ }
+ for _, r := range sym.R {
+ 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, r.Add, r.Sym.Value+r.Add)
}
}
if addr < eaddr {
- fmt.Fprintf(&Bso, "\t%.8x| 00 ...\n", uint(addr))
+ fmt.Fprintf(Bso, "\t%.8x| 00 ...\n", uint(addr))
}
- fmt.Fprintf(&Bso, "\t%.8x|\n", uint(eaddr))
+ fmt.Fprintf(Bso, "\t%.8x|\n", uint(eaddr))
+}
+
+func Dwarfblk(addr int64, size int64) {
+ if Debug['a'] != 0 {
+ fmt.Fprintf(Bso, "dwarfblk [%#x,%#x) at offset %#x\n", addr, addr+size, Cpos())
+ }
+
+ blk(dwarfp, addr, size)
}
var zeros [512]byte
@@ -938,7 +1028,7 @@ func addstrdata(name string, value string) {
s.Attr |= AttrDuplicateOK
reachable := s.Attr.Reachable()
Addaddr(Ctxt, s, sp)
- adduintxx(Ctxt, s, uint64(len(value)), Thearch.Ptrsize)
+ adduintxx(Ctxt, s, uint64(len(value)), SysArch.PtrSize)
// addstring, addaddr, etc., mark the symbols as reachable.
// In this case that is not necessarily true, so stick to what
@@ -965,16 +1055,14 @@ func Addstring(s *LSym, str string) int64 {
s.Type = obj.SNOPTRDATA
}
s.Attr |= AttrReachable
- r := int32(s.Size)
- n := len(str) + 1
+ r := s.Size
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)
+ s.P = append(s.P, str...)
+ s.P = append(s.P, 0)
+ s.Size = int64(len(s.P))
+ return r
}
// addgostring adds str, as a Go string value, to s. symname is the name of the
@@ -1031,7 +1119,7 @@ func symalign(s *LSym) int32 {
} else if s.Align != 0 {
return min
}
- if strings.HasPrefix(s.Name, "go.string.") && !strings.HasPrefix(s.Name, "go.string.hdr.") {
+ if (strings.HasPrefix(s.Name, "go.string.") && !strings.HasPrefix(s.Name, "go.string.hdr.")) || strings.HasPrefix(s.Name, "type..namedata.") {
// String data is just bytes.
// If we align it, we waste a lot of space to padding.
return min
@@ -1047,22 +1135,6 @@ 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 {
@@ -1084,7 +1156,7 @@ func (p *GCProg) writeByte(x byte) {
}
func (p *GCProg) End(size int64) {
- p.w.ZeroUntil(size / int64(Thearch.Ptrsize))
+ p.w.ZeroUntil(size / int64(SysArch.PtrSize))
p.w.End()
if debugGCProg {
fmt.Fprintf(os.Stderr, "ld: end GCProg\n")
@@ -1100,7 +1172,7 @@ func (p *GCProg) AddSym(s *LSym) {
return
}
- ptrsize := int64(Thearch.Ptrsize)
+ ptrsize := int64(SysArch.PtrSize)
nptr := decodetype_ptrdata(typ) / ptrsize
if debugGCProg {
@@ -1124,236 +1196,170 @@ func (p *GCProg) AddSym(s *LSym) {
p.w.Append(prog[4:], nptr)
}
+// dataSortKey is used to sort a slice of data symbol *LSym pointers.
+// The sort keys are kept inline to improve cache behaviour while sorting.
type dataSortKey struct {
- // keep sort keys inline to improve cache behaviour while sorting
- Type int16
- Size int64
- Name string
-
- Lsym *LSym
+ size int64
+ name string
+ lsym *LSym
}
-type dataSlice []dataSortKey
-
-func (d dataSlice) Len() int { return len(d) }
-func (d dataSlice) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
-func (d dataSlice) Less(i, j int) bool {
- s1, s2 := &d[i], &d[j]
- if s1.Type != s2.Type {
- return s1.Type < s2.Type
- }
+type bySizeAndName []dataSortKey
- // 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 {
- return s1.Size < s2.Size
+func (d bySizeAndName) Len() int { return len(d) }
+func (d bySizeAndName) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
+func (d bySizeAndName) Less(i, j int) bool {
+ s1, s2 := d[i], d[j]
+ if s1.size != s2.size {
+ return s1.size < s2.size
}
-
- return s1.Name < s2.Name
+ return s1.name < s2.name
}
-func growdatsize(datsizep *int64, s *LSym) {
- datsize := *datsizep
- const cutoff int64 = 2e9 // 2 GB (or so; looks better in errors than 2^31)
- switch {
- case s.Size < 0:
- Diag("%s: negative size (%d bytes)", s.Name, s.Size)
- case s.Size > cutoff:
- Diag("%s: symbol too large (%d bytes)", s.Name, s.Size)
- case datsize <= cutoff && datsize+s.Size > cutoff:
- Diag("%s: too much data (over %d bytes)", s.Name, cutoff)
- }
- *datsizep = datsize + s.Size
-}
+const cutoff int64 = 2e9 // 2 GB (or so; looks better in errors than 2^31)
-func list2Slice(head *LSym) dataSlice {
- n := 0
- for s := datap; s != nil; s = s.Next {
- n++
+func checkdatsize(datsize int64, symn int) {
+ if datsize > cutoff {
+ Diag("too much data in section %v (over %d bytes)", symn, cutoff)
}
- slice := make(dataSlice, n)
- i := 0
- for s := datap; s != nil; s = s.Next {
- k := &slice[i]
- k.Type = s.Type
- k.Size = s.Size
- k.Name = s.Name
- k.Lsym = s
-
- i++
- }
- return slice
}
-func slice2List(d dataSlice) *LSym {
- for i := 0; i < len(d)-1; i++ {
- d[i].Lsym.Next = d[i+1].Lsym
+func list2slice(s *LSym) []*LSym {
+ var syms []*LSym
+ for ; s != nil; s = s.Next {
+ syms = append(syms, s)
}
- d[len(d)-1].Lsym.Next = nil
- return d[0].Lsym
+ return syms
}
-func dataSort(head *LSym) *LSym {
- d := list2Slice(head)
- sort.Sort(d)
- return slice2List(d)
-}
+// datap is a collection of reachable data symbols in address order.
+// Generated by dodata.
+var datap []*LSym
func dodata() {
if Debug['v'] != 0 {
- fmt.Fprintf(&Bso, "%5.2f dodata\n", obj.Cputime())
+ fmt.Fprintf(Bso, "%5.2f dodata\n", obj.Cputime())
}
Bso.Flush()
- var last *LSym
- datap = nil
-
+ // Collect data symbols by type into data.
+ var data [obj.SXREF][]*LSym
for _, s := range Ctxt.Allsym {
if !s.Attr.Reachable() || s.Attr.Special() {
continue
}
- if obj.STEXT < s.Type && s.Type < obj.SXREF {
- if s.Attr.OnList() {
- log.Fatalf("symbol %s listed multiple times", s.Name)
- }
- s.Attr |= AttrOnList
- 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))
+ if s.Type <= obj.STEXT || s.Type >= obj.SXREF {
+ continue
}
+ data[s.Type] = append(data[s.Type], s)
}
- /*
- * 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.
- */
+ // Now that we have the data symbols, 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
+ dynreloc(&data)
if UseRelro() {
// "read only" data with relocations needs to go in its own section
// when building a shared library. We do this by boosting objects of
// type SXXX with relocations to type SXXXRELRO.
- for s := datap; s != nil; s = s.Next {
- if (s.Type >= obj.STYPE && s.Type <= obj.SFUNCTAB && len(s.R) > 0) || s.Type == obj.STYPE || s.Type == obj.SGOSTRINGHDR {
- s.Type += (obj.STYPERELRO - obj.STYPE)
- if s.Outer != nil {
- s.Outer.Type = s.Type
- }
- }
- }
- // Check that we haven't made two symbols with the same .Outer into
- // different types (because references two symbols with non-nil Outer
- // become references to the outer symbol + offset it's vital that the
- // symbol and the outer end up in the same section).
- for s := datap; s != nil; s = s.Next {
- if s.Outer != nil && s.Outer.Type != s.Type {
- Diag("inconsistent types for %s and its Outer %s (%d != %d)",
- s.Name, s.Outer.Name, s.Type, s.Outer.Type)
- }
- }
-
- }
+ for symnro := int16(obj.STYPE); symnro < obj.STYPERELRO; symnro++ {
+ symnrelro := symnro + obj.STYPERELRO - obj.STYPE
- datap = dataSort(datap)
+ ro := []*LSym{}
+ relro := data[symnrelro]
- 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
+ for _, s := range data[symnro] {
+ isRelro := len(s.R) > 0
+ switch s.Type {
+ case obj.STYPE, obj.SGOSTRINGHDR, obj.STYPERELRO, obj.SGOSTRINGHDRRELRO:
+ // Symbols are not sorted yet, so it is possible
+ // that an Outer symbol has been changed to a
+ // relro Type before it reaches here.
+ isRelro = true
+ }
+ if isRelro {
+ s.Type = symnrelro
+ if s.Outer != nil {
+ s.Outer.Type = s.Type
+ }
+ relro = append(relro, s)
+ } else {
+ ro = append(ro, s)
+ }
}
- }
- 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
+
+ // Check that we haven't made two symbols with the same .Outer into
+ // different types (because references two symbols with non-nil Outer
+ // become references to the outer symbol + offset it's vital that the
+ // symbol and the outer end up in the same section).
+ for _, s := range relro {
+ if s.Outer != nil && s.Outer.Type != s.Type {
+ Diag("inconsistent types for %s and its Outer %s (%d != %d)",
+ s.Name, s.Outer.Name, s.Type, s.Outer.Type)
}
}
+
+ data[symnro] = ro
+ data[symnrelro] = relro
}
}
- /*
- * 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 {
+ // Sort symbols.
+ var dataMaxAlign [obj.SXREF]int32
+ var wg sync.WaitGroup
+ for symn := range data {
+ symn := symn
+ wg.Add(1)
+ go func() {
+ data[symn], dataMaxAlign[symn] = dodataSect(symn, data[symn])
+ wg.Done()
+ }()
}
+ wg.Wait()
- /* writable ELF sections */
+ // Allocate sections.
+ // Data is processed before segtext, because we need
+ // to see all symbols in the .data and .bss sections in order
+ // to generate garbage collection information.
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
+ // Writable sections.
+ writableSects := []int{
+ obj.SELFSECT,
+ obj.SMACHO,
+ obj.SMACHOGOT,
+ obj.SWINDOWS,
+ }
+ for _, symn := range writableSects {
+ for _, s := range data[symn] {
+ 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)
+ datsize += s.Size
+ sect.Length = uint64(datsize) - sect.Vaddr
+ }
+ checkdatsize(datsize, symn)
}
- /* .got (and .toc on ppc64) */
- if s.Type == obj.SELFGOT {
+ // .got (and .toc on ppc64)
+ if len(data[obj.SELFGOT]) > 0 {
sect := addsection(&Segdata, ".got", 06)
- sect.Align = maxalign(s, obj.SELFGOT)
+ sect.Align = dataMaxAlign[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 {
+ for _, s := range data[obj.SELFGOT] {
datsize = aligndatsize(datsize, s)
s.Sect = sect
s.Type = obj.SDATA
@@ -1361,7 +1367,6 @@ func dodata() {
// Resolve .TOC. symbol for this object file (ppc64)
toc = Linkrlookup(Ctxt, ".TOC.", int(s.Version))
-
if toc != nil {
toc.Sect = sect
toc.Outer = s
@@ -1371,28 +1376,27 @@ func dodata() {
toc.Value = 0x8000
}
- growdatsize(&datsize, s)
+ datsize += s.Size
}
-
+ checkdatsize(datsize, obj.SELFGOT)
sect.Length = uint64(datsize) - sect.Vaddr
}
/* pointer-free data */
- sect = addsection(&Segdata, ".noptrdata", 06)
-
- sect.Align = maxalign(s, obj.SINITARR-1)
+ sect := addsection(&Segdata, ".noptrdata", 06)
+ sect.Align = dataMaxAlign[obj.SNOPTRDATA]
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 {
+ for _, s := range data[obj.SNOPTRDATA] {
datsize = aligndatsize(datsize, s)
s.Sect = sect
s.Type = obj.SDATA
s.Value = int64(uint64(datsize) - sect.Vaddr)
- growdatsize(&datsize, s)
+ datsize += s.Size
}
-
+ checkdatsize(datsize, obj.SNOPTRDATA)
sect.Length = uint64(datsize) - sect.Vaddr
hasinitarr := Linkshared
@@ -1402,116 +1406,102 @@ func dodata() {
case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared:
hasinitarr = true
}
-
if hasinitarr {
sect := addsection(&Segdata, ".init_array", 06)
- sect.Align = maxalign(s, obj.SINITARR)
+ sect.Align = dataMaxAlign[obj.SINITARR]
datsize = Rnd(datsize, int64(sect.Align))
sect.Vaddr = uint64(datsize)
- for ; s != nil && s.Type == obj.SINITARR; s = s.Next {
+ for _, s := range data[obj.SINITARR] {
datsize = aligndatsize(datsize, s)
s.Sect = sect
s.Value = int64(uint64(datsize) - sect.Vaddr)
- growdatsize(&datsize, s)
+ datsize += s.Size
}
-
sect.Length = uint64(datsize) - sect.Vaddr
+ checkdatsize(datsize, obj.SINITARR)
}
/* data */
sect = addsection(&Segdata, ".data", 06)
- sect.Align = maxalign(s, obj.SBSS-1)
+ sect.Align = dataMaxAlign[obj.SDATA]
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)
- }
-
+ for _, s := range data[obj.SDATA] {
s.Sect = sect
s.Type = obj.SDATA
datsize = aligndatsize(datsize, s)
s.Value = int64(uint64(datsize) - sect.Vaddr)
gc.AddSym(s)
- growdatsize(&datsize, s)
+ datsize += s.Size
}
+ checkdatsize(datsize, obj.SDATA)
sect.Length = uint64(datsize) - sect.Vaddr
gc.End(int64(sect.Length))
/* bss */
sect = addsection(&Segdata, ".bss", 06)
- sect.Align = maxalign(s, obj.SNOPTRBSS-1)
+ sect.Align = dataMaxAlign[obj.SBSS]
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 {
+ for _, s := range data[obj.SBSS] {
s.Sect = sect
datsize = aligndatsize(datsize, s)
s.Value = int64(uint64(datsize) - sect.Vaddr)
gc.AddSym(s)
- growdatsize(&datsize, s)
+ datsize += s.Size
}
+ checkdatsize(datsize, obj.SBSS)
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)
+ sect.Align = dataMaxAlign[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 {
+ for _, s := range data[obj.SNOPTRBSS] {
datsize = aligndatsize(datsize, s)
s.Sect = sect
s.Value = int64(uint64(datsize) - sect.Vaddr)
- growdatsize(&datsize, s)
+ datsize += s.Size
}
sect.Length = uint64(datsize) - sect.Vaddr
Linklookup(Ctxt, "runtime.end", 0).Sect = sect
+ checkdatsize(datsize, obj.SNOPTRBSS)
- // 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 s != nil && s.Type == obj.STLSBSS {
+ if len(data[obj.STLSBSS]) > 0 {
+ var sect *Section
if Iself && (Linkmode == LinkExternal || Debug['d'] == 0) && HEADTYPE != obj.Hopenbsd {
sect = addsection(&Segdata, ".tbss", 06)
- sect.Align = int32(Thearch.Ptrsize)
+ sect.Align = int32(SysArch.PtrSize)
sect.Vaddr = 0
- } else {
- sect = nil
}
datsize = 0
- for ; s != nil && s.Type == obj.STLSBSS; s = s.Next {
+ for _, s := range data[obj.STLSBSS] {
datsize = aligndatsize(datsize, s)
s.Sect = sect
s.Value = datsize
- growdatsize(&datsize, s)
+ datsize += s.Size
}
+ checkdatsize(datsize, obj.STLSBSS)
if sect != nil {
sect.Length = uint64(datsize)
}
}
- 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.
@@ -1529,39 +1519,62 @@ func dodata() {
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)
+ if len(data[obj.STEXT]) != 0 {
+ Diag("dodata found an STEXT symbol: %s", data[obj.STEXT][0].Name)
+ }
+ for _, s := range data[obj.SELFRXSECT] {
+ 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)
+ datsize += s.Size
sect.Length = uint64(datsize) - sect.Vaddr
+ checkdatsize(datsize, obj.SELFRXSECT)
}
/* read-only data */
sect = addsection(segro, ".rodata", 04)
- sect.Align = maxalign(s, obj.STYPERELRO-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.STYPERELRO; s = s.Next {
- datsize = aligndatsize(datsize, s)
- s.Sect = sect
- s.Type = obj.SRODATA
- s.Value = int64(uint64(datsize) - sect.Vaddr)
- growdatsize(&datsize, s)
+ if !UseRelro() {
+ Linklookup(Ctxt, "runtime.types", 0).Sect = sect
+ Linklookup(Ctxt, "runtime.etypes", 0).Sect = sect
+ }
+ roSects := []int{
+ obj.STYPE,
+ obj.SSTRING,
+ obj.SGOSTRING,
+ obj.SGOSTRINGHDR,
+ obj.SGOFUNC,
+ obj.SGCBITS,
+ obj.SRODATA,
+ obj.SFUNCTAB,
+ }
+ for _, symn := range roSects {
+ align := dataMaxAlign[symn]
+ if sect.Align < align {
+ sect.Align = align
+ }
+ }
+ datsize = Rnd(datsize, int64(sect.Align))
+ for _, symn := range roSects {
+ for _, s := range data[symn] {
+ datsize = aligndatsize(datsize, s)
+ s.Sect = sect
+ s.Type = obj.SRODATA
+ s.Value = int64(uint64(datsize) - sect.Vaddr)
+ datsize += s.Size
+ }
+ checkdatsize(datsize, symn)
}
-
sect.Length = uint64(datsize) - sect.Vaddr
// There is some data that are conceptually read-only but are written to by
@@ -1583,18 +1596,38 @@ func dodata() {
/* data only written by relocations */
sect = addsection(segro, ".data.rel.ro", 06)
- sect.Align = maxalign(s, obj.STYPELINK-1)
- datsize = Rnd(datsize, int64(sect.Align))
sect.Vaddr = 0
- for ; s != nil && s.Type < obj.STYPELINK; s = s.Next {
- datsize = aligndatsize(datsize, s)
- if s.Outer != nil && s.Outer.Sect != nil && s.Outer.Sect != sect {
- Diag("s.Outer (%s) in different section from s (%s)", s.Outer.Name, s.Name)
+ Linklookup(Ctxt, "runtime.types", 0).Sect = sect
+ Linklookup(Ctxt, "runtime.etypes", 0).Sect = sect
+ relroSects := []int{
+ obj.STYPERELRO,
+ obj.SSTRINGRELRO,
+ obj.SGOSTRINGRELRO,
+ obj.SGOSTRINGHDRRELRO,
+ obj.SGOFUNCRELRO,
+ obj.SGCBITSRELRO,
+ obj.SRODATARELRO,
+ obj.SFUNCTABRELRO,
+ }
+ for _, symn := range relroSects {
+ align := dataMaxAlign[symn]
+ if sect.Align < align {
+ sect.Align = align
}
- s.Sect = sect
- s.Type = obj.SRODATA
- s.Value = int64(uint64(datsize) - sect.Vaddr)
- growdatsize(&datsize, s)
+ }
+ datsize = Rnd(datsize, int64(sect.Align))
+ for _, symn := range relroSects {
+ for _, s := range data[symn] {
+ datsize = aligndatsize(datsize, s)
+ if s.Outer != nil && s.Outer.Sect != nil && s.Outer.Sect != sect {
+ Diag("s.Outer (%s) in different section from s (%s)", s.Outer.Name, s.Name)
+ }
+ s.Sect = sect
+ s.Type = obj.SRODATA
+ s.Value = int64(uint64(datsize) - sect.Vaddr)
+ datsize += s.Size
+ }
+ checkdatsize(datsize, symn)
}
sect.Length = uint64(datsize) - sect.Vaddr
@@ -1603,78 +1636,74 @@ func dodata() {
/* typelink */
sect = addsection(segro, relro_prefix+".typelink", relro_perms)
-
- sect.Align = maxalign(s, obj.STYPELINK)
+ sect.Align = dataMaxAlign[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 {
+ for _, s := range data[obj.STYPELINK] {
datsize = aligndatsize(datsize, s)
s.Sect = sect
s.Type = obj.SRODATA
s.Value = int64(uint64(datsize) - sect.Vaddr)
- growdatsize(&datsize, s)
+ datsize += s.Size
}
-
+ checkdatsize(datsize, obj.STYPELINK)
sect.Length = uint64(datsize) - sect.Vaddr
/* itablink */
sect = addsection(segro, relro_prefix+".itablink", relro_perms)
-
- sect.Align = maxalign(s, obj.SITABLINK)
+ sect.Align = dataMaxAlign[obj.SITABLINK]
datsize = Rnd(datsize, int64(sect.Align))
sect.Vaddr = uint64(datsize)
Linklookup(Ctxt, "runtime.itablink", 0).Sect = sect
Linklookup(Ctxt, "runtime.eitablink", 0).Sect = sect
- for ; s != nil && s.Type == obj.SITABLINK; s = s.Next {
+ for _, s := range data[obj.SITABLINK] {
datsize = aligndatsize(datsize, s)
s.Sect = sect
s.Type = obj.SRODATA
s.Value = int64(uint64(datsize) - sect.Vaddr)
- growdatsize(&datsize, s)
+ datsize += s.Size
}
-
+ checkdatsize(datsize, obj.SITABLINK)
sect.Length = uint64(datsize) - sect.Vaddr
/* gosymtab */
sect = addsection(segro, relro_prefix+".gosymtab", relro_perms)
-
- sect.Align = maxalign(s, obj.SPCLNTAB-1)
+ sect.Align = dataMaxAlign[obj.SSYMTAB]
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 {
+ for _, s := range data[obj.SSYMTAB] {
datsize = aligndatsize(datsize, s)
s.Sect = sect
s.Type = obj.SRODATA
s.Value = int64(uint64(datsize) - sect.Vaddr)
- growdatsize(&datsize, s)
+ datsize += s.Size
}
-
+ checkdatsize(datsize, obj.SSYMTAB)
sect.Length = uint64(datsize) - sect.Vaddr
/* gopclntab */
sect = addsection(segro, relro_prefix+".gopclntab", relro_perms)
-
- sect.Align = maxalign(s, obj.SELFROSECT-1)
+ sect.Align = dataMaxAlign[obj.SPCLNTAB]
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 {
+ for _, s := range data[obj.SPCLNTAB] {
datsize = aligndatsize(datsize, s)
s.Sect = sect
s.Type = obj.SRODATA
s.Value = int64(uint64(datsize) - sect.Vaddr)
- growdatsize(&datsize, s)
+ datsize += s.Size
}
-
+ checkdatsize(datsize, obj.SRODATA)
sect.Length = uint64(datsize) - sect.Vaddr
/* read-only ELF, Mach-O sections */
- for ; s != nil && s.Type < obj.SELFSECT; s = s.Next {
+ for _, s := range data[obj.SELFROSECT] {
sect = addsection(segro, s.Name, 04)
sect.Align = symalign(s)
datsize = Rnd(datsize, int64(sect.Align))
@@ -1682,15 +1711,65 @@ func dodata() {
s.Sect = sect
s.Type = obj.SRODATA
s.Value = int64(uint64(datsize) - sect.Vaddr)
- growdatsize(&datsize, s)
+ datsize += s.Size
sect.Length = uint64(datsize) - sect.Vaddr
}
+ checkdatsize(datsize, obj.SELFROSECT)
+
+ for _, s := range data[obj.SMACHOPLT] {
+ 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)
+ datsize += s.Size
+ sect.Length = uint64(datsize) - sect.Vaddr
+ }
+ checkdatsize(datsize, obj.SMACHOPLT)
// 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")
}
+ for symn := obj.SELFRXSECT; symn < obj.SXREF; symn++ {
+ datap = append(datap, data[symn]...)
+ }
+
+ dwarfgeneratedebugsyms()
+
+ var s *LSym
+ for s = dwarfp; s != nil && s.Type == obj.SDWARFSECT; s = s.Next {
+ sect = addsection(&Segdwarf, s.Name, 04)
+ sect.Align = 1
+ datsize = Rnd(datsize, int64(sect.Align))
+ sect.Vaddr = uint64(datsize)
+ s.Sect = sect
+ s.Type = obj.SRODATA
+ s.Value = int64(uint64(datsize) - sect.Vaddr)
+ datsize += s.Size
+ sect.Length = uint64(datsize) - sect.Vaddr
+ }
+ checkdatsize(datsize, obj.SDWARFSECT)
+
+ if s != nil {
+ sect = addsection(&Segdwarf, ".debug_info", 04)
+ sect.Align = 1
+ datsize = Rnd(datsize, int64(sect.Align))
+ sect.Vaddr = uint64(datsize)
+ for ; s != nil && s.Type == obj.SDWARFINFO; s = s.Next {
+ s.Sect = sect
+ s.Type = obj.SRODATA
+ s.Value = int64(uint64(datsize) - sect.Vaddr)
+ s.Attr |= AttrLocal
+ datsize += s.Size
+ }
+ sect.Length = uint64(datsize) - sect.Vaddr
+ checkdatsize(datsize, obj.SDWARFINFO)
+ }
+
/* number the sections */
n := int32(1)
@@ -1706,6 +1785,97 @@ func dodata() {
sect.Extnum = int16(n)
n++
}
+ for sect := Segdwarf.Sect; sect != nil; sect = sect.Next {
+ sect.Extnum = int16(n)
+ n++
+ }
+}
+
+func dodataSect(symn int, syms []*LSym) (result []*LSym, maxAlign int32) {
+ if HEADTYPE == obj.Hdarwin {
+ // Some symbols may no longer belong in syms
+ // due to movement in machosymorder.
+ newSyms := make([]*LSym, 0, len(syms))
+ for _, s := range syms {
+ if int(s.Type) == symn {
+ newSyms = append(newSyms, s)
+ }
+ }
+ syms = newSyms
+ }
+
+ symsSort := make([]dataSortKey, len(syms))
+ for i, s := range syms {
+ if s.Attr.OnList() {
+ log.Fatalf("symbol %s listed multiple times", s.Name)
+ }
+ s.Attr |= AttrOnList
+ switch {
+ case s.Size < int64(len(s.P)):
+ Diag("%s: initialize bounds (%d < %d)", s.Name, s.Size, len(s.P))
+ case s.Size < 0:
+ Diag("%s: negative size (%d bytes)", s.Name, s.Size)
+ case s.Size > cutoff:
+ Diag("%s: symbol too large (%d bytes)", s.Name, s.Size)
+ }
+
+ symsSort[i] = dataSortKey{
+ size: s.Size,
+ name: s.Name,
+ lsym: s,
+ }
+
+ switch s.Type {
+ case obj.SELFGOT:
+ // For ppc64, we want to interleave the .got and .toc sections
+ // from input files. Both are type SELFGOT, so in that case
+ // we skip size comparison and fall through to the name
+ // comparison (conveniently, .got sorts before .toc).
+ symsSort[i].size = 0
+ case obj.STYPELINK:
+ // Sort typelinks by the rtype.string field so the reflect
+ // package can binary search type links.
+ symsSort[i].name = string(decodetype_str(s.R[0].Sym))
+ }
+ }
+
+ sort.Sort(bySizeAndName(symsSort))
+
+ for i, symSort := range symsSort {
+ syms[i] = symSort.lsym
+ align := symalign(symSort.lsym)
+ if maxAlign < align {
+ maxAlign = align
+ }
+ }
+
+ if Iself && symn == obj.SELFROSECT {
+ // Make .rela and .rela.plt contiguous, the ELF ABI requires this
+ // and Solaris actually cares.
+ reli, plti := -1, -1
+ for i, s := range syms {
+ switch s.Name {
+ case ".rel.plt", ".rela.plt":
+ plti = i
+ case ".rel", ".rela":
+ reli = i
+ }
+ }
+ if reli >= 0 && plti >= 0 && plti != reli+1 {
+ var first, second int
+ if plti > reli {
+ first, second = reli, plti
+ } else {
+ first, second = plti, reli
+ }
+ rel, plt := syms[reli], syms[plti]
+ copy(syms[first+2:], syms[first+1:second])
+ syms[first+0] = rel
+ syms[first+1] = plt
+ }
+ }
+
+ return syms, maxAlign
}
// Add buildid to beginning of text segment, on non-ELF systems.
@@ -1727,14 +1897,13 @@ func textbuildid() {
sym.P = []byte(data)
sym.Size = int64(len(sym.P))
- sym.Next = Ctxt.Textp
- Ctxt.Textp = sym
+ Ctxt.Textp = append(Ctxt.Textp, nil)
+ copy(Ctxt.Textp[1:], Ctxt.Textp)
+ Ctxt.Textp[0] = sym
}
// assign addresses to text
func textaddress() {
- var sub *LSym
-
addsection(&Segtext, ".text", 05)
// Assign PCs in text segment.
@@ -1750,7 +1919,7 @@ func textaddress() {
}
va := uint64(INITTEXT)
sect.Vaddr = va
- for sym := Ctxt.Textp; sym != nil; sym = sym.Next {
+ for _, sym := range Ctxt.Textp {
sym.Sect = sect
if sym.Type&obj.SSUB != 0 {
continue
@@ -1761,7 +1930,7 @@ func textaddress() {
va = uint64(Rnd(int64(va), int64(Funcalign)))
}
sym.Value = 0
- for sub = sym; sub != nil; sub = sub.Sub {
+ for sub := sym; sub != nil; sub = sub.Sub {
sub.Value += int64(va)
}
if sym.Size == 0 && sym.Sub != nil {
@@ -1857,6 +2026,29 @@ func address() {
Segdata.Filelen = bss.Vaddr - Segdata.Vaddr
+ va = uint64(Rnd(int64(va), int64(INITRND)))
+ Segdwarf.Rwx = 06
+ Segdwarf.Vaddr = va
+ Segdwarf.Fileoff = Segdata.Fileoff + uint64(Rnd(int64(Segdata.Filelen), int64(INITRND)))
+ Segdwarf.Filelen = 0
+ if HEADTYPE == obj.Hwindows {
+ Segdwarf.Fileoff = Segdata.Fileoff + uint64(Rnd(int64(Segdata.Filelen), int64(PEFILEALIGN)))
+ }
+ for s := Segdwarf.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)
+ if HEADTYPE == obj.Hwindows {
+ va = uint64(Rnd(int64(va), PEFILEALIGN))
+ }
+ Segdwarf.Length = va - Segdwarf.Vaddr
+ }
+
+ Segdwarf.Filelen = va - Segdwarf.Vaddr
+
text := Segtext.Sect
var rodata *Section
if Segrodata.Sect != nil {
@@ -1864,23 +2056,34 @@ func address() {
} else {
rodata = text.Next
}
+ var relrodata *Section
typelink := rodata.Next
if UseRelro() {
// There is another section (.data.rel.ro) when building a shared
// object on elf systems.
+ relrodata = typelink
typelink = typelink.Next
}
itablink := typelink.Next
symtab := itablink.Next
pclntab := symtab.Next
- var sub *LSym
- for sym := datap; sym != nil; sym = sym.Next {
+ for _, s := range datap {
+ Ctxt.Cursym = s
+ if s.Sect != nil {
+ s.Value += int64(s.Sect.Vaddr)
+ }
+ for sub := s.Sub; sub != nil; sub = sub.Sub {
+ sub.Value += s.Value
+ }
+ }
+
+ for sym := dwarfp; sym != nil; sym = sym.Next {
Ctxt.Cursym = sym
if sym.Sect != nil {
sym.Value += int64(sym.Sect.Vaddr)
}
- for sub = sym.Sub; sub != nil; sub = sub.Sub {
+ for sub := sym.Sub; sub != nil; sub = sub.Sub {
sub.Value += sym.Value
}
}
@@ -1892,6 +2095,11 @@ func address() {
s.Value = int64(sectSym.Sect.Vaddr + 16)
}
+ types := relrodata
+ if types == nil {
+ types = rodata
+ }
+
xdefine("runtime.text", obj.STEXT, int64(text.Vaddr))
xdefine("runtime.etext", obj.STEXT, int64(text.Vaddr+text.Length))
if HEADTYPE == obj.Hwindows {
@@ -1899,6 +2107,8 @@ func address() {
}
xdefine("runtime.rodata", obj.SRODATA, int64(rodata.Vaddr))
xdefine("runtime.erodata", obj.SRODATA, int64(rodata.Vaddr+rodata.Length))
+ xdefine("runtime.types", obj.SRODATA, int64(types.Vaddr))
+ xdefine("runtime.etypes", obj.SRODATA, int64(types.Vaddr+types.Length))
xdefine("runtime.typelink", obj.SRODATA, int64(typelink.Vaddr))
xdefine("runtime.etypelink", obj.SRODATA, int64(typelink.Vaddr+typelink.Length))
xdefine("runtime.itablink", obj.SRODATA, int64(itablink.Vaddr))
diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go
index 56c4370bcc..aaed6cde21 100644
--- a/src/cmd/link/internal/ld/deadcode.go
+++ b/src/cmd/link/internal/ld/deadcode.go
@@ -6,6 +6,7 @@ package ld
import (
"cmd/internal/obj"
+ "cmd/internal/sys"
"fmt"
"strings"
"unicode"
@@ -18,7 +19,7 @@ import (
//
// This flood fill is wrapped in logic for pruning unused methods.
// All methods are mentioned by relocations on their receiver's *rtype.
-// These relocations are specially defined as R_METHOD by the compiler
+// These relocations are specially defined as R_METHODOFF by the compiler
// so we can detect and manipulated them here.
//
// There are three ways a method of a reachable type can be invoked:
@@ -99,7 +100,7 @@ func deadcode(ctxt *Link) {
d.flood()
}
- // Remove all remaining unreached R_METHOD relocations.
+ // Remove all remaining unreached R_METHODOFF relocations.
for _, m := range d.markableMethods {
for _, r := range m.r {
d.cleanupReloc(r)
@@ -118,25 +119,13 @@ func deadcode(ctxt *Link) {
}
// Remove dead text but keep file information (z symbols).
- var last *LSym
- for s := ctxt.Textp; s != nil; s = s.Next {
- if !s.Attr.Reachable() {
- continue
+ textp := make([]*LSym, 0, len(ctxt.Textp))
+ for _, s := range ctxt.Textp {
+ if s.Attr.Reachable() {
+ textp = append(textp, s)
}
- 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.Textp = textp
}
var markextra = []string{
@@ -166,12 +155,10 @@ var markextra = []string{
type methodref struct {
m methodsig
src *LSym // receiver type symbol
- r [3]*Reloc // R_METHOD relocations to fields of runtime.method
+ r [3]*Reloc // R_METHODOFF relocations to fields of runtime.method
}
-func (m methodref) mtyp() *LSym { return m.r[0].Sym }
-func (m methodref) ifn() *LSym { return m.r[1].Sym }
-func (m methodref) tfn() *LSym { return m.r[2].Sym }
+func (m methodref) ifn() *LSym { return m.r[1].Sym }
func (m methodref) isExported() bool {
for _, r := range m.m {
@@ -191,7 +178,7 @@ type deadcodepass struct {
func (d *deadcodepass) cleanupReloc(r *Reloc) {
if r.Sym.Attr.Reachable() {
- r.Type = obj.R_ADDR
+ r.Type = obj.R_ADDROFF
} else {
if Debug['v'] > 1 {
fmt.Fprintf(d.ctxt.Bso, "removing method %s\n", r.Sym.Name)
@@ -209,6 +196,13 @@ func (d *deadcodepass) mark(s, parent *LSym) {
if s.Attr.ReflectMethod() {
d.reflectMethod = true
}
+ if flag_dumpdep {
+ p := "_"
+ if parent != nil {
+ p = parent.Name
+ }
+ fmt.Printf("%s -> %s\n", p, s.Name)
+ }
s.Attr |= AttrReachable
s.Reachparent = parent
d.markQueue = append(d.markQueue, s)
@@ -218,7 +212,7 @@ func (d *deadcodepass) mark(s, parent *LSym) {
func (d *deadcodepass) markMethod(m methodref) {
for _, r := range m.r {
d.mark(r.Sym, m.src)
- r.Type = obj.R_ADDR
+ r.Type = obj.R_ADDROFF
}
}
@@ -227,7 +221,7 @@ func (d *deadcodepass) markMethod(m methodref) {
func (d *deadcodepass) init() {
var names []string
- if Thearch.Thechar == '5' {
+ if SysArch.Family == sys.ARM {
// mark some functions that are only referenced after linker code editing
if d.ctxt.Goarm == 5 {
names = append(names, "_sfloat")
@@ -273,9 +267,12 @@ func (d *deadcodepass) flood() {
if Debug['v'] > 1 {
fmt.Fprintf(d.ctxt.Bso, "marktext %s\n", s.Name)
}
- for _, a := range s.Autom {
- d.mark(a.Gotype, s)
+ if s.FuncInfo != nil {
+ for _, a := range s.FuncInfo.Autom {
+ d.mark(a.Gotype, s)
+ }
}
+
}
if strings.HasPrefix(s.Name, "type.") && s.Name[5] != '.' {
@@ -289,14 +286,14 @@ func (d *deadcodepass) flood() {
}
}
- mpos := 0 // 0-3, the R_METHOD relocs of runtime.uncommontype
+ mpos := 0 // 0-3, the R_METHODOFF relocs of runtime.uncommontype
var methods []methodref
for i := 0; i < len(s.R); i++ {
r := &s.R[i]
if r.Sym == nil {
continue
}
- if r.Type != obj.R_METHOD {
+ if r.Type != obj.R_METHODOFF {
d.mark(r.Sym, s)
continue
}
@@ -333,9 +330,9 @@ func (d *deadcodepass) flood() {
d.markableMethods = append(d.markableMethods, methods...)
}
- if s.Pcln != nil {
- for i := range s.Pcln.Funcdata {
- d.mark(s.Pcln.Funcdata[i], s)
+ if s.FuncInfo != nil {
+ for i := range s.FuncInfo.Funcdata {
+ d.mark(s.FuncInfo.Funcdata[i], s)
}
}
d.mark(s.Gotype, s)
diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go
index 0a6bf094aa..551ff802d7 100644
--- a/src/cmd/link/internal/ld/decodesym.go
+++ b/src/cmd/link/internal/ld/decodesym.go
@@ -7,6 +7,7 @@ package ld
import (
"bytes"
"cmd/internal/obj"
+ "cmd/internal/sys"
"debug/elf"
"fmt"
)
@@ -15,10 +16,22 @@ import (
// ../../runtime/type.go, or more specifically, with what
// ../gc/reflect.c stuffs in these.
+// tflag is documented in reflect/type.go.
+//
+// tflag values must be kept in sync with copies in:
+// cmd/compile/internal/gc/reflect.go
+// cmd/link/internal/ld/decodesym.go
+// reflect/type.go
+// runtime/type.go
+const (
+ tflagUncommon = 1 << 0
+ tflagExtraStar = 1 << 1
+)
+
func decode_reloc(s *LSym, off int32) *Reloc {
- for i := 0; i < len(s.R); i++ {
+ for i := range s.R {
if s.R[i].Off == off {
- return &s.R[i:][0]
+ return &s.R[i]
}
}
return nil
@@ -46,39 +59,33 @@ func decode_inuxi(p []byte, sz int) uint64 {
}
}
-func commonsize() int { return 6*Thearch.Ptrsize + 8 } // runtime._type
-func structfieldSize() int { return 3 * Thearch.Ptrsize } // runtime.structfield
-func uncommonSize() int { return 2*Thearch.Ptrsize + 2*Thearch.Intsize } // runtime.uncommontype
+func commonsize() int { return 4*SysArch.PtrSize + 8 + 8 } // runtime._type
+func structfieldSize() int { return 3 * SysArch.PtrSize } // runtime.structfield
+func uncommonSize() int { return 4 + 2 + 2 } // runtime.uncommontype
// 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
+ return s.P[2*SysArch.PtrSize+7] & obj.KindMask // 0x13 / 0x1f
}
// Type.commonType.kind
func decodetype_usegcprog(s *LSym) uint8 {
- return uint8(s.P[2*Thearch.Ptrsize+7] & obj.KindGCProg) // 0x13 / 0x1f
+ return s.P[2*SysArch.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
+ return int64(decode_inuxi(s.P, SysArch.PtrSize)) // 0x8 / 0x10
}
// Type.commonType.ptrdata
func decodetype_ptrdata(s *LSym) int64 {
- return int64(decode_inuxi(s.P[Thearch.Ptrsize:], Thearch.Ptrsize)) // 0x8 / 0x10
+ return int64(decode_inuxi(s.P[SysArch.PtrSize:], SysArch.PtrSize)) // 0x8 / 0x10
}
// Type.commonType.tflag
func decodetype_hasUncommon(s *LSym) bool {
- const tflagUncommon = 1 // see ../../../../reflect/type.go:/^type.tflag
- return s.P[2*Thearch.Ptrsize+4]&tflagUncommon != 0
+ return s.P[2*SysArch.PtrSize+4]&tflagUncommon != 0
}
// Find the elf.Section of a given shared library that contains a given address.
@@ -112,11 +119,11 @@ func decodetype_gcprog(s *LSym) []byte {
Exitf("cannot find gcprog for %s", s.Name)
return nil
}
- return decode_reloc_sym(s, 2*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize)).P
+ return decode_reloc_sym(s, 2*int32(SysArch.PtrSize)+8+1*int32(SysArch.PtrSize)).P
}
func decodetype_gcprog_shlib(s *LSym) uint64 {
- if Thearch.Thechar == '7' {
+ if SysArch.Family == sys.ARM64 {
for _, shlib := range Ctxt.Shlibs {
if shlib.Path == s.File {
return shlib.gcdata_addresses[s]
@@ -124,7 +131,7 @@ func decodetype_gcprog_shlib(s *LSym) uint64 {
}
return 0
}
- return decode_inuxi(s.P[2*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize):], Thearch.Ptrsize)
+ return decode_inuxi(s.P[2*int32(SysArch.PtrSize)+8+1*int32(SysArch.PtrSize):], SysArch.PtrSize)
}
func decodetype_gcmask(s *LSym) []byte {
@@ -133,14 +140,14 @@ func decodetype_gcmask(s *LSym) []byte {
ptrdata := decodetype_ptrdata(s)
sect := findShlibSection(s.File, addr)
if sect != nil {
- r := make([]byte, ptrdata/int64(Thearch.Ptrsize))
+ r := make([]byte, ptrdata/int64(SysArch.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))
+ mask := decode_reloc_sym(s, 2*int32(SysArch.PtrSize)+8+1*int32(SysArch.PtrSize))
return mask.P
}
@@ -150,7 +157,7 @@ func decodetype_arrayelem(s *LSym) *LSym {
}
func decodetype_arraylen(s *LSym) int64 {
- return int64(decode_inuxi(s.P[commonsize()+2*Thearch.Ptrsize:], Thearch.Ptrsize))
+ return int64(decode_inuxi(s.P[commonsize()+2*SysArch.PtrSize:], SysArch.PtrSize))
}
// Type.PtrType.elem
@@ -164,7 +171,7 @@ func decodetype_mapkey(s *LSym) *LSym {
}
func decodetype_mapvalue(s *LSym) *LSym {
- return decode_reloc_sym(s, int32(commonsize())+int32(Thearch.Ptrsize)) // 0x20 / 0x38
+ return decode_reloc_sym(s, int32(commonsize())+int32(SysArch.PtrSize)) // 0x20 / 0x38
}
// Type.ChanType.elem
@@ -188,13 +195,13 @@ func decodetype_funcoutcount(s *LSym) int {
func decodetype_funcintype(s *LSym, i int) *LSym {
uadd := commonsize() + 4
- if Thearch.Ptrsize == 8 {
+ if SysArch.PtrSize == 8 {
uadd += 4
}
if decodetype_hasUncommon(s) {
uadd += uncommonSize()
}
- return decode_reloc_sym(s, int32(uadd+i*Thearch.Ptrsize))
+ return decode_reloc_sym(s, int32(uadd+i*SysArch.PtrSize))
}
func decodetype_funcouttype(s *LSym, i int) *LSym {
@@ -203,11 +210,11 @@ func decodetype_funcouttype(s *LSym, i int) *LSym {
// Type.StructType.fields.Slice::length
func decodetype_structfieldcount(s *LSym) int {
- return int(decode_inuxi(s.P[commonsize()+2*Thearch.Ptrsize:], Thearch.Intsize))
+ return int(decode_inuxi(s.P[commonsize()+2*SysArch.PtrSize:], SysArch.IntSize))
}
func decodetype_structfieldarrayoff(s *LSym, i int) int {
- off := commonsize() + 2*Thearch.Ptrsize + 2*Thearch.Intsize
+ off := commonsize() + 2*SysArch.PtrSize + 2*SysArch.IntSize
if decodetype_hasUncommon(s) {
off += uncommonSize()
}
@@ -215,17 +222,13 @@ func decodetype_structfieldarrayoff(s *LSym, i int) int {
return off
}
-func decodetype_stringptr(s *LSym, off int) string {
- s = decode_reloc_sym(s, int32(off))
- if s == nil {
- return ""
- }
- r := decode_reloc(s, 0) // s has a pointer to the string data at offset 0
- if r == nil { // shouldn't happen.
- return ""
+// decodetype_str returns the contents of an rtype's str field (a nameOff).
+func decodetype_str(s *LSym) string {
+ str := decodetype_name(s, 4*SysArch.PtrSize+8)
+ if s.P[2*SysArch.PtrSize+4]&tflagExtraStar != 0 {
+ return str[1:]
}
- strlen := int64(decode_inuxi(s.P[Thearch.Ptrsize:], Thearch.Intsize))
- return string(r.Sym.P[r.Add : r.Add+strlen])
+ return str
}
// decodetype_name decodes the name from a reflect.name.
@@ -236,9 +239,8 @@ func decodetype_name(s *LSym, off int) string {
}
data := r.Sym.P
- namelen := int(uint16(data[1]<<8) | uint16(data[2]))
+ namelen := int(uint16(data[1])<<8 | uint16(data[2]))
return string(data[3 : 3+namelen])
-
}
func decodetype_structfieldname(s *LSym, i int) string {
@@ -248,17 +250,17 @@ func decodetype_structfieldname(s *LSym, i int) string {
func decodetype_structfieldtype(s *LSym, i int) *LSym {
off := decodetype_structfieldarrayoff(s, i)
- return decode_reloc_sym(s, int32(off+Thearch.Ptrsize))
+ return decode_reloc_sym(s, int32(off+SysArch.PtrSize))
}
func decodetype_structfieldoffs(s *LSym, i int) int64 {
off := decodetype_structfieldarrayoff(s, i)
- return int64(decode_inuxi(s.P[off+2*Thearch.Ptrsize:], Thearch.Intsize))
+ return int64(decode_inuxi(s.P[off+2*SysArch.PtrSize:], SysArch.IntSize))
}
// InterfaceType.methods.length
func decodetype_ifacemethodcount(s *LSym) int64 {
- return int64(decode_inuxi(s.P[commonsize()+2*Thearch.Ptrsize:], Thearch.Intsize))
+ return int64(decode_inuxi(s.P[commonsize()+2*SysArch.PtrSize:], SysArch.IntSize))
}
// methodsig is a fully qualified typed method signature, like
@@ -279,8 +281,9 @@ const (
)
// decode_methodsig decodes an array of method signature information.
-// Each element of the array is size bytes. The first word is a
-// reflect.name for the name, the second word is a *rtype for the funcType.
+// Each element of the array is size bytes. The first 4 bytes is a
+// nameOff for the method name, and the next 4 bytes is a typeOff for
+// the function type.
//
// Conveniently this is the layout of both runtime.method and runtime.imethod.
func decode_methodsig(s *LSym, off, size, count int) []methodsig {
@@ -288,7 +291,7 @@ func decode_methodsig(s *LSym, off, size, count int) []methodsig {
var methods []methodsig
for i := 0; i < count; i++ {
buf.WriteString(decodetype_name(s, off))
- mtypSym := decode_reloc_sym(s, int32(off+Thearch.Ptrsize))
+ mtypSym := decode_reloc_sym(s, int32(off+4))
buf.WriteRune('(')
inCount := decodetype_funcincount(mtypSym)
@@ -319,7 +322,7 @@ func decodetype_ifacemethods(s *LSym) []methodsig {
if decodetype_kind(s)&kindMask != kindInterface {
panic(fmt.Sprintf("symbol %q is not an interface", s.Name))
}
- r := decode_reloc(s, int32(commonsize()+Thearch.Ptrsize))
+ r := decode_reloc(s, int32(commonsize()+SysArch.PtrSize))
if r == nil {
return nil
}
@@ -328,7 +331,7 @@ func decodetype_ifacemethods(s *LSym) []methodsig {
}
off := int(r.Add) // array of reflect.imethod values
numMethods := int(decodetype_ifacemethodcount(s))
- sizeofIMethod := 2 * Thearch.Ptrsize
+ sizeofIMethod := 4 + 4
return decode_methodsig(s, off, sizeofIMethod, numMethods)
}
@@ -339,31 +342,28 @@ func decodetype_methods(s *LSym) []methodsig {
off := commonsize() // reflect.rtype
switch decodetype_kind(s) & kindMask {
case kindStruct: // reflect.structType
- off += 2*Thearch.Ptrsize + 2*Thearch.Intsize
+ off += 2*SysArch.PtrSize + 2*SysArch.IntSize
case kindPtr: // reflect.ptrType
- off += Thearch.Ptrsize
+ off += SysArch.PtrSize
case kindFunc: // reflect.funcType
- off += Thearch.Ptrsize // 4 bytes, pointer aligned
+ off += SysArch.PtrSize // 4 bytes, pointer aligned
case kindSlice: // reflect.sliceType
- off += Thearch.Ptrsize
+ off += SysArch.PtrSize
case kindArray: // reflect.arrayType
- off += 3 * Thearch.Ptrsize
+ off += 3 * SysArch.PtrSize
case kindChan: // reflect.chanType
- off += 2 * Thearch.Ptrsize
+ off += 2 * SysArch.PtrSize
case kindMap: // reflect.mapType
- off += 4*Thearch.Ptrsize + 8
+ off += 4*SysArch.PtrSize + 8
case kindInterface: // reflect.interfaceType
- off += Thearch.Ptrsize + 2*Thearch.Intsize
+ off += SysArch.PtrSize + 2*SysArch.IntSize
default:
// just Sizeof(rtype)
}
- numMethods := int(decode_inuxi(s.P[off+2*Thearch.Ptrsize:], Thearch.Intsize))
- r := decode_reloc(s, int32(off+Thearch.Ptrsize))
- if r.Sym != s {
- panic(fmt.Sprintf("method slice pointer in %s leads to a different symbol %s", s, r.Sym))
- }
- off = int(r.Add) // array of reflect.method values
- sizeofMethod := 4 * Thearch.Ptrsize // sizeof reflect.method in program
- return decode_methodsig(s, off, sizeofMethod, numMethods)
+ mcount := int(decode_inuxi(s.P[off+4:], 2))
+ moff := int(decode_inuxi(s.P[off+4+2:], 2))
+ off += moff // offset to array of reflect.method values
+ const sizeofMethod = 4 * 4 // sizeof reflect.method in program
+ return decode_methodsig(s, off, sizeofMethod, mcount)
}
diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go
index fd177cfef0..bf1a7e74c1 100644
--- a/src/cmd/link/internal/ld/dwarf.go
+++ b/src/cmd/link/internal/ld/dwarf.go
@@ -17,97 +17,34 @@ package ld
import (
"cmd/internal/obj"
"fmt"
+ "log"
"os"
"strings"
)
+const infoprefix = "go.dwarf.info."
+
/*
* 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 infosec *LSym
+var linesec *LSym
var gdbscript string
/*
* Basic I/O
*/
-func addrput(addr int64) {
- switch Thearch.Ptrsize {
+func addrput(s *LSym, addr int64) {
+ switch SysArch.PtrSize {
case 4:
- Thearch.Lput(uint32(addr))
+ Adduint32(Ctxt, s, uint32(addr))
case 8:
- Thearch.Vput(uint64(addr))
+ Adduint64(Ctxt, s, uint64(addr))
}
}
@@ -144,14 +81,14 @@ func appendSleb128(b []byte, v int64) []byte {
var encbuf [10]byte
-func uleb128put(v int64) {
+func uleb128put(s *LSym, v int64) {
b := appendUleb128(encbuf[:0], uint64(v))
- Cwrite(b)
+ Addbytes(Ctxt, s, b)
}
-func sleb128put(v int64) {
+func sleb128put(s *LSym, v int64) {
b := appendSleb128(encbuf[:0], v)
- Cwrite(b)
+ Addbytes(Ctxt, s, b)
}
/*
@@ -462,24 +399,29 @@ var abbrevs = [DW_NABRV]DWAbbrev{
},
}
-func writeabbrev() {
- abbrevo = Cpos()
+var dwarfp *LSym
+
+func writeabbrev() *LSym {
+ s := Linklookup(Ctxt, ".debug_abbrev", 0)
+ s.Type = obj.SDWARFSECT
+ abbrevsym = s
+
for i := 1; i < DW_NABRV; i++ {
// See section 7.5.3
- uleb128put(int64(i))
+ uleb128put(s, int64(i))
- uleb128put(int64(abbrevs[i].tag))
- Cput(abbrevs[i].children)
+ uleb128put(s, int64(abbrevs[i].tag))
+ Adduint8(Ctxt, s, abbrevs[i].children)
for _, f := range abbrevs[i].attr {
- uleb128put(int64(f.attr))
- uleb128put(int64(f.form))
+ uleb128put(s, int64(f.attr))
+ uleb128put(s, int64(f.form))
}
- uleb128put(0)
- uleb128put(0)
+ uleb128put(s, 0)
+ uleb128put(s, 0)
}
- Cput(0)
- abbrevsize = Cpos() - abbrevo
+ Adduint8(Ctxt, s, 0)
+ return s
}
/*
@@ -504,10 +446,7 @@ type DWDie struct {
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 map[string]*DWDie // optional index of DWAttr by name, enabled by mkindex()
+ sym *LSym
}
/*
@@ -556,9 +495,8 @@ func getattr(die *DWDie, attr uint16) *DWAttr {
}
// 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 {
+// written out if it is listed in the abbrev).
+func newdie(parent *DWDie, abbrev int, name string, version int) *DWDie {
die := new(DWDie)
die.abbrev = abbrev
die.link = parent.child
@@ -566,17 +504,17 @@ func newdie(parent *DWDie, abbrev int, name string) *DWDie {
newattr(die, DW_AT_name, DW_CLS_STRING, int64(len(name)), name)
- if parent.hash != nil {
- parent.hash[name] = die
+ if name != "" && (abbrev <= DW_ABRV_VARIABLE || abbrev >= DW_ABRV_NULLTYPE) {
+ if abbrev != DW_ABRV_VARIABLE || version == 0 {
+ die.sym = Linklookup(Ctxt, infoprefix+name, version)
+ die.sym.Attr |= AttrHidden
+ die.sym.Type = obj.SDWARFINFO
+ }
}
return die
}
-func mkindex(die *DWDie) {
- die.hash = make(map[string]*DWDie)
-}
-
func walktypedef(die *DWDie) *DWDie {
// Resolve typedef if present.
if die.abbrev == DW_ABRV_TYPEDECL {
@@ -590,159 +528,157 @@ func walktypedef(die *DWDie) *DWDie {
return die
}
+func walksymtypedef(s *LSym) *LSym {
+ if t := Linkrlookup(Ctxt, s.Name+".def", int(s.Version)); t != nil {
+ return t
+ }
+ return s
+}
+
// Find child by AT_name using hashtable if available or linear scan
// if not.
-func find(die *DWDie, name string) *DWDie {
+func findchild(die *DWDie, name string) *DWDie {
var prev *DWDie
for ; die != prev; prev, die = die, walktypedef(die) {
- if die.hash == nil {
- for a := die.child; a != nil; a = a.link {
- if name == getattr(a, DW_AT_name).data {
- return a
- }
+ for a := die.child; a != nil; a = a.link {
+ if name == getattr(a, DW_AT_name).data {
+ return a
}
- continue
- }
- if a := die.hash[name]; a != nil {
- return a
}
+ continue
}
return nil
}
-func mustFind(die *DWDie, name string) *DWDie {
- r := find(die, name)
+// Used to avoid string allocation when looking up dwarf symbols
+var prefixBuf = []byte(infoprefix)
+
+func find(name string) *LSym {
+ n := append(prefixBuf, name...)
+ // The string allocation below is optimized away because it is only used in a map lookup.
+ s := Linkrlookup(Ctxt, string(n), 0)
+ prefixBuf = n[:len(infoprefix)]
+ return s
+}
+
+func mustFind(name string) *LSym {
+ r := find(name)
if r == nil {
- Exitf("dwarf find: %s %p has no %s", getattr(die, DW_AT_name).data, die, name)
+ Exitf("dwarf find: cannot find %s", 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))
-
+func adddwarfref(ctxt *Link, s *LSym, t *LSym, size int) int64 {
+ var result int64
+ switch size {
default:
- Diag("bad size in adddwarfrel")
+ Diag("invalid size %d in adddwarfref\n", size)
+ fallthrough
+ case SysArch.PtrSize:
+ result = Addaddr(ctxt, s, t)
+ case 4:
+ result = addaddrplus4(ctxt, s, t, 0)
}
+ r := &s.R[len(s.R)-1]
+ r.Type = obj.R_DWARFREF
+ return result
}
-func newrefattr(die *DWDie, attr uint16, ref *DWDie) *DWAttr {
+func newrefattr(die *DWDie, attr uint16, ref *LSym) *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{}) {
+func putattr(s *LSym, 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)
+ Addaddrplus(Ctxt, s, data.(*LSym), value)
break
}
- addrput(value)
+ addrput(s, 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)
+ Adduint8(Ctxt, s, uint8(1+SysArch.PtrSize))
+ Adduint8(Ctxt, s, DW_OP_addr)
+ Addaddr(Ctxt, s, data.(*LSym))
break
}
value &= 0xff
- Cput(uint8(value))
+ Adduint8(Ctxt, s, uint8(value))
p := data.([]byte)
for i := 0; int64(i) < value; i++ {
- Cput(uint8(p[i]))
+ Adduint8(Ctxt, s, p[i])
}
case DW_FORM_block2: // block
value &= 0xffff
- Thearch.Wput(uint16(value))
+ Adduint16(Ctxt, s, uint16(value))
p := data.([]byte)
for i := 0; int64(i) < value; i++ {
- Cput(uint8(p[i]))
+ Adduint8(Ctxt, s, p[i])
}
case DW_FORM_block4: // block
value &= 0xffffffff
- Thearch.Lput(uint32(value))
+ Adduint32(Ctxt, s, uint32(value))
p := data.([]byte)
for i := 0; int64(i) < value; i++ {
- Cput(uint8(p[i]))
+ Adduint8(Ctxt, s, p[i])
}
case DW_FORM_block: // block
- uleb128put(value)
+ uleb128put(s, value)
p := data.([]byte)
for i := 0; int64(i) < value; i++ {
- Cput(uint8(p[i]))
+ Adduint8(Ctxt, s, p[i])
}
case DW_FORM_data1: // constant
- Cput(uint8(value))
+ Adduint8(Ctxt, s, uint8(value))
case DW_FORM_data2: // constant
- Thearch.Wput(uint16(value))
+ Adduint16(Ctxt, s, 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)
+ adddwarfref(Ctxt, s, linesec, 4)
break
}
- Thearch.Lput(uint32(value))
+ Adduint32(Ctxt, s, uint32(value))
case DW_FORM_data8: // constant, {line,loclist,mac,rangelist}ptr
- Thearch.Vput(uint64(value))
+ Adduint64(Ctxt, s, uint64(value))
case DW_FORM_sdata: // constant
- sleb128put(value)
+ sleb128put(s, value)
case DW_FORM_udata: // constant
- uleb128put(value)
+ uleb128put(s, value)
case DW_FORM_string: // string
- strnput(data.(string), int(value+1))
+ str := data.(string)
+ Addstring(s, str)
+ for i := int64(len(str)); i < value; i++ {
+ Adduint8(Ctxt, s, 0)
+ }
case DW_FORM_flag: // flag
if value != 0 {
- Cput(1)
+ Adduint8(Ctxt, s, 1)
} else {
- Cput(0)
+ Adduint8(Ctxt, s, 0)
}
// In DWARF 2 (which is what we claim to generate),
@@ -752,22 +688,14 @@ func putattr(abbrev int, form int, cls int, value int64, data interface{}) {
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.
+ if SysArch.PtrSize == 8 {
+ Adduint64(Ctxt, s, 0) // invalid dwarf, gdb will complain.
} else {
- Thearch.Lput(0) // invalid dwarf, gdb will complain.
+ Adduint32(Ctxt, s, 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)
+ dsym := data.(*LSym)
+ adddwarfref(Ctxt, s, dsym, SysArch.PtrSize)
}
case DW_FORM_ref1, // reference within the compilation unit
@@ -786,34 +714,45 @@ func putattr(abbrev int, form int, cls int, value int64, data interface{}) {
// 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) {
+func putattrs(s *LSym, 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)
+ putattr(s, abbrev, int(f.form), int(ap.cls), ap.value, ap.data)
continue Outer
}
}
- putattr(abbrev, int(f.form), 0, 0, nil)
+ putattr(s, abbrev, int(f.form), 0, 0, nil)
}
}
-func putdies(die *DWDie) {
+func putdies(prev *LSym, die *DWDie) *LSym {
for ; die != nil; die = die.link {
- putdie(die)
+ prev = putdie(prev, die)
}
+ Adduint8(Ctxt, prev, 0)
+ return prev
}
-func putdie(die *DWDie) {
- die.offs = Cpos() - infoo
- uleb128put(int64(die.abbrev))
- putattrs(die.abbrev, die.attr)
+func putdie(prev *LSym, die *DWDie) *LSym {
+ s := die.sym
+ if s == nil {
+ s = prev
+ } else {
+ if s.Attr.OnList() {
+ log.Fatalf("symbol %s listed multiple times", s.Name)
+ }
+ s.Attr |= AttrOnList
+ prev.Next = s
+ }
+ uleb128put(s, int64(die.abbrev))
+ putattrs(s, die.abbrev, die.attr)
if abbrevs[die.abbrev].children != 0 {
- putdies(die.child)
- Cput(0)
+ return putdies(s, die.child)
}
+ return s
}
func reverselist(list **DWDie) {
@@ -880,44 +819,50 @@ func dotypedef(parent *DWDie, name string, def *DWDie) {
Diag("dwarf: bad def in dotypedef")
}
+ def.sym = Linklookup(Ctxt, def.sym.Name+".def", 0)
+ def.sym.Attr |= AttrHidden
+ def.sym.Type = obj.SDWARFINFO
+
// 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)
+ die := newdie(parent, DW_ABRV_TYPEDECL, name, 0)
- newrefattr(die, DW_AT_type, def)
+ newrefattr(die, DW_AT_type, def.sym)
}
// Define gotype, for composite ones recurse into constituents.
-func defgotype(gotype *LSym) *DWDie {
+func defgotype(gotype *LSym) *LSym {
if gotype == nil {
- return mustFind(&dwtypes, "<unspecified>")
+ return mustFind("<unspecified>")
}
if !strings.HasPrefix(gotype.Name, "type.") {
Diag("dwarf: type name doesn't start with \"type.\": %s", gotype.Name)
- return mustFind(&dwtypes, "<unspecified>")
+ return mustFind("<unspecified>")
}
name := gotype.Name[5:] // could also decode from Type.string
- die := find(&dwtypes, name)
+ sdie := find(name)
- if die != nil {
- return die
+ if sdie != nil {
+ return sdie
}
- if false && Debug['v'] > 2 {
- fmt.Printf("new type: %v\n", gotype)
- }
+ return newtype(gotype).sym
+}
+func newtype(gotype *LSym) *DWDie {
+ name := gotype.Name[5:] // could also decode from Type.string
kind := decodetype_kind(gotype)
bytesize := decodetype_size(gotype)
+ var die *DWDie
switch kind {
case obj.KindBool:
- die = newdie(&dwtypes, DW_ABRV_BASETYPE, name)
+ die = newdie(&dwtypes, DW_ABRV_BASETYPE, name, 0)
newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_boolean, 0)
newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0)
@@ -926,7 +871,7 @@ func defgotype(gotype *LSym) *DWDie {
obj.KindInt16,
obj.KindInt32,
obj.KindInt64:
- die = newdie(&dwtypes, DW_ABRV_BASETYPE, name)
+ die = newdie(&dwtypes, DW_ABRV_BASETYPE, name, 0)
newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_signed, 0)
newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0)
@@ -936,66 +881,69 @@ func defgotype(gotype *LSym) *DWDie {
obj.KindUint32,
obj.KindUint64,
obj.KindUintptr:
- die = newdie(&dwtypes, DW_ABRV_BASETYPE, name)
+ die = newdie(&dwtypes, DW_ABRV_BASETYPE, name, 0)
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)
+ die = newdie(&dwtypes, DW_ABRV_BASETYPE, name, 0)
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)
+ die = newdie(&dwtypes, DW_ABRV_BASETYPE, name, 0)
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)
+ die = newdie(&dwtypes, DW_ABRV_ARRAYTYPE, name, 0)
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")
+ fld := newdie(die, DW_ABRV_ARRAYRANGE, "range", 0)
// 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"))
+ newrefattr(fld, DW_AT_type, mustFind("uintptr"))
case obj.KindChan:
- die = newdie(&dwtypes, DW_ABRV_CHANTYPE, name)
+ die = newdie(&dwtypes, DW_ABRV_CHANTYPE, name, 0)
newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0)
s := decodetype_chanelem(gotype)
newrefattr(die, DW_AT_go_elem, defgotype(s))
+ // Save elem type for synthesizechantypes. We could synthesize here
+ // but that would change the order of DIEs we output.
+ newrefattr(die, DW_AT_type, s)
case obj.KindFunc:
- die = newdie(&dwtypes, DW_ABRV_FUNCTYPE, name)
+ die = newdie(&dwtypes, DW_ABRV_FUNCTYPE, name, 0)
dotypedef(&dwtypes, name, die)
- newrefattr(die, DW_AT_type, mustFind(&dwtypes, "void"))
+ newrefattr(die, DW_AT_type, mustFind("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:])
+ fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s.Name[5:], 0)
newrefattr(fld, DW_AT_type, defgotype(s))
}
if decodetype_funcdotdotdot(gotype) {
- newdie(die, DW_ABRV_DOTDOTDOT, "...")
+ newdie(die, DW_ABRV_DOTDOTDOT, "...", 0)
}
nfields = decodetype_funcoutcount(gotype)
for i := 0; i < nfields; i++ {
s = decodetype_funcouttype(gotype, i)
- fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s.Name[5:])
+ fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s.Name[5:], 0)
newrefattr(fld, DW_AT_type, defptrto(defgotype(s)))
}
case obj.KindInterface:
- die = newdie(&dwtypes, DW_ABRV_IFACETYPE, name)
+ die = newdie(&dwtypes, DW_ABRV_IFACETYPE, name, 0)
dotypedef(&dwtypes, name, die)
newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0)
nfields := int(decodetype_ifacemethodcount(gotype))
@@ -1008,31 +956,35 @@ func defgotype(gotype *LSym) *DWDie {
newrefattr(die, DW_AT_type, defgotype(s))
case obj.KindMap:
- die = newdie(&dwtypes, DW_ABRV_MAPTYPE, name)
+ die = newdie(&dwtypes, DW_ABRV_MAPTYPE, name, 0)
s := decodetype_mapkey(gotype)
newrefattr(die, DW_AT_go_key, defgotype(s))
s = decodetype_mapvalue(gotype)
newrefattr(die, DW_AT_go_elem, defgotype(s))
+ // Save gotype for use in synthesizemaptypes. We could synthesize here,
+ // but that would change the order of the DIEs.
+ newrefattr(die, DW_AT_type, gotype)
case obj.KindPtr:
- die = newdie(&dwtypes, DW_ABRV_PTRTYPE, name)
+ die = newdie(&dwtypes, DW_ABRV_PTRTYPE, name, 0)
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)
+ die = newdie(&dwtypes, DW_ABRV_SLICETYPE, name, 0)
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))
+ elem := defgotype(s)
+ newrefattr(die, DW_AT_go_elem, elem)
case obj.KindString:
- die = newdie(&dwtypes, DW_ABRV_STRINGTYPE, name)
+ die = newdie(&dwtypes, DW_ABRV_STRINGTYPE, name, 0)
newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0)
case obj.KindStruct:
- die = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, name)
+ die = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, name, 0)
dotypedef(&dwtypes, name, die)
newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0)
nfields := decodetype_structfieldcount(gotype)
@@ -1045,32 +997,41 @@ func defgotype(gotype *LSym) *DWDie {
if f == "" {
f = s.Name[5:] // skip "type."
}
- fld = newdie(die, DW_ABRV_STRUCTFIELD, f)
+ fld = newdie(die, DW_ABRV_STRUCTFIELD, f, 0)
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)
+ die = newdie(&dwtypes, DW_ABRV_BARE_PTRTYPE, name, 0)
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, "<unspecified>"))
+ die = newdie(&dwtypes, DW_ABRV_TYPEDECL, name, 0)
+ newrefattr(die, DW_AT_type, mustFind("<unspecified>"))
}
newattr(die, DW_AT_go_kind, DW_CLS_CONSTANT, int64(kind), 0)
+ if _, ok := prototypedies[gotype.Name]; ok {
+ prototypedies[gotype.Name] = die
+ }
+
return die
}
+func nameFromDIESym(dwtype *LSym) string {
+ return strings.TrimSuffix(dwtype.Name[len(infoprefix):], ".def")
+}
+
// 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)
+func defptrto(dwtype *LSym) *LSym {
+ ptrname := "*" + nameFromDIESym(dwtype)
+ die := find(ptrname)
if die == nil {
- die = newdie(&dwtypes, DW_ABRV_PTRTYPE, ptrname)
- newrefattr(die, DW_AT_type, dwtype)
+ pdie := newdie(&dwtypes, DW_ABRV_PTRTYPE, ptrname, 0)
+ newrefattr(pdie, DW_AT_type, dwtype)
+ return pdie.sym
}
return die
@@ -1084,7 +1045,7 @@ func copychildrenexcept(dst *DWDie, src *DWDie, except *DWDie) {
if src == except {
continue
}
- c := newdie(dst, src.abbrev, getattr(src, DW_AT_name).data.(string))
+ c := newdie(dst, src.abbrev, getattr(src, DW_AT_name).data.(string), 0)
for a := src.attr; a != nil; a = a.link {
newattr(c, a.atr, int(a.cls), a.value, a.data)
}
@@ -1100,9 +1061,11 @@ func copychildren(dst *DWDie, src *DWDie) {
// 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)
+func substitutetype(structdie *DWDie, field string, dwtype *LSym) {
+ child := findchild(structdie, field)
if child == nil {
+ Exitf("dwarf substitutetype: %s does not have member %s",
+ getattr(structdie, DW_AT_name).data, field)
return
}
@@ -1114,8 +1077,17 @@ func substitutetype(structdie *DWDie, field string, dwtype *DWDie) {
}
}
+func findprotodie(name string) *DWDie {
+ die, ok := prototypedies[name]
+ if ok && die == nil {
+ defgotype(lookup_or_diag(name))
+ die = prototypedies[name]
+ }
+ return die
+}
+
func synthesizestringtypes(die *DWDie) {
- prototype := walktypedef(defgotype(lookup_or_diag("type.runtime.stringStructDWARF")))
+ prototype := walktypedef(findprotodie("type.runtime.stringStructDWARF"))
if prototype == nil {
return
}
@@ -1129,7 +1101,7 @@ func synthesizestringtypes(die *DWDie) {
}
func synthesizeslicetypes(die *DWDie) {
- prototype := walktypedef(defgotype(lookup_or_diag("type.runtime.slice")))
+ prototype := walktypedef(findprotodie("type.runtime.slice"))
if prototype == nil {
return
}
@@ -1139,7 +1111,7 @@ func synthesizeslicetypes(die *DWDie) {
continue
}
copychildren(die, prototype)
- elem := getattr(die, DW_AT_go_elem).data.(*DWDie)
+ elem := getattr(die, DW_AT_go_elem).data.(*LSym)
substitutetype(die, "array", defptrto(elem))
}
}
@@ -1163,9 +1135,21 @@ const (
BucketSize = 8
)
+func mkinternaltype(abbrev int, typename, keyname, valname string, f func(*DWDie)) *LSym {
+ name := mkinternaltypename(typename, keyname, valname)
+ symname := infoprefix + name
+ s := Linkrlookup(Ctxt, symname, 0)
+ if s != nil {
+ return s
+ }
+ die := newdie(&dwtypes, abbrev, name, 0)
+ f(die)
+ return die.sym
+}
+
func synthesizemaptypes(die *DWDie) {
- hash := walktypedef(defgotype(lookup_or_diag("type.runtime.hmap")))
- bucket := walktypedef(defgotype(lookup_or_diag("type.runtime.bmap")))
+ hash := walktypedef(findprotodie("type.runtime.hmap"))
+ bucket := walktypedef(findprotodie("type.runtime.bmap"))
if hash == nil {
return
@@ -1175,97 +1159,92 @@ func synthesizemaptypes(die *DWDie) {
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))
+ gotype := getattr(die, DW_AT_type).data.(*LSym)
+ keytype := decodetype_mapkey(gotype)
+ valtype := decodetype_mapvalue(gotype)
+ keysize, valsize := decodetype_size(keytype), decodetype_size(valtype)
+ keytype, valtype = walksymtypedef(defgotype(keytype)), walksymtypedef(defgotype(valtype))
// compute size info like hashmap.c does.
- keysize, valsize := Thearch.Ptrsize, Thearch.Ptrsize
- a := getattr(keytype, DW_AT_byte_size)
- if a != nil {
- keysize = int(a.value)
- }
- a = getattr(valtype, DW_AT_byte_size)
- if a != nil {
- valsize = int(a.value)
- }
indirect_key, indirect_val := false, false
if keysize > MaxKeySize {
- keysize = Thearch.Ptrsize
+ keysize = int64(SysArch.PtrSize)
indirect_key = true
}
if valsize > MaxValSize {
- valsize = Thearch.Ptrsize
+ valsize = int64(SysArch.PtrSize)
indirect_val = true
}
// 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 {
- 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"))
+ keyname := nameFromDIESym(keytype)
+ dwhks := mkinternaltype(DW_ABRV_ARRAYTYPE, "[]key", keyname, "", func(dwhk *DWDie) {
+ newattr(dwhk, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize*keysize, 0)
+ t := keytype
+ if indirect_key {
+ t = defptrto(keytype)
+ }
+ newrefattr(dwhk, DW_AT_type, t)
+ fld := newdie(dwhk, DW_ABRV_ARRAYRANGE, "size", 0)
+ newattr(fld, DW_AT_count, DW_CLS_CONSTANT, BucketSize, 0)
+ newrefattr(fld, DW_AT_type, mustFind("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 {
- 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"))
+ valname := nameFromDIESym(valtype)
+ dwhvs := mkinternaltype(DW_ABRV_ARRAYTYPE, "[]val", valname, "", func(dwhv *DWDie) {
+ newattr(dwhv, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize*valsize, 0)
+ t := valtype
+ if indirect_val {
+ t = defptrto(valtype)
+ }
+ newrefattr(dwhv, DW_AT_type, t)
+ fld := newdie(dwhv, DW_ABRV_ARRAYRANGE, "size", 0)
+ newattr(fld, DW_AT_count, DW_CLS_CONSTANT, BucketSize, 0)
+ newrefattr(fld, DW_AT_type, mustFind("uintptr"))
+ })
// Construct bucket<K,V>
- 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"))
+ dwhbs := mkinternaltype(DW_ABRV_STRUCTTYPE, "bucket", keyname, valname, func(dwhb *DWDie) {
+ // Copy over all fields except the field "data" from the generic
+ // bucket. "data" will be replaced with keys/values below.
+ copychildrenexcept(dwhb, bucket, findchild(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))
- }
+ fld := newdie(dwhb, DW_ABRV_STRUCTFIELD, "keys", 0)
+ newrefattr(fld, DW_AT_type, dwhks)
+ newmemberoffsetattr(fld, BucketSize)
+ fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "values", 0)
+ newrefattr(fld, DW_AT_type, dwhvs)
+ newmemberoffsetattr(fld, BucketSize+BucketSize*int32(keysize))
+ fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "overflow", 0)
+ newrefattr(fld, DW_AT_type, defptrto(dwhb.sym))
+ newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize)))
+ if SysArch.RegSize > SysArch.PtrSize {
+ fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "pad", 0)
+ newrefattr(fld, DW_AT_type, mustFind("uintptr"))
+ newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))+int32(SysArch.PtrSize))
+ }
- newattr(dwhb, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize+BucketSize*int64(keysize)+BucketSize*int64(valsize)+int64(Thearch.Regsize), 0)
+ newattr(dwhb, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize+BucketSize*keysize+BucketSize*valsize+int64(SysArch.RegSize), 0)
+ })
// Construct hash<K,V>
- 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)
+ dwhs := mkinternaltype(DW_ABRV_STRUCTTYPE, "hash", keyname, valname, func(dwh *DWDie) {
+ copychildren(dwh, hash)
+ substitutetype(dwh, "buckets", defptrto(dwhbs))
+ substitutetype(dwh, "oldbuckets", defptrto(dwhbs))
+ newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, getattr(hash, DW_AT_byte_size).value, nil)
+ })
// make map type a pointer to hash<K,V>
- newrefattr(die, DW_AT_type, defptrto(dwh))
+ newrefattr(die, DW_AT_type, defptrto(dwhs))
}
}
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")))
+ sudog := walktypedef(findprotodie("type.runtime.sudog"))
+ waitq := walktypedef(findprotodie("type.runtime.waitq"))
+ hchan := walktypedef(findprotodie("type.runtime.hchan"))
if sudog == nil || waitq == nil || hchan == nil {
return
}
@@ -1276,42 +1255,41 @@ func synthesizechantypes(die *DWDie) {
if die.abbrev != DW_ABRV_CHANTYPE {
continue
}
- elemsize := Thearch.Ptrsize
- elemtype := getattr(die, DW_AT_go_elem).data.(*DWDie)
- a := getattr(elemtype, DW_AT_byte_size)
- if a != nil {
- elemsize = int(a.value)
- }
+ elemgotype := getattr(die, DW_AT_type).data.(*LSym)
+ elemsize := decodetype_size(elemgotype)
+ elemname := elemgotype.Name[5:]
+ elemtype := walksymtypedef(defgotype(elemgotype))
// sudog<T>
- 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)
+ dwss := mkinternaltype(DW_ABRV_STRUCTTYPE, "sudog", elemname, "", func(dws *DWDie) {
+ 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)+elemsize, nil)
+ })
// waitq<T>
- dww := newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("waitq", getattr(elemtype, DW_AT_name).data.(string), ""))
+ dwws := mkinternaltype(DW_ABRV_STRUCTTYPE, "waitq", elemname, "", func(dww *DWDie) {
- 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)
+ copychildren(dww, waitq)
+ substitutetype(dww, "first", defptrto(dwss))
+ substitutetype(dww, "last", defptrto(dwss))
+ newattr(dww, DW_AT_byte_size, DW_CLS_CONSTANT, getattr(waitq, DW_AT_byte_size).value, nil)
+ })
// hchan<T>
- dwh := newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("hchan", getattr(elemtype, DW_AT_name).data.(string), ""))
+ dwhs := mkinternaltype(DW_ABRV_STRUCTTYPE, "hchan", elemname, "", func(dwh *DWDie) {
+ copychildren(dwh, hchan)
+ substitutetype(dwh, "recvq", dwws)
+ substitutetype(dwh, "sendq", dwws)
+ newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, getattr(hchan, DW_AT_byte_size).value, nil)
+ })
- 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))
+ newrefattr(die, DW_AT_type, defptrto(dwhs))
}
}
@@ -1331,13 +1309,13 @@ func defdwsymb(sym *LSym, s string, t int, v int64, size int64, ver int, gotype
var dv *DWDie
- var dt *DWDie
+ var dt *LSym
switch t {
default:
return
case 'd', 'b', 'D', 'B':
- dv = newdie(&dwglobals, DW_ABRV_VARIABLE, s)
+ dv = newdie(&dwglobals, DW_ABRV_VARIABLE, s, ver)
newabslocexprattr(dv, v, sym)
if ver == 0 {
newattr(dv, DW_AT_external, DW_CLS_FLAG, 1, 0)
@@ -1367,8 +1345,8 @@ func finddebugruntimepath(s *LSym) {
return
}
- for i := range s.Pcln.File {
- f := s.Pcln.File[i]
+ for i := range s.FuncInfo.File {
+ f := s.FuncInfo.File[i]
if i := strings.Index(f.Name, "runtime/runtime.go"); i >= 0 {
gdbscript = f.Name[:i] + "runtime/runtime-gdb.py"
break
@@ -1386,23 +1364,23 @@ const (
OPCODE_BASE = 10
)
-func putpclcdelta(delta_pc int64, delta_lc int64) {
+func putpclcdelta(s *LSym, 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))
+ Adduint8(Ctxt, s, uint8(opcode))
return
}
}
if delta_pc != 0 {
- Cput(DW_LNS_advance_pc)
- sleb128put(delta_pc)
+ Adduint8(Ctxt, s, DW_LNS_advance_pc)
+ sleb128put(s, delta_pc)
}
- Cput(DW_LNS_advance_line)
- sleb128put(delta_lc)
- Cput(DW_LNS_copy)
+ Adduint8(Ctxt, s, DW_LNS_advance_line)
+ sleb128put(s, delta_lc)
+ Adduint8(Ctxt, s, DW_LNS_copy)
}
func newcfaoffsetattr(die *DWDie, offs int32) {
@@ -1428,26 +1406,6 @@ func mkvarname(name string, da int) string {
* 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
@@ -1455,28 +1413,30 @@ func getCompilationDir() string {
return "/"
}
-func writelines() {
+func writelines(prev *LSym) *LSym {
if linesec == nil {
- linesec = Linklookup(Ctxt, ".dwarfline", 0)
+ linesec = Linklookup(Ctxt, ".debug_line", 0)
}
+ linesec.Type = obj.SDWARFSECT
linesec.R = linesec.R[:0]
+ ls := linesec
+ prev.Next = ls
+
unitstart := int64(-1)
+ headerstart := 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
+ s := Ctxt.Textp[0]
- dwinfo = newdie(&dwroot, DW_ABRV_COMPUNIT, "go")
+ dwinfo = newdie(&dwroot, DW_ABRV_COMPUNIT, "go", 0)
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_stmt_list, DW_CLS_PTR, 0, 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()
@@ -1484,61 +1444,62 @@ func writelines() {
// 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.
+ unit_length_offset := ls.Size
+ Adduint32(Ctxt, ls, 0) // unit_length (*), filled in at end.
+ unitstart = ls.Size
+ Adduint16(Ctxt, ls, 2) // dwarf version (appendix F)
+ header_length_offset := ls.Size
+ Adduint32(Ctxt, ls, 0) // header_length (*), filled in at end.
+ headerstart = ls.Size
// 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)
+ Adduint8(Ctxt, ls, 1) // minimum_instruction_length
+ Adduint8(Ctxt, ls, 1) // default_is_stmt
+ Adduint8(Ctxt, ls, LINE_BASE&0xFF) // line_base
+ Adduint8(Ctxt, ls, LINE_RANGE) // line_range
+ Adduint8(Ctxt, ls, OPCODE_BASE) // opcode_base
+ Adduint8(Ctxt, ls, 0) // standard_opcode_lengths[1]
+ Adduint8(Ctxt, ls, 1) // standard_opcode_lengths[2]
+ Adduint8(Ctxt, ls, 1) // standard_opcode_lengths[3]
+ Adduint8(Ctxt, ls, 1) // standard_opcode_lengths[4]
+ Adduint8(Ctxt, ls, 1) // standard_opcode_lengths[5]
+ Adduint8(Ctxt, ls, 0) // standard_opcode_lengths[6]
+ Adduint8(Ctxt, ls, 0) // standard_opcode_lengths[7]
+ Adduint8(Ctxt, ls, 0) // standard_opcode_lengths[8]
+ Adduint8(Ctxt, ls, 1) // standard_opcode_lengths[9]
+ Adduint8(Ctxt, ls, 0) // include_directories (empty)
- 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)
+ for _, f := range Ctxt.Filesyms {
+ Addstring(ls, f.Name)
+ Adduint8(Ctxt, ls, 0)
+ Adduint8(Ctxt, ls, 0)
+ Adduint8(Ctxt, ls, 0)
}
// 4 zeros: the string termination + 3 fields.
- Cput(0)
+ Adduint8(Ctxt, ls, 0)
// terminate file_names.
- headerend = Cpos()
+ headerend = ls.Size
- Cput(0) // start extended opcode
- uleb128put(1 + int64(Thearch.Ptrsize))
- Cput(DW_LNE_set_address)
+ Adduint8(Ctxt, ls, 0) // start extended opcode
+ uleb128put(ls, 1+int64(SysArch.PtrSize))
+ Adduint8(Ctxt, ls, DW_LNE_set_address)
pc := s.Value
line := 1
file := 1
if Linkmode == LinkExternal {
- adddwarfrel(linesec, s, lineo, Thearch.Ptrsize, 0)
+ Addaddr(Ctxt, ls, s)
} else {
- addrput(pc)
+ addrput(ls, pc)
}
var pcfile Pciter
var pcline Pciter
- for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next {
- s = Ctxt.Cursym
+ for _, Ctxt.Cursym = range Ctxt.Textp {
+ s := Ctxt.Cursym
- dwfunc := newdie(dwinfo, DW_ABRV_FUNCTION, s.Name)
+ dwfunc := newdie(dwinfo, DW_ABRV_FUNCTION, s.Name, int(s.Version))
newattr(dwfunc, DW_AT_low_pc, DW_CLS_ADDRESS, s.Value, s)
epc = s.Value + s.Size
epcs = s
@@ -1547,14 +1508,14 @@ func writelines() {
newattr(dwfunc, DW_AT_external, DW_CLS_FLAG, 1, 0)
}
- if s.Pcln == nil {
+ if s.FuncInfo == nil {
continue
}
finddebugruntimepath(s)
- pciterinit(Ctxt, &pcfile, &s.Pcln.Pcfile)
- pciterinit(Ctxt, &pcline, &s.Pcln.Pcline)
+ pciterinit(Ctxt, &pcfile, &s.FuncInfo.Pcfile)
+ pciterinit(Ctxt, &pcline, &s.FuncInfo.Pcline)
epc = pc
for pcfile.done == 0 && pcline.done == 0 {
if epc-s.Value >= int64(pcfile.nextpc) {
@@ -1568,12 +1529,12 @@ func writelines() {
}
if int32(file) != pcfile.value {
- Cput(DW_LNS_set_file)
- uleb128put(int64(pcfile.value))
+ Adduint8(Ctxt, ls, DW_LNS_set_file)
+ uleb128put(ls, int64(pcfile.value))
file = int(pcfile.value)
}
- putpclcdelta(s.Value+int64(pcline.pc)-pc, int64(pcline.value)-int64(line))
+ putpclcdelta(ls, s.Value+int64(pcline.pc)-pc, int64(pcline.value)-int64(line))
pc = s.Value + int64(pcline.pc)
line = int(pcline.value)
@@ -1589,13 +1550,13 @@ func writelines() {
dt, da int
offs int64
)
- for _, a := range s.Autom {
+ for _, a := range s.FuncInfo.Autom {
switch a.Name {
case obj.A_AUTO:
dt = DW_ABRV_AUTO
offs = int64(a.Aoffset)
if !haslinkregister() {
- offs -= int64(Thearch.Ptrsize)
+ offs -= int64(SysArch.PtrSize)
}
case obj.A_PARAM:
@@ -1610,7 +1571,7 @@ func writelines() {
continue
}
var n string
- if find(dwfunc, a.Asym.Name) != nil {
+ if findchild(dwfunc, a.Asym.Name) != nil {
n = mkvarname(a.Asym.Name, da)
} else {
n = a.Asym.Name
@@ -1621,7 +1582,7 @@ func writelines() {
n = n[i+1:]
}
- dwvar := newdie(dwfunc, dt, n)
+ dwvar := newdie(dwfunc, dt, n, 0)
newcfaoffsetattr(dwvar, int32(offs))
newrefattr(dwvar, DW_AT_type, defgotype(a.Gotype))
@@ -1642,22 +1603,29 @@ func writelines() {
}
}
- flushunit(dwinfo, epc, epcs, unitstart, int32(headerend-unitstart-10))
- linesize = Cpos() - lineo
+ Adduint8(Ctxt, ls, 0) // start extended opcode
+ uleb128put(ls, 1)
+ Adduint8(Ctxt, ls, DW_LNE_end_sequence)
+
+ newattr(dwinfo, DW_AT_high_pc, DW_CLS_ADDRESS, epc+1, epcs)
+
+ setuint32(Ctxt, ls, unit_length_offset, uint32(ls.Size-unitstart))
+ setuint32(Ctxt, ls, header_length_offset, uint32(headerend-headerstart))
+
+ return ls
}
/*
* Emit .debug_frame
*/
const (
- CIERESERVE = 16
- DATAALIGNMENTFACTOR = -4
+ dataAlignmentFactor = -4
)
// appendPCDeltaCFA appends per-PC CFA deltas to b and returns the final slice.
func appendPCDeltaCFA(b []byte, deltapc, cfa int64) []byte {
b = append(b, DW_CFA_def_cfa_offset_sf)
- b = appendSleb128(b, cfa/DATAALIGNMENTFACTOR)
+ b = appendSleb128(b, cfa/dataAlignmentFactor)
switch {
case deltapc < 0x40:
@@ -1675,60 +1643,68 @@ func appendPCDeltaCFA(b []byte, deltapc, cfa int64) []byte {
return b
}
-func writeframes() {
+func writeframes(prev *LSym) *LSym {
if framesec == nil {
- framesec = Linklookup(Ctxt, ".dwarfframe", 0)
+ framesec = Linklookup(Ctxt, ".debug_frame", 0)
}
+ framesec.Type = obj.SDWARFSECT
framesec.R = framesec.R[:0]
- frameo = Cpos()
+ fs := framesec
+ prev.Next = fs
// 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)
+ cieReserve := uint32(16)
if haslinkregister() {
- uleb128put(int64(0)) // offset
- } else {
- uleb128put(int64(Thearch.Ptrsize)) // offset
+ cieReserve = 32
}
+ Adduint32(Ctxt, fs, cieReserve) // initial length, must be multiple of pointer size
+ Adduint32(Ctxt, fs, 0xffffffff) // cid.
+ Adduint8(Ctxt, fs, 3) // dwarf version (appendix F)
+ Adduint8(Ctxt, fs, 0) // augmentation ""
+ uleb128put(fs, 1) // code_alignment_factor
+ sleb128put(fs, dataAlignmentFactor) // all CFI offset calculations include multiplication with this factor
+ uleb128put(fs, int64(Thearch.Dwarfreglr)) // return_address_register
- Cput(DW_CFA_offset_extended)
- uleb128put(int64(Thearch.Dwarfreglr)) // return address
+ Adduint8(Ctxt, fs, DW_CFA_def_cfa) // Set the current frame address..
+ uleb128put(fs, int64(Thearch.Dwarfregsp)) // ...to use the value in the platform's SP register (defined in l.go)...
if haslinkregister() {
- uleb128put(int64(0) / DATAALIGNMENTFACTOR) // at cfa - 0
+ uleb128put(fs, int64(0)) // ...plus a 0 offset.
+
+ Adduint8(Ctxt, fs, DW_CFA_same_value) // The platform's link register is unchanged during the prologue.
+ uleb128put(fs, int64(Thearch.Dwarfreglr))
+
+ Adduint8(Ctxt, fs, DW_CFA_val_offset) // The previous value...
+ uleb128put(fs, int64(Thearch.Dwarfregsp)) // ...of the platform's SP register...
+ uleb128put(fs, int64(0)) // ...is CFA+0.
} else {
- uleb128put(int64(-Thearch.Ptrsize) / DATAALIGNMENTFACTOR) // at cfa - x*4
+ uleb128put(fs, int64(SysArch.PtrSize)) // ...plus the word size (because the call instruction implicitly adds one word to the frame).
+
+ Adduint8(Ctxt, fs, DW_CFA_offset_extended) // The previous value...
+ uleb128put(fs, int64(Thearch.Dwarfreglr)) // ...of the return address...
+ uleb128put(fs, int64(-SysArch.PtrSize)/dataAlignmentFactor) // ...is saved at [CFA - (PtrSize/4)].
}
// 4 is to exclude the length field.
- pad := CIERESERVE + frameo + 4 - Cpos()
+ pad := int64(cieReserve) + 4 - fs.Size
if pad < 0 {
- Exitf("dwarf: CIERESERVE too small by %d bytes.", -pad)
+ Exitf("dwarf: cieReserve too small by %d bytes.", -pad)
}
- strnput("", int(pad))
+ Addbytes(Ctxt, fs, zeros[:pad])
var deltaBuf []byte
var pcsp Pciter
- for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next {
+ for _, Ctxt.Cursym = range Ctxt.Textp {
s := Ctxt.Cursym
- if s.Pcln == nil {
+ if s.FuncInfo == nil {
continue
}
// Emit a FDE, Section 6.4.1.
// First build the section contents into a byte buffer.
deltaBuf = deltaBuf[:0]
- for pciterinit(Ctxt, &pcsp, &s.Pcln.Pcsp); pcsp.done == 0; pciternext(&pcsp) {
+ for pciterinit(Ctxt, &pcsp, &s.FuncInfo.Pcsp); pcsp.done == 0; pciternext(&pcsp) {
nextpc := pcsp.nextpc
// pciterinit goes up to the end of the function,
@@ -1741,12 +1717,27 @@ func writeframes() {
}
if haslinkregister() {
+ // TODO(bryanpkc): This is imprecise. In general, the instruction
+ // that stores the return address to the stack frame is not the
+ // same one that allocates the frame.
+ if pcsp.value > 0 {
+ // The return address is preserved at (CFA-frame_size)
+ // after a stack frame has been allocated.
+ deltaBuf = append(deltaBuf, DW_CFA_offset_extended_sf)
+ deltaBuf = appendUleb128(deltaBuf, uint64(Thearch.Dwarfreglr))
+ deltaBuf = appendSleb128(deltaBuf, -int64(pcsp.value)/dataAlignmentFactor)
+ } else {
+ // The return address is restored into the link register
+ // when a stack frame has been de-allocated.
+ deltaBuf = append(deltaBuf, DW_CFA_same_value)
+ deltaBuf = appendUleb128(deltaBuf, uint64(Thearch.Dwarfreglr))
+ }
deltaBuf = appendPCDeltaCFA(deltaBuf, int64(nextpc)-int64(pcsp.pc), int64(pcsp.value))
} else {
- deltaBuf = appendPCDeltaCFA(deltaBuf, int64(nextpc)-int64(pcsp.pc), int64(Thearch.Ptrsize)+int64(pcsp.value))
+ deltaBuf = appendPCDeltaCFA(deltaBuf, int64(nextpc)-int64(pcsp.pc), int64(SysArch.PtrSize)+int64(pcsp.value))
}
}
- pad := int(Rnd(int64(len(deltaBuf)), int64(Thearch.Ptrsize))) - len(deltaBuf)
+ pad := int(Rnd(int64(len(deltaBuf)), int64(SysArch.PtrSize))) - len(deltaBuf)
deltaBuf = append(deltaBuf, zeros[:pad]...)
// Emit the FDE header, Section 6.4.1.
@@ -1754,21 +1745,17 @@ func writeframes() {
// 4 bytes: Pointer to the CIE above, at offset 0
// ptrsize: initial location
// ptrsize: address range
- Thearch.Lput(uint32(4 + 2*Thearch.Ptrsize + len(deltaBuf))) // length (excludes itself)
+ Adduint32(Ctxt, fs, uint32(4+2*SysArch.PtrSize+len(deltaBuf))) // length (excludes itself)
if Linkmode == LinkExternal {
- adddwarfrel(framesec, framesym, frameo, 4, 0) // CIE offset
- adddwarfrel(framesec, s, frameo, Thearch.Ptrsize, 0) // initial location
+ adddwarfref(Ctxt, fs, framesec, 4)
} else {
- Thearch.Lput(0) // CIE offset
- addrput(s.Value) // initial location
+ Adduint32(Ctxt, fs, 0) // CIE offset
}
- addrput(s.Size) // address range
-
- Cwrite(deltaBuf)
+ Addaddr(Ctxt, fs, s)
+ addrput(fs, s.Size) // address range
+ Addbytes(Ctxt, fs, deltaBuf)
}
-
- Cflush()
- framesize = Cpos() - frameo
+ return fs
}
/*
@@ -1778,12 +1765,14 @@ const (
COMPUNITHEADERSIZE = 4 + 2 + 4 + 1
)
-func writeinfo() {
- fwdcount = 0
+func writeinfo(prev *LSym) *LSym {
if infosec == nil {
- infosec = Linklookup(Ctxt, ".dwarfinfo", 0)
+ infosec = Linklookup(Ctxt, ".debug_info", 0)
}
infosec.R = infosec.R[:0]
+ infosec.Type = obj.SDWARFINFO
+ infosec.Attr |= AttrReachable
+ prev.Next, prev = infosec, infosec
if arangessec == nil {
arangessec = Linklookup(Ctxt, ".dwarfaranges", 0)
@@ -1791,32 +1780,30 @@ func writeinfo() {
arangessec.R = arangessec.R[:0]
for compunit := dwroot.child; compunit != nil; compunit = compunit.link {
- unitstart := Cpos()
+ s := compunit.sym
+ prev.Next, prev = s, s
// 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)
+ Adduint32(Ctxt, s, 0) // unit_length (*), will be filled in later.
+ Adduint16(Ctxt, s, 2) // dwarf version (appendix F)
// debug_abbrev_offset (*)
- if Linkmode == LinkExternal {
- adddwarfrel(infosec, abbrevsym, infoo, 4, 0)
- } else {
- Thearch.Lput(0)
- }
+ adddwarfref(Ctxt, s, abbrevsym, 4)
- Cput(uint8(Thearch.Ptrsize)) // address_size
+ Adduint8(Ctxt, s, uint8(SysArch.PtrSize)) // address_size
- putdie(compunit)
+ prev = putdie(prev, compunit)
+ cusize := s.Size - 4 // exclude the length field.
+ for child := s.Next; child != nil; child = child.Next {
+ cusize += child.Size
+ }
- here := Cpos()
- Cseek(unitstart)
- Thearch.Lput(uint32(here - unitstart - 4)) // exclude the length field.
- Cseek(here)
+ setuint32(Ctxt, s, 0, uint32(cusize))
+ newattr(compunit, DW_AT_byte_size, DW_CLS_CONSTANT, cusize, 0)
}
-
- Cflush()
+ return prev
}
/*
@@ -1837,51 +1824,52 @@ func ispubtype(die *DWDie) bool {
return die.abbrev >= DW_ABRV_NULLTYPE
}
-func writepub(ispub func(*DWDie) bool) int64 {
- sectionstart := Cpos()
+func writepub(sname string, ispub func(*DWDie) bool, prev *LSym) *LSym {
+ s := Linklookup(Ctxt, sname, 0)
+ s.Type = obj.SDWARFSECT
+ prev.Next = s
for compunit := dwroot.child; compunit != nil; compunit = compunit.link {
- unitend := infoo + infosize
- unitstart := compunit.offs - COMPUNITHEADERSIZE
- if compunit.link != nil {
- unitend = compunit.link.offs - COMPUNITHEADERSIZE
- }
+ sectionstart := s.Size
+ culength := uint32(getattr(compunit, DW_AT_byte_size).value) + 4
// 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
+ Adduint32(Ctxt, s, 0) // unit_length (*), will be filled in later.
+ Adduint16(Ctxt, s, 2) // dwarf version (appendix F)
+ adddwarfref(Ctxt, s, compunit.sym, 4) // debug_info_offset (of the Comp unit Header)
+ Adduint32(Ctxt, s, culength) // 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))
+ name := dwa.data.(string)
+ if die.sym == nil {
+ fmt.Println("Missing sym for ", name)
+ }
+ adddwarfref(Ctxt, s, die.sym, 4)
+ Addstring(s, name)
}
- Thearch.Lput(0)
+ Adduint32(Ctxt, s, 0)
- here := Cpos()
- Cseek(sectionstart)
- Thearch.Lput(uint32(here - sectionstart - 4)) // exclude the length field.
- Cseek(here)
+ setuint32(Ctxt, s, sectionstart, uint32(s.Size-sectionstart)-4) // exclude the length field.
}
- return sectionstart
+ return s
}
/*
* emit .debug_aranges. _info must have been written before,
* because we need die->offs of dw_globals.
*/
-func writearanges() int64 {
- sectionstart := Cpos()
+func writearanges(prev *LSym) *LSym {
+ s := Linklookup(Ctxt, ".debug_aranges", 0)
+ s.Type = obj.SDWARFSECT
// The first tuple is aligned to a multiple of the size of a single tuple
// (twice the size of an address)
- headersize := int(Rnd(4+2+4+1+1, int64(Thearch.Ptrsize*2))) // don't count unit_length field itself
+ headersize := int(Rnd(4+2+4+1+1, int64(SysArch.PtrSize*2))) // don't count unit_length field itself
for compunit := dwroot.child; compunit != nil; compunit = compunit.link {
b := getattr(compunit, DW_AT_low_pc)
@@ -1894,78 +1882,46 @@ func writearanges() int64 {
}
// 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)
+ unitlength := uint32(headersize) + 4*uint32(SysArch.PtrSize) - 4
+ Adduint32(Ctxt, s, unitlength) // unit_length (*)
+ Adduint16(Ctxt, s, 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))
- }
+ adddwarfref(Ctxt, s, compunit.sym, 4)
- 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)
+ Adduint8(Ctxt, s, uint8(SysArch.PtrSize)) // address_size
+ Adduint8(Ctxt, s, 0) // segment_size
+ padding := headersize - (4 + 2 + 4 + 1 + 1)
+ for i := 0; i < padding; i++ {
+ Adduint8(Ctxt, s, 0)
}
- addrput(e.value - b.value)
- addrput(0)
- addrput(0)
+ Addaddrplus(Ctxt, s, b.data.(*LSym), b.value-(b.data.(*LSym)).Value)
+ addrput(s, e.value-b.value)
+ addrput(s, 0)
+ addrput(s, 0)
}
-
- Cflush()
- return sectionstart
-}
-
-func writegdbscript() int64 {
- sectionstart := Cpos()
-
- if gdbscript != "" {
- Cput(1) // magic 1 byte?
- strnput(gdbscript, len(gdbscript)+1)
- Cflush()
+ if s.Size > 0 {
+ prev.Next = s
+ prev = s
}
-
- return sectionstart
+ return prev
}
-func align(size int64) {
- if HEADTYPE == obj.Hwindows { // Only Windows PE need section align.
- strnput("", int(Rnd(size, PEFILEALIGN)-size))
- }
-}
+func writegdbscript(prev *LSym) *LSym {
-func writedwarfreloc(s *LSym) int64 {
- start := Cpos()
- for ri := 0; ri < len(s.R); ri++ {
- r := &s.R[ri]
- i := -1
- if Iself {
- i = Thearch.Elfreloc1(r, int64(r.Off))
- } else if HEADTYPE == obj.Hdarwin {
- i = Thearch.Machoreloc1(r, int64(r.Off))
- }
- if i < 0 {
- Diag("unsupported obj reloc %d/%d to %s", r.Type, r.Siz, r.Sym.Name)
- }
+ if gdbscript != "" {
+ s := Linklookup(Ctxt, ".debug_gdb_scripts", 0)
+ s.Type = obj.SDWARFSECT
+ prev.Next = s
+ prev = s
+ Adduint8(Ctxt, s, 1) // magic 1 byte?
+ Addstring(s, gdbscript)
}
- return start
+ return prev
}
-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
-}
+var prototypedies map[string]*DWDie
/*
* This is the main entry point for generating dwarf. After emitting
@@ -1976,58 +1932,52 @@ func addmachodwarfsect(prev *Section, name string) *Section {
* passes.
*
*/
-func Dwarfemitdebugsections() {
+func dwarfgeneratedebugsyms() {
if Debug['w'] != 0 { // disable dwarf
return
}
+ if Debug['s'] != 0 && HEADTYPE != obj.Hdarwin {
+ return
+ }
+ if HEADTYPE == obj.Hplan9 {
+ 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.Attr |= AttrHidden
-
- abbrevsym = Linklookup(Ctxt, ".debug_abbrev", 0)
- abbrevsym.Attr |= AttrHidden
-
- linesym = Linklookup(Ctxt, ".debug_line", 0)
- linesym.Attr |= AttrHidden
+ }
- framesym = Linklookup(Ctxt, ".debug_frame", 0)
- framesym.Attr |= AttrHidden
- }
+ if Debug['v'] != 0 {
+ fmt.Fprintf(Bso, "%5.2f dwarf\n", obj.Cputime())
}
// 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, "<unspecified>")
+ newdie(&dwtypes, DW_ABRV_NULLTYPE, "<unspecified>", 0)
- newdie(&dwtypes, DW_ABRV_NULLTYPE, "void")
- newdie(&dwtypes, DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer")
+ newdie(&dwtypes, DW_ABRV_NULLTYPE, "void", 0)
+ newdie(&dwtypes, DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer", 0)
- die := newdie(&dwtypes, DW_ABRV_BASETYPE, "uintptr") // needed for array size
+ die := newdie(&dwtypes, DW_ABRV_BASETYPE, "uintptr", 0) // 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_byte_size, DW_CLS_CONSTANT, int64(SysArch.PtrSize), 0)
newattr(die, DW_AT_go_kind, DW_CLS_CONSTANT, obj.KindUintptr, 0)
+ // Prototypes needed for type synthesis.
+ prototypedies = map[string]*DWDie{
+ "type.runtime.stringStructDWARF": nil,
+ "type.runtime.slice": nil,
+ "type.runtime.hmap": nil,
+ "type.runtime.bmap": nil,
+ "type.runtime.sudog": nil,
+ "type.runtime.waitq": nil,
+ "type.runtime.hchan": nil,
+ }
+
// Needed by the prettyprinter code for interface inspection.
defgotype(lookup_or_diag("type.runtime._type"))
@@ -2036,12 +1986,10 @@ func Dwarfemitdebugsections() {
genasmsym(defdwsymb)
- writeabbrev()
- align(abbrevsize)
- writelines()
- align(linesize)
- writeframes()
- align(framesize)
+ dwarfp = writeabbrev()
+ last := dwarfp
+ last = writelines(last)
+ last = writeframes(last)
synthesizestringtypes(dwtypes.child)
synthesizeslicetypes(dwtypes.child)
@@ -2055,412 +2003,61 @@ func Dwarfemitdebugsections() {
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)
+ // Need to reorder symbols so SDWARFINFO is after all SDWARFSECT
+ // (but we need to generate dies before writepub)
+ writeinfo(last)
+ infosyms := last.Next
- 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)
+ last = writepub(".debug_pubnames", ispubname, last)
+ last = writepub(".debug_pubtypes", ispubtype, last)
+ last = writearanges(last)
+ last = writegdbscript(last)
+ last.Next = infosyms
}
/*
* 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")
+ Addstring(shstrtab, ".debug_abbrev")
+ Addstring(shstrtab, ".debug_aranges")
+ Addstring(shstrtab, ".debug_frame")
+ Addstring(shstrtab, ".debug_info")
+ Addstring(shstrtab, ".debug_line")
+ Addstring(shstrtab, ".debug_pubnames")
+ Addstring(shstrtab, ".debug_pubtypes")
+ Addstring(shstrtab, ".debug_gdb_scripts")
if Linkmode == LinkExternal {
- switch Thearch.Thechar {
- case '0', '6', '7', '9', 'z':
- 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.Attr |= AttrHidden
-
- abbrevsym = Linklookup(Ctxt, ".debug_abbrev", 0)
- abbrevsym.Attr |= AttrHidden
-
- linesym = Linklookup(Ctxt, ".debug_line", 0)
- linesym.Attr |= AttrHidden
-
- framesym = Linklookup(Ctxt, ".debug_frame", 0)
- framesym.Attr |= AttrHidden
+ Addstring(shstrtab, elfRelType+".debug_info")
+ Addstring(shstrtab, elfRelType+".debug_aranges")
+ Addstring(shstrtab, elfRelType+".debug_line")
+ Addstring(shstrtab, elfRelType+".debug_frame")
+ Addstring(shstrtab, elfRelType+".debug_pubnames")
+ Addstring(shstrtab, elfRelType+".debug_pubtypes")
}
}
-// Add section symbols for DWARF debug info. This is called before
+// 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 '0', '6', '7', '9', 'z':
- 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
+ return
}
+ sym := Linklookup(Ctxt, ".debug_info", 0)
+ putelfsectionsym(sym, sym.Sect.Elfsect.shnum)
+ sym = Linklookup(Ctxt, ".debug_abbrev", 0)
+ putelfsectionsym(sym, sym.Sect.Elfsect.shnum)
+ sym = Linklookup(Ctxt, ".debug_line", 0)
+ putelfsectionsym(sym, sym.Sect.Elfsect.shnum)
+ sym = Linklookup(Ctxt, ".debug_frame", 0)
+ putelfsectionsym(sym, sym.Sect.Elfsect.shnum)
}
/*
@@ -2470,13 +2067,12 @@ 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)
+ for sect := Segdwarf.Sect; sect != nil; sect = sect.Next {
+ h := newPEDWARFSection(sect.Name, int64(sect.Length))
+ fileoff := sect.Vaddr - Segdwarf.Vaddr + Segdwarf.Fileoff
+ if uint64(h.PointerToRawData) != fileoff {
+ Diag("%s.PointerToRawData = %#x, want %#x", sect.Name, h.PointerToRawData, fileoff)
+ errorexit()
+ }
+ }
}
diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go
index ffb7c4bdde..15b8d7af93 100644
--- a/src/cmd/link/internal/ld/elf.go
+++ b/src/cmd/link/internal/ld/elf.go
@@ -6,6 +6,7 @@ package ld
import (
"cmd/internal/obj"
+ "cmd/internal/sys"
"crypto/sha1"
"encoding/binary"
"encoding/hex"
@@ -866,25 +867,23 @@ var buildinfo []byte
func Elfinit() {
Iself = true
- switch Thearch.Thechar {
- case '0', '6', '7', '9', 'z':
+ if SysArch.InFamily(sys.AMD64, sys.ARM64, sys.MIPS64, sys.PPC64, sys.S390X) {
elfRelType = ".rela"
- default:
+ } else {
elfRelType = ".rel"
}
- switch Thearch.Thechar {
+ switch SysArch.Family {
// 64-bit architectures
- case '9', 'z':
+ case sys.PPC64, sys.S390X:
if Ctxt.Arch.ByteOrder == binary.BigEndian {
ehdr.flags = 1 /* Version 1 ABI */
} else {
ehdr.flags = 2 /* Version 2 ABI */
}
fallthrough
-
- case '0', '6', '7':
- if Thearch.Thechar == '0' {
+ case sys.AMD64, sys.ARM64, sys.MIPS64:
+ if SysArch.Family == sys.MIPS64 {
ehdr.flags = 0x20000000 /* MIPS 3 */
}
elf64 = true
@@ -897,7 +896,7 @@ func Elfinit() {
// we use EABI on both linux/arm and freebsd/arm.
// 32-bit architectures
- case '5':
+ case sys.ARM:
// we use EABI on both linux/arm and freebsd/arm.
if HEADTYPE == obj.Hlinux || HEADTYPE == obj.Hfreebsd {
// We set a value here that makes no indication of which
@@ -911,7 +910,6 @@ func Elfinit() {
ehdr.flags = 0x5000002 // has entry point, Version5 EABI
}
fallthrough
-
default:
ehdr.phoff = ELF32HDRSIZE
/* Must be be ELF32HDRSIZE: first PHdr must follow ELF header */
@@ -1432,7 +1430,7 @@ func elfdynhash() {
}
// s390x (ELF64) hash table entries are 8 bytes
- if Thearch.Thechar == 'z' {
+ if SysArch.Family == sys.S390X {
Adduint64(Ctxt, s, uint64(nbucket))
Adduint64(Ctxt, s, uint64(nsym))
for i := 0; i < nbucket; i++ {
@@ -1622,6 +1620,9 @@ func elfshbits(sect *Section) *ElfShdr {
sh.flags |= SHF_TLS
sh.type_ = SHT_NOBITS
}
+ if strings.HasPrefix(sect.Name, ".debug") {
+ sh.flags = 0
+ }
if Linkmode != LinkExternal {
sh.addr = sect.Vaddr
@@ -1657,19 +1658,19 @@ func elfshreloc(sect *Section) *ElfShdr {
sh := elfshname(elfRelType + sect.Name)
sh.type_ = uint32(typ)
- sh.entsize = uint64(Thearch.Regsize) * 2
+ sh.entsize = uint64(SysArch.RegSize) * 2
if typ == SHT_RELA {
- sh.entsize += uint64(Thearch.Regsize)
+ sh.entsize += uint64(SysArch.RegSize)
}
sh.link = uint32(elfshname(".symtab").shnum)
sh.info = uint32(sect.Elfsect.shnum)
sh.off = sect.Reloff
sh.size = sect.Rellen
- sh.addralign = uint64(Thearch.Regsize)
+ sh.addralign = uint64(SysArch.RegSize)
return sh
}
-func elfrelocsect(sect *Section, first *LSym) {
+func elfrelocsect(sect *Section, syms []*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 {
@@ -1680,20 +1681,18 @@ func elfrelocsect(sect *Section, first *LSym) {
}
sect.Reloff = uint64(Cpos())
- var sym *LSym
- for sym = first; sym != nil; sym = sym.Next {
- if !sym.Attr.Reachable() {
+ for i, s := range syms {
+ if !s.Attr.Reachable() {
continue
}
- if uint64(sym.Value) >= sect.Vaddr {
+ if uint64(s.Value) >= sect.Vaddr {
+ syms = syms[i:]
break
}
}
eaddr := int32(sect.Vaddr + sect.Length)
- var r *Reloc
- var ri int
- for ; sym != nil; sym = sym.Next {
+ for _, sym := range syms {
if !sym.Attr.Reachable() {
continue
}
@@ -1702,8 +1701,8 @@ func elfrelocsect(sect *Section, first *LSym) {
}
Ctxt.Cursym = sym
- for ri = 0; ri < len(sym.R); ri++ {
- r = &sym.R[ri]
+ for ri := 0; ri < len(sym.R); ri++ {
+ r := &sym.R[ri]
if r.Done != 0 {
continue
}
@@ -1711,7 +1710,6 @@ func elfrelocsect(sect *Section, first *LSym) {
Diag("missing xsym in relocation")
continue
}
-
if r.Xsym.ElfsymForReloc() == 0 {
Diag("reloc %d to non-elf symbol %s (outer=%s) %d", r.Type, r.Sym.Name, r.Xsym.Name, r.Sym.Type)
}
@@ -1739,6 +1737,9 @@ func Elfemitreloc() {
for sect := Segdata.Sect; sect != nil; sect = sect.Next {
elfrelocsect(sect, datap)
}
+ for sect := Segdwarf.Sect; sect != nil; sect = sect.Next {
+ elfrelocsect(sect, list2slice(dwarfp))
+ }
}
func addgonote(sectionName string, tag uint32, desc []byte) {
@@ -1866,7 +1867,7 @@ func doelf() {
Addstring(shstrtab, ".interp")
Addstring(shstrtab, ".hash")
Addstring(shstrtab, ".got")
- if Thearch.Thechar == '9' {
+ if SysArch.Family == sys.PPC64 {
Addstring(shstrtab, ".glink")
}
Addstring(shstrtab, ".got.plt")
@@ -1885,10 +1886,9 @@ func doelf() {
s.Type = obj.SELFROSECT
s.Attr |= AttrReachable
- switch Thearch.Thechar {
- case '0', '6', '7', '9', 'z':
+ if elf64 {
s.Size += ELF64SYMSIZE
- default:
+ } else {
s.Size += ELF32SYMSIZE
}
@@ -1914,7 +1914,7 @@ func doelf() {
s.Type = obj.SELFGOT // writable
/* ppc64 glink resolver */
- if Thearch.Thechar == '9' {
+ if SysArch.Family == sys.PPC64 {
s := Linklookup(Ctxt, ".glink", 0)
s.Attr |= AttrReachable
s.Type = obj.SELFRXSECT
@@ -1933,7 +1933,7 @@ func doelf() {
s = Linklookup(Ctxt, ".plt", 0)
s.Attr |= AttrReachable
- if Thearch.Thechar == '9' {
+ if SysArch.Family == sys.PPC64 {
// In the ppc64 ABI, .plt is a data section
// written by the dynamic linker.
s.Type = obj.SELFSECT
@@ -1967,10 +1967,9 @@ func doelf() {
elfwritedynentsym(s, DT_HASH, Linklookup(Ctxt, ".hash", 0))
elfwritedynentsym(s, DT_SYMTAB, Linklookup(Ctxt, ".dynsym", 0))
- switch Thearch.Thechar {
- case '0', '6', '7', '9', 'z':
+ if elf64 {
Elfwritedynent(s, DT_SYMENT, ELF64SYMSIZE)
- default:
+ } else {
Elfwritedynent(s, DT_SYMENT, ELF32SYMSIZE)
}
elfwritedynentsym(s, DT_STRTAB, Linklookup(Ctxt, ".dynstr", 0))
@@ -1989,15 +1988,15 @@ func doelf() {
Elfwritedynent(s, DT_RUNPATH, uint64(Addstring(dynstr, rpath.val)))
}
- if Thearch.Thechar == '9' {
+ if SysArch.Family == sys.PPC64 {
elfwritedynentsym(s, DT_PLTGOT, Linklookup(Ctxt, ".plt", 0))
- } else if Thearch.Thechar == 'z' {
+ } else if SysArch.Family == sys.S390X {
elfwritedynentsym(s, DT_PLTGOT, Linklookup(Ctxt, ".got", 0))
} else {
elfwritedynentsym(s, DT_PLTGOT, Linklookup(Ctxt, ".got.plt", 0))
}
- if Thearch.Thechar == '9' {
+ if SysArch.Family == sys.PPC64 {
Elfwritedynent(s, DT_PPC64_OPT, 0)
}
@@ -2024,7 +2023,7 @@ func doelf() {
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))
+ addgonote(".note.go.pkg-list", ELF_NOTE_GOPKGLIST_TAG, pkglistfornote)
var deplist []string
for _, shlib := range Ctxt.Shlibs {
deplist = append(deplist, filepath.Base(shlib.Path))
@@ -2069,26 +2068,29 @@ func Asmbelfsetup() {
for sect := Segdata.Sect; sect != nil; sect = sect.Next {
elfshalloc(sect)
}
+ for sect := Segdwarf.Sect; sect != nil; sect = sect.Next {
+ elfshalloc(sect)
+ }
}
func Asmbelf(symo int64) {
eh := getElfEhdr()
- switch Thearch.Thechar {
+ switch SysArch.Family {
default:
- Exitf("unknown architecture in asmbelf: %v", Thearch.Thechar)
- case '0':
+ Exitf("unknown architecture in asmbelf: %v", SysArch.Family)
+ case sys.MIPS64:
eh.machine = EM_MIPS
- case '5':
+ case sys.ARM:
eh.machine = EM_ARM
- case '6':
+ case sys.AMD64:
eh.machine = EM_X86_64
- case '7':
+ case sys.ARM64:
eh.machine = EM_AARCH64
- case '8':
+ case sys.I386:
eh.machine = EM_386
- case '9':
+ case sys.PPC64:
eh.machine = EM_PPC64
- case 'z':
+ case sys.S390X:
eh.machine = EM_S390
}
@@ -2244,7 +2246,7 @@ func Asmbelf(symo int64) {
} else {
sh.entsize = ELF32SYMSIZE
}
- sh.addralign = uint64(Thearch.Regsize)
+ sh.addralign = uint64(SysArch.RegSize)
sh.link = uint32(elfshname(".dynstr").shnum)
// sh->info = index of first non-local symbol (number of local symbols)
@@ -2268,7 +2270,7 @@ func Asmbelf(symo int64) {
sh = elfshname(".gnu.version_r")
sh.type_ = SHT_GNU_VERNEED
sh.flags = SHF_ALLOC
- sh.addralign = uint64(Thearch.Regsize)
+ sh.addralign = uint64(SysArch.RegSize)
sh.info = uint32(elfverneed)
sh.link = uint32(elfshname(".dynstr").shnum)
shsym(sh, Linklookup(Ctxt, ".gnu.version_r", 0))
@@ -2279,7 +2281,7 @@ func Asmbelf(symo int64) {
sh.type_ = SHT_RELA
sh.flags = SHF_ALLOC
sh.entsize = ELF64RELASIZE
- sh.addralign = uint64(Thearch.Regsize)
+ sh.addralign = uint64(SysArch.RegSize)
sh.link = uint32(elfshname(".dynsym").shnum)
sh.info = uint32(elfshname(".plt").shnum)
shsym(sh, Linklookup(Ctxt, ".rela.plt", 0))
@@ -2343,15 +2345,15 @@ func Asmbelf(symo int64) {
sh := elfshname(".got")
sh.type_ = SHT_PROGBITS
sh.flags = SHF_ALLOC + SHF_WRITE
- sh.entsize = uint64(Thearch.Regsize)
- sh.addralign = uint64(Thearch.Regsize)
+ sh.entsize = uint64(SysArch.RegSize)
+ sh.addralign = uint64(SysArch.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)
+ sh.entsize = uint64(SysArch.RegSize)
+ sh.addralign = uint64(SysArch.RegSize)
shsym(sh, Linklookup(Ctxt, ".got.plt", 0))
}
@@ -2359,7 +2361,7 @@ func Asmbelf(symo int64) {
sh.type_ = SHT_HASH
sh.flags = SHF_ALLOC
sh.entsize = 4
- sh.addralign = uint64(Thearch.Regsize)
+ sh.addralign = uint64(SysArch.RegSize)
sh.link = uint32(elfshname(".dynsym").shnum)
shsym(sh, Linklookup(Ctxt, ".hash", 0))
@@ -2368,8 +2370,8 @@ func Asmbelf(symo int64) {
sh.type_ = SHT_DYNAMIC
sh.flags = SHF_ALLOC + SHF_WRITE
- sh.entsize = 2 * uint64(Thearch.Regsize)
- sh.addralign = uint64(Thearch.Regsize)
+ sh.entsize = 2 * uint64(SysArch.RegSize)
+ sh.addralign = uint64(SysArch.RegSize)
sh.link = uint32(elfshname(".dynstr").shnum)
shsym(sh, Linklookup(Ctxt, ".dynamic", 0))
ph := newElfPhdr()
@@ -2395,7 +2397,7 @@ func Asmbelf(symo int64) {
ph.type_ = PT_TLS
ph.flags = PF_R
ph.memsz = tlssize
- ph.align = uint64(Thearch.Regsize)
+ ph.align = uint64(SysArch.RegSize)
}
}
}
@@ -2404,12 +2406,12 @@ func Asmbelf(symo int64) {
ph := newElfPhdr()
ph.type_ = PT_GNU_STACK
ph.flags = PF_W + PF_R
- ph.align = uint64(Thearch.Regsize)
+ ph.align = uint64(SysArch.RegSize)
ph = newElfPhdr()
ph.type_ = PT_PAX_FLAGS
ph.flags = 0x2a00 // mprotect, randexec, emutramp disabled
- ph.align = uint64(Thearch.Regsize)
+ ph.align = uint64(SysArch.RegSize)
}
elfobj:
@@ -2434,6 +2436,9 @@ elfobj:
for sect := Segdata.Sect; sect != nil; sect = sect.Next {
elfshbits(sect)
}
+ for sect := Segdwarf.Sect; sect != nil; sect = sect.Next {
+ elfshbits(sect)
+ }
if Linkmode == LinkExternal {
for sect := Segtext.Sect; sect != nil; sect = sect.Next {
@@ -2445,7 +2450,14 @@ elfobj:
for sect := Segdata.Sect; sect != nil; sect = sect.Next {
elfshreloc(sect)
}
-
+ for s := dwarfp; s != nil; s = s.Next {
+ if len(s.R) > 0 || s.Type == obj.SDWARFINFO {
+ elfshreloc(s.Sect)
+ }
+ if s.Type == obj.SDWARFINFO {
+ break
+ }
+ }
// add a .note.GNU-stack section to mark the stack as non-executable
sh := elfshname(".note.GNU-stack")
@@ -2459,8 +2471,8 @@ elfobj:
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.addralign = uint64(SysArch.RegSize)
+ sh.entsize = 8 + 2*uint64(SysArch.RegSize)
sh.link = uint32(elfshname(".strtab").shnum)
sh.info = uint32(elfglobalsymndx)
@@ -2469,8 +2481,6 @@ elfobj:
sh.off = uint64(symo) + uint64(Symsize)
sh.size = uint64(len(Elfstrdat))
sh.addralign = 1
-
- dwarfaddelfheaders()
}
/* Main header */
@@ -2585,7 +2595,7 @@ func Elfadddynsym(ctxt *Link, s *LSym) {
/* size of object */
Adduint64(ctxt, d, uint64(s.Size))
- if Thearch.Thechar == '6' && !s.Attr.CgoExportDynamic() && s.Dynimplib != "" && !seenlib[s.Dynimplib] {
+ if SysArch.Family == sys.AMD64 && !s.Attr.CgoExportDynamic() && s.Dynimplib != "" && !seenlib[s.Dynimplib] {
Elfwritedynent(Linklookup(ctxt, ".dynamic", 0), DT_NEEDED, uint64(Addstring(Linklookup(ctxt, ".dynstr", 0), s.Dynimplib)))
}
} else {
@@ -2613,9 +2623,9 @@ func Elfadddynsym(ctxt *Link, s *LSym) {
t := STB_GLOBAL << 4
// TODO(mwhudson): presumably the behaviour should actually be the same on both arm and 386.
- if Thearch.Thechar == '8' && s.Attr.CgoExport() && s.Type&obj.SMASK == obj.STEXT {
+ if SysArch.Family == sys.I386 && s.Attr.CgoExport() && s.Type&obj.SMASK == obj.STEXT {
t |= STT_FUNC
- } else if Thearch.Thechar == '5' && s.Attr.CgoExportDynamic() && s.Type&obj.SMASK == obj.STEXT {
+ } else if SysArch.Family == sys.ARM && s.Attr.CgoExportDynamic() && s.Type&obj.SMASK == obj.STEXT {
t |= STT_FUNC
} else {
t |= STT_OBJECT
diff --git a/src/cmd/link/internal/ld/go.go b/src/cmd/link/internal/ld/go.go
index 027e05d845..425c75571f 100644
--- a/src/cmd/link/internal/ld/go.go
+++ b/src/cmd/link/internal/ld/go.go
@@ -8,8 +8,10 @@ package ld
import (
"bytes"
+ "cmd/internal/bio"
"cmd/internal/obj"
"fmt"
+ "io"
"os"
"strings"
)
@@ -26,7 +28,7 @@ func expandpkg(t0 string, pkg string) string {
// once the dust settles, try to move some code to
// libmach, so that other linkers and ar can share.
-func ldpkg(f *obj.Biobuf, pkg string, length int64, filename string, whence int) {
+func ldpkg(f *bio.Reader, pkg string, length int64, filename string, whence int) {
var p0, p1 int
if Debug['g'] != 0 {
@@ -48,7 +50,7 @@ func ldpkg(f *obj.Biobuf, pkg string, length int64, filename string, whence int)
}
bdata := make([]byte, length)
- if int64(obj.Bread(f, bdata)) != length {
+ if _, err := io.ReadFull(f, bdata); err != nil {
fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename)
if Debug['u'] != 0 {
errorexit()
@@ -418,35 +420,7 @@ type Pkg struct {
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)
-}
+var pkgall []*Pkg
func (p *Pkg) cycle() *Pkg {
if p.checked {
diff --git a/src/cmd/link/internal/ld/ldelf.go b/src/cmd/link/internal/ld/ldelf.go
index 0255331ac6..af60a5c85b 100644
--- a/src/cmd/link/internal/ld/ldelf.go
+++ b/src/cmd/link/internal/ld/ldelf.go
@@ -2,7 +2,9 @@ package ld
import (
"bytes"
+ "cmd/internal/bio"
"cmd/internal/obj"
+ "cmd/internal/sys"
"encoding/binary"
"fmt"
"io"
@@ -266,7 +268,7 @@ type ElfSect struct {
}
type ElfObj struct {
- f *obj.Biobuf
+ f *bio.Reader
base int64 // offset in f where ELF begins
length int64 // length of ELF
is64 int
@@ -403,7 +405,7 @@ func parseArmAttributes(e binary.ByteOrder, data []byte) {
ehdr.flags = 0x5000202
}
if data[0] != 'A' {
- fmt.Fprintf(&Bso, ".ARM.attributes has unexpected format %c\n", data[0])
+ fmt.Fprintf(Bso, ".ARM.attributes has unexpected format %c\n", data[0])
return
}
data = data[1:]
@@ -414,7 +416,7 @@ func parseArmAttributes(e binary.ByteOrder, data []byte) {
nulIndex := bytes.IndexByte(sectiondata, 0)
if nulIndex < 0 {
- fmt.Fprintf(&Bso, "corrupt .ARM.attributes (section name not NUL-terminated)\n")
+ fmt.Fprintf(Bso, "corrupt .ARM.attributes (section name not NUL-terminated)\n")
return
}
name := string(sectiondata[:nulIndex])
@@ -438,20 +440,20 @@ func parseArmAttributes(e binary.ByteOrder, data []byte) {
}
}
if attrList.err != nil {
- fmt.Fprintf(&Bso, "could not parse .ARM.attributes\n")
+ fmt.Fprintf(Bso, "could not parse .ARM.attributes\n")
}
}
}
}
}
-func ldelf(f *obj.Biobuf, pkg string, length int64, pn string) {
+func ldelf(f *bio.Reader, pkg string, length int64, pn string) {
if Debug['v'] != 0 {
- fmt.Fprintf(&Bso, "%5.2f ldelf %s\n", obj.Cputime(), pn)
+ fmt.Fprintf(Bso, "%5.2f ldelf %s\n", obj.Cputime(), pn)
}
Ctxt.IncVersion()
- base := int32(obj.Boffset(f))
+ base := f.Offset()
var add uint64
var e binary.ByteOrder
@@ -474,7 +476,7 @@ func ldelf(f *obj.Biobuf, pkg string, length int64, pn string) {
var sect *ElfSect
var sym ElfSym
var symbols []*LSym
- if obj.Bread(f, hdrbuf[:]) != len(hdrbuf) {
+ if _, err := io.ReadFull(f, hdrbuf[:]); err != nil {
goto bad
}
hdr = new(ElfHdrBytes)
@@ -498,7 +500,7 @@ func ldelf(f *obj.Biobuf, pkg string, length int64, pn string) {
elfobj.e = e
elfobj.f = f
- elfobj.base = int64(base)
+ elfobj.base = base
elfobj.length = length
elfobj.name = pn
@@ -546,47 +548,48 @@ func ldelf(f *obj.Biobuf, pkg string, length int64, pn string) {
return
}
- switch Thearch.Thechar {
+ switch SysArch.Family {
default:
- Diag("%s: elf %s unimplemented", pn, Thestring)
+ Diag("%s: elf %s unimplemented", pn, SysArch.Name)
return
- case '0':
+ case sys.MIPS64:
if elfobj.machine != ElfMachMips || hdr.Ident[4] != ElfClass64 {
Diag("%s: elf object but not mips64", pn)
return
}
- case '5':
+ case sys.ARM:
if e != binary.LittleEndian || elfobj.machine != ElfMachArm || hdr.Ident[4] != ElfClass32 {
Diag("%s: elf object but not arm", pn)
return
}
- case '6':
+ case sys.AMD64:
if e != binary.LittleEndian || elfobj.machine != ElfMachAmd64 || hdr.Ident[4] != ElfClass64 {
Diag("%s: elf object but not amd64", pn)
return
}
- case '7':
+ case sys.ARM64:
if e != binary.LittleEndian || elfobj.machine != ElfMachArm64 || hdr.Ident[4] != ElfClass64 {
Diag("%s: elf object but not arm64", pn)
return
}
- case '8':
+ case sys.I386:
if e != binary.LittleEndian || elfobj.machine != ElfMach386 || hdr.Ident[4] != ElfClass32 {
Diag("%s: elf object but not 386", pn)
return
}
- case '9':
+ case sys.PPC64:
if elfobj.machine != ElfMachPower64 || hdr.Ident[4] != ElfClass64 {
Diag("%s: elf object but not ppc64", pn)
return
}
- case 'z':
+
+ case sys.S390X:
if elfobj.machine != ElfMachS390 || hdr.Ident[4] != ElfClass64 {
Diag("%s: elf object but not s390x", pn)
return
@@ -598,7 +601,7 @@ func ldelf(f *obj.Biobuf, pkg string, length int64, pn string) {
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 {
+ if f.Seek(int64(uint64(base)+elfobj.shoff+uint64(int64(i)*int64(elfobj.shentsize))), 0) < 0 {
goto bad
}
sect = &elfobj.sect[i]
@@ -609,7 +612,7 @@ func ldelf(f *obj.Biobuf, pkg string, length int64, pn string) {
goto bad
}
- sect.nameoff = uint32(e.Uint32(b.Name[:]))
+ sect.nameoff = e.Uint32(b.Name[:])
sect.type_ = e.Uint32(b.Type[:])
sect.flags = e.Uint64(b.Flags[:])
sect.addr = e.Uint64(b.Addr[:])
@@ -626,7 +629,7 @@ func ldelf(f *obj.Biobuf, pkg string, length int64, pn string) {
goto bad
}
- sect.nameoff = uint32(e.Uint32(b.Name[:]))
+ sect.nameoff = e.Uint32(b.Name[:])
sect.type_ = e.Uint32(b.Type[:])
sect.flags = uint64(e.Uint32(b.Flags[:]))
sect.addr = uint64(e.Uint32(b.Addr[:]))
@@ -771,7 +774,7 @@ func ldelf(f *obj.Biobuf, pkg string, length int64, pn string) {
if sym.sym == nil {
continue
}
- sect = &elfobj.sect[sym.shndx:][0]
+ sect = &elfobj.sect[sym.shndx]
if sect.sym == nil {
if strings.HasPrefix(sym.name, ".Linfo_string") { // clang does this
continue
@@ -839,19 +842,13 @@ func ldelf(f *obj.Biobuf, pkg string, length int64, pn string) {
log.Fatalf("symbol %s listed multiple times", s.Name)
}
s.Attr |= AttrOnList
- if Ctxt.Etextp != nil {
- Ctxt.Etextp.Next = s
- } else {
- Ctxt.Textp = s
- }
- Ctxt.Etextp = s
+ Ctxt.Textp = append(Ctxt.Textp, s)
for s = s.Sub; s != nil; s = s.Sub {
if s.Attr.OnList() {
log.Fatalf("symbol %s listed multiple times", s.Name)
}
s.Attr |= AttrOnList
- Ctxt.Etextp.Next = s
- Ctxt.Etextp = s
+ Ctxt.Textp = append(Ctxt.Textp, s)
}
}
}
@@ -925,7 +922,8 @@ func ldelf(f *obj.Biobuf, pkg string, length int64, pn string) {
rp.Sym = sym.sym
}
- rp.Type = int32(reltype(pn, int(uint32(info)), &rp.Siz))
+ rp.Type = 256 + int32(info)
+ rp.Siz = relSize(pn, uint32(info))
if rela != 0 {
rp.Add = int64(add)
} else {
@@ -982,9 +980,11 @@ func elfmap(elfobj *ElfObj, sect *ElfSect) (err error) {
}
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
+ if elfobj.f.Seek(int64(uint64(elfobj.base)+sect.off), 0) < 0 {
+ return fmt.Errorf("short read: seek not successful")
+ }
+ if _, err := io.ReadFull(elfobj.f, sect.base); err != nil {
+ return fmt.Errorf("short read: %v", err)
}
return nil
@@ -1056,7 +1056,7 @@ func readelfsym(elfobj *ElfObj, i int, sym *ElfSym, needSym int) (err error) {
}
case ElfSymBindLocal:
- if Thearch.Thechar == '5' && (strings.HasPrefix(sym.name, "$a") || strings.HasPrefix(sym.name, "$d")) {
+ if SysArch.Family == sys.ARM && (strings.HasPrefix(sym.name, "$a") || strings.HasPrefix(sym.name, "$d")) {
// binutils for arm generate these mapping
// symbols, ignore these
break
@@ -1126,79 +1126,89 @@ func (x rbyoff) Less(i, j int) bool {
return false
}
-func reltype(pn string, elftype int, siz *uint8) int {
- switch uint32(Thearch.Thechar) | uint32(elftype)<<24 {
+func relSize(pn string, elftype uint32) uint8 {
+ // TODO(mdempsky): Replace this with a struct-valued switch statement
+ // once golang.org/issue/15164 is fixed or found to not impair cmd/link
+ // performance.
+
+ const (
+ AMD64 = uint32(sys.AMD64)
+ ARM = uint32(sys.ARM)
+ I386 = uint32(sys.I386)
+ PPC64 = uint32(sys.PPC64)
+ S390X = uint32(sys.S390X)
+ )
+
+ switch uint32(SysArch.Family) | elftype<<24 {
default:
Diag("%s: unknown relocation type %d; compiled without -fpic?", pn, elftype)
fallthrough
- case 'z' | R_390_8:
- *siz = 1
+ case S390X | R_390_8<<24:
+ return 1
- 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,
- 'z' | R_390_16<<24,
- 'z' | R_390_GOT16<<24,
- 'z' | R_390_PC16<<24,
- 'z' | R_390_PC16DBL<<24,
- 'z' | R_390_PLT16DBL<<24:
- *siz = 2
+ case PPC64 | R_PPC64_TOC16<<24,
+ PPC64 | R_PPC64_TOC16_LO<<24,
+ PPC64 | R_PPC64_TOC16_HI<<24,
+ PPC64 | R_PPC64_TOC16_HA<<24,
+ PPC64 | R_PPC64_TOC16_DS<<24,
+ PPC64 | R_PPC64_TOC16_LO_DS<<24,
+ PPC64 | R_PPC64_REL16_LO<<24,
+ PPC64 | R_PPC64_REL16_HI<<24,
+ PPC64 | R_PPC64_REL16_HA<<24,
+ S390X | R_390_16<<24,
+ S390X | R_390_GOT16<<24,
+ S390X | R_390_PC16<<24,
+ S390X | R_390_PC16DBL<<24,
+ S390X | R_390_PLT16DBL<<24:
+ return 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,
- '6' | R_X86_64_GOTPCRELX<<24,
- '6' | R_X86_64_REX_GOTPCRELX<<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,
- '8' | R_386_GOT32X<<24,
- '9' | R_PPC64_REL24<<24,
- '9' | R_PPC_REL32<<24,
- 'z' | R_390_32<<24,
- 'z' | R_390_PC32<<24,
- 'z' | R_390_GOT32<<24,
- 'z' | R_390_PLT32<<24,
- 'z' | R_390_PC32DBL<<24,
- 'z' | R_390_PLT32DBL<<24,
- 'z' | R_390_GOTPCDBL<<24,
- 'z' | R_390_GOTENT<<24:
- *siz = 4
+ case ARM | R_ARM_ABS32<<24,
+ ARM | R_ARM_GOT32<<24,
+ ARM | R_ARM_PLT32<<24,
+ ARM | R_ARM_GOTOFF<<24,
+ ARM | R_ARM_GOTPC<<24,
+ ARM | R_ARM_THM_PC22<<24,
+ ARM | R_ARM_REL32<<24,
+ ARM | R_ARM_CALL<<24,
+ ARM | R_ARM_V4BX<<24,
+ ARM | R_ARM_GOT_PREL<<24,
+ ARM | R_ARM_PC24<<24,
+ ARM | R_ARM_JUMP24<<24,
+ AMD64 | R_X86_64_PC32<<24,
+ AMD64 | R_X86_64_PLT32<<24,
+ AMD64 | R_X86_64_GOTPCREL<<24,
+ AMD64 | R_X86_64_GOTPCRELX<<24,
+ AMD64 | R_X86_64_REX_GOTPCRELX<<24,
+ I386 | R_386_32<<24,
+ I386 | R_386_PC32<<24,
+ I386 | R_386_GOT32<<24,
+ I386 | R_386_PLT32<<24,
+ I386 | R_386_GOTOFF<<24,
+ I386 | R_386_GOTPC<<24,
+ I386 | R_386_GOT32X<<24,
+ PPC64 | R_PPC64_REL24<<24,
+ PPC64 | R_PPC_REL32<<24,
+ S390X | R_390_32<<24,
+ S390X | R_390_PC32<<24,
+ S390X | R_390_GOT32<<24,
+ S390X | R_390_PLT32<<24,
+ S390X | R_390_PC32DBL<<24,
+ S390X | R_390_PLT32DBL<<24,
+ S390X | R_390_GOTPCDBL<<24,
+ S390X | R_390_GOTENT<<24:
+ return 4
- case '6' | R_X86_64_64<<24,
- '9' | R_PPC64_ADDR64<<24,
- 'z' | R_390_GLOB_DAT<<24,
- 'z' | R_390_RELATIVE<<24,
- 'z' | R_390_GOTOFF<<24,
- 'z' | R_390_GOTPC<<24,
- 'z' | R_390_64<<24,
- 'z' | R_390_PC64<<24,
- 'z' | R_390_GOT64<<24,
- 'z' | R_390_PLT64<<24:
- *siz = 8
+ case AMD64 | R_X86_64_64<<24,
+ PPC64 | R_PPC64_ADDR64<<24,
+ S390X | R_390_GLOB_DAT<<24,
+ S390X | R_390_RELATIVE<<24,
+ S390X | R_390_GOTOFF<<24,
+ S390X | R_390_GOTPC<<24,
+ S390X | R_390_64<<24,
+ S390X | R_390_PC64<<24,
+ S390X | R_390_GOT64<<24,
+ S390X | R_390_PLT64<<24:
+ return 8
}
-
- return 256 + elftype
}
diff --git a/src/cmd/link/internal/ld/ldmacho.go b/src/cmd/link/internal/ld/ldmacho.go
index c4c13f13b9..a10124907c 100644
--- a/src/cmd/link/internal/ld/ldmacho.go
+++ b/src/cmd/link/internal/ld/ldmacho.go
@@ -1,9 +1,12 @@
package ld
import (
+ "cmd/internal/bio"
"cmd/internal/obj"
+ "cmd/internal/sys"
"encoding/binary"
"fmt"
+ "io"
"log"
"sort"
)
@@ -41,7 +44,7 @@ const (
)
type LdMachoObj struct {
- f *obj.Biobuf
+ f *bio.Reader
base int64 // off in f where Mach-O begins
length int64 // length of Mach-O
is64 bool
@@ -297,7 +300,10 @@ func macholoadrel(m *LdMachoObj, sect *LdMachoSect) int {
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 {
+ if m.f.Seek(m.base+int64(sect.reloff), 0) < 0 {
+ return -1
+ }
+ if _, err := io.ReadFull(m.f, buf); err != nil {
return -1
}
var p []byte
@@ -343,7 +349,10 @@ 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) {
+ if m.f.Seek(m.base+int64(d.indirectsymoff), 0) < 0 {
+ return -1
+ }
+ if _, err := io.ReadFull(m.f, p); err != nil {
return -1
}
@@ -360,7 +369,10 @@ func macholoadsym(m *LdMachoObj, symtab *LdMachoSymtab) int {
}
strbuf := make([]byte, symtab.strsize)
- if obj.Bseek(m.f, m.base+int64(symtab.stroff), 0) < 0 || obj.Bread(m.f, strbuf) != len(strbuf) {
+ if m.f.Seek(m.base+int64(symtab.stroff), 0) < 0 {
+ return -1
+ }
+ if _, err := io.ReadFull(m.f, strbuf); err != nil {
return -1
}
@@ -370,7 +382,10 @@ func macholoadsym(m *LdMachoObj, symtab *LdMachoSymtab) int {
}
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) {
+ if m.f.Seek(m.base+int64(symtab.symoff), 0) < 0 {
+ return -1
+ }
+ if _, err := io.ReadFull(m.f, symbuf); err != nil {
return -1
}
sym := make([]LdMachoSym, symtab.nsym)
@@ -384,8 +399,8 @@ func macholoadsym(m *LdMachoObj, symtab *LdMachoSymtab) int {
return -1
}
s.name = cstring(strbuf[v:])
- s.type_ = uint8(p[4])
- s.sectnum = uint8(p[5])
+ s.type_ = p[4]
+ s.sectnum = p[5]
s.desc = m.e.Uint16(p[6:])
if m.is64 {
s.value = m.e.Uint64(p[8:])
@@ -400,7 +415,7 @@ func macholoadsym(m *LdMachoObj, symtab *LdMachoSymtab) int {
return 0
}
-func ldmacho(f *obj.Biobuf, pkg string, length int64, pn string) {
+func ldmacho(f *bio.Reader, pkg string, length int64, pn string) {
var err error
var j int
var is64 bool
@@ -430,8 +445,8 @@ func ldmacho(f *obj.Biobuf, pkg string, length int64, pn string) {
var name string
Ctxt.IncVersion()
- base := obj.Boffset(f)
- if obj.Bread(f, hdr[:]) != len(hdr) {
+ base := f.Offset()
+ if _, err := io.ReadFull(f, hdr[:]); err != nil {
goto bad
}
@@ -445,44 +460,43 @@ func ldmacho(f *obj.Biobuf, pkg string, length int64, pn string) {
}
is64 = e.Uint32(hdr[:]) == 0xFEEDFACF
- ncmd = e.Uint32([]byte(hdr[4*4:]))
- cmdsz = e.Uint32([]byte(hdr[5*4:]))
+ ncmd = e.Uint32(hdr[4*4:])
+ cmdsz = e.Uint32(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
+ f.Seek(4, 1) // 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.cputype = uint(e.Uint32(hdr[1*4:]))
+ m.subcputype = uint(e.Uint32(hdr[2*4:]))
+ m.filetype = e.Uint32(hdr[3*4:])
m.ncmd = uint(ncmd)
- m.flags = e.Uint32([]byte(hdr[6*4:]))
+ m.flags = e.Uint32(hdr[6*4:])
m.is64 = is64
m.base = base
m.length = length
m.name = pn
- switch Thearch.Thechar {
+ switch SysArch.Family {
default:
- Diag("%s: mach-o %s unimplemented", pn, Thestring)
+ Diag("%s: mach-o %s unimplemented", pn, SysArch.Name)
return
- case '6':
+ case sys.AMD64:
if e != binary.LittleEndian || m.cputype != LdMachoCpuAmd64 {
Diag("%s: mach-o object but not amd64", pn)
return
}
- case '8':
+ case sys.I386:
if e != binary.LittleEndian || m.cputype != LdMachoCpu386 {
Diag("%s: mach-o object but not 386", pn)
return
@@ -492,7 +506,7 @@ func ldmacho(f *obj.Biobuf, pkg string, length int64, pn string) {
m.cmd = make([]LdMachoCmd, ncmd)
off = uint32(len(hdr))
cmdp = make([]byte, cmdsz)
- if obj.Bread(f, cmdp) != len(cmdp) {
+ if _, err2 := io.ReadFull(f, cmdp); err2 != nil {
err = fmt.Errorf("reading cmds: %v", err)
goto bad
}
@@ -555,7 +569,11 @@ func ldmacho(f *obj.Biobuf, pkg string, length int64, pn string) {
}
dat = make([]byte, c.seg.filesz)
- if obj.Bseek(f, m.base+int64(c.seg.fileoff), 0) < 0 || obj.Bread(f, dat) != len(dat) {
+ if f.Seek(m.base+int64(c.seg.fileoff), 0) < 0 {
+ err = fmt.Errorf("cannot load object data: %v", err)
+ goto bad
+ }
+ if _, err2 := io.ReadFull(f, dat); err2 != nil {
err = fmt.Errorf("cannot load object data: %v", err)
goto bad
}
@@ -689,19 +707,13 @@ func ldmacho(f *obj.Biobuf, pkg string, length int64, pn string) {
log.Fatalf("symbol %s listed multiple times", s.Name)
}
s.Attr |= AttrOnList
- if Ctxt.Etextp != nil {
- Ctxt.Etextp.Next = s
- } else {
- Ctxt.Textp = s
- }
- Ctxt.Etextp = s
+ Ctxt.Textp = append(Ctxt.Textp, s)
for s1 = s.Sub; s1 != nil; s1 = s1.Sub {
if s1.Attr.OnList() {
log.Fatalf("symbol %s listed multiple times", s1.Name)
}
s1.Attr |= AttrOnList
- Ctxt.Etextp.Next = s1
- Ctxt.Etextp = s1
+ Ctxt.Textp = append(Ctxt.Textp, s1)
}
}
}
@@ -724,10 +736,9 @@ func ldmacho(f *obj.Biobuf, pkg string, length int64, pn string) {
rp = &r[rpi]
rel = &sect.rel[j]
if rel.scattered != 0 {
- if Thearch.Thechar != '8' {
+ if SysArch.Family != sys.I386 {
// mach-o only uses scattered relocation on 32-bit platforms
Diag("unexpected scattered relocation")
-
continue
}
@@ -821,7 +832,7 @@ func ldmacho(f *obj.Biobuf, pkg string, length int64, pn string) {
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 {
+ if SysArch.Family == sys.AMD64 && 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
@@ -847,7 +858,7 @@ func ldmacho(f *obj.Biobuf, pkg string, length int64, pn string) {
// 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' {
+ if rel.pcrel != 0 && SysArch.Family == sys.I386 {
rp.Add += int64(rp.Off) + int64(rp.Siz)
}
if rel.extrn == 0 {
@@ -866,7 +877,7 @@ func ldmacho(f *obj.Biobuf, pkg string, length int64, pn string) {
// include that information in the addend.
// We only care about the delta from the
// section base.
- if Thearch.Thechar == '8' {
+ if SysArch.Family == sys.I386 {
rp.Add -= int64(c.seg.sect[rel.symnum-1].addr)
}
} else {
diff --git a/src/cmd/link/internal/ld/ldpe.go b/src/cmd/link/internal/ld/ldpe.go
index 5c3e99c44f..7eb26bcbe8 100644
--- a/src/cmd/link/internal/ld/ldpe.go
+++ b/src/cmd/link/internal/ld/ldpe.go
@@ -5,9 +5,12 @@
package ld
import (
+ "cmd/internal/bio"
"cmd/internal/obj"
+ "cmd/internal/sys"
"encoding/binary"
"fmt"
+ "io"
"log"
"sort"
"strconv"
@@ -116,7 +119,7 @@ type PeSect struct {
}
type PeObj struct {
- f *obj.Biobuf
+ f *bio.Reader
name string
base uint32
sect []PeSect
@@ -127,14 +130,14 @@ type PeObj struct {
snames []byte
}
-func ldpe(f *obj.Biobuf, pkg string, length int64, pn string) {
+func ldpe(f *bio.Reader, pkg string, length int64, pn string) {
if Debug['v'] != 0 {
- fmt.Fprintf(&Bso, "%5.2f ldpe %s\n", obj.Cputime(), pn)
+ fmt.Fprintf(Bso, "%5.2f ldpe %s\n", obj.Cputime(), pn)
}
var sect *PeSect
Ctxt.IncVersion()
- base := int32(obj.Boffset(f))
+ base := f.Offset()
peobj := new(PeObj)
peobj.f = f
@@ -172,15 +175,15 @@ func ldpe(f *obj.Biobuf, pkg string, length int64, pn string) {
// 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)
+ f.Seek(base+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0)
- if obj.Bread(f, symbuf[:4]) != 4 {
+ if _, err := io.ReadFull(f, symbuf[:4]); err != nil {
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) {
+ f.Seek(base+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0)
+ if _, err := io.ReadFull(f, peobj.snames); err != nil {
goto bad
}
@@ -200,10 +203,10 @@ func ldpe(f *obj.Biobuf, pkg string, length int64, pn string) {
peobj.pesym = make([]PeSym, peobj.fh.NumberOfSymbols)
peobj.npesym = uint(peobj.fh.NumberOfSymbols)
- obj.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable), 0)
+ f.Seek(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) {
+ f.Seek(base+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(i), 0)
+ if _, err := io.ReadFull(f, symbuf[:]); err != nil {
goto bad
}
@@ -288,10 +291,10 @@ func ldpe(f *obj.Biobuf, pkg string, length int64, pn string) {
}
r = make([]Reloc, rsect.sh.NumberOfRelocations)
- obj.Bseek(f, int64(peobj.base)+int64(rsect.sh.PointerToRelocations), 0)
+ f.Seek(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 {
+ if _, err := io.ReadFull(f, symbuf[:10]); err != nil {
goto bad
}
rva := Le32(symbuf[0:])
@@ -432,19 +435,13 @@ func ldpe(f *obj.Biobuf, pkg string, length int64, pn string) {
log.Fatalf("symbol %s listed multiple times", s.Name)
}
s.Attr |= AttrOnList
- if Ctxt.Etextp != nil {
- Ctxt.Etextp.Next = s
- } else {
- Ctxt.Textp = s
- }
- Ctxt.Etextp = s
+ Ctxt.Textp = append(Ctxt.Textp, s)
for s = s.Sub; s != nil; s = s.Sub {
if s.Attr.OnList() {
log.Fatalf("symbol %s listed multiple times", s.Name)
}
s.Attr |= AttrOnList
- Ctxt.Etextp.Next = s
- Ctxt.Etextp = s
+ Ctxt.Textp = append(Ctxt.Textp, s)
}
}
}
@@ -464,7 +461,10 @@ func pemap(peobj *PeObj, sect *PeSect) int {
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) {
+ if peobj.f.Seek(int64(peobj.base)+int64(sect.sh.PointerToRawData), 0) < 0 {
+ return -1
+ }
+ if _, err := io.ReadFull(peobj.f, sect.base); err != nil {
return -1
}
@@ -492,7 +492,7 @@ func readpesym(peobj *PeObj, i int, y **PeSym) (err error) {
if strings.HasPrefix(name, "__imp_") {
name = name[6:] // __imp_Name => Name
}
- if Thearch.Thechar == '8' && name[0] == '_' {
+ if SysArch.Family == sys.I386 && name[0] == '_' {
name = name[1:] // _Name => Name
}
}
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index 5616700445..53428bb1c6 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -33,7 +33,9 @@ package ld
import (
"bufio"
"bytes"
+ "cmd/internal/bio"
"cmd/internal/obj"
+ "cmd/internal/sys"
"crypto/sha1"
"debug/elf"
"encoding/binary"
@@ -82,14 +84,9 @@ import (
// THE SOFTWARE.
type Arch struct {
- Thechar int
- Ptrsize int
- Intsize int
- Regsize int
Funcalign int
Maxalign int
Minalign int
- Minlc int
Dwarfregsp int
Dwarfreglr int
Linuxdynld string
@@ -133,7 +130,6 @@ func (r *Rpath) String() string {
var (
Thearch Arch
- datap *LSym
Debug [128]int
Lcsize int32
rpath Rpath
@@ -191,8 +187,7 @@ func UseRelro() bool {
}
var (
- Thestring string
- Thelinkarch *LinkArch
+ SysArch *sys.Arch
outfile string
dynexp []*LSym
dynlib []string
@@ -201,6 +196,7 @@ var (
Funcalign int
iscgo bool
elfglobalsymndx int
+ flag_dumpdep bool
flag_installsuffix string
flag_race int
flag_msan int
@@ -245,9 +241,10 @@ const (
var (
headstring string
// buffered output
- Bso obj.Biobuf
+ Bso *bufio.Writer
)
+// TODO(dfc) outBuf duplicates bio.Writer
type outBuf struct {
w *bufio.Writer
f *os.File
@@ -472,7 +469,7 @@ func loadinternal(name string) {
if Linkshared {
shlibname := filepath.Join(Ctxt.Libdir[i], name+".shlibname")
if Debug['v'] != 0 {
- fmt.Fprintf(&Bso, "searching for %s.a in %s\n", name, shlibname)
+ fmt.Fprintf(Bso, "searching for %s.a in %s\n", name, shlibname)
}
if _, err := os.Stat(shlibname); err == nil {
addlibpath(Ctxt, "internal", "internal", "", name, shlibname)
@@ -482,7 +479,7 @@ func loadinternal(name string) {
}
pname := filepath.Join(Ctxt.Libdir[i], name+".a")
if Debug['v'] != 0 {
- fmt.Fprintf(&Bso, "searching for %s.a in %s\n", name, pname)
+ fmt.Fprintf(Bso, "searching for %s.a in %s\n", name, pname)
}
if _, err := os.Stat(pname); err == nil {
addlibpath(Ctxt, "internal", "internal", pname, name, "")
@@ -492,7 +489,7 @@ func loadinternal(name string) {
}
if found == 0 {
- fmt.Fprintf(&Bso, "warning: unable to find %s.a\n", name)
+ fmt.Fprintf(Bso, "warning: unable to find %s.a\n", name)
}
}
@@ -509,7 +506,7 @@ func loadlib() {
}
loadinternal("runtime")
- if Thearch.Thechar == '5' {
+ if SysArch.Family == sys.ARM {
loadinternal("math")
}
if flag_race != 0 {
@@ -524,7 +521,7 @@ func loadlib() {
iscgo = iscgo || Ctxt.Library[i].Pkg == "runtime/cgo"
if Ctxt.Library[i].Shlib == "" {
if Debug['v'] > 1 {
- fmt.Fprintf(&Bso, "%5.2f autolib: %s (from %s)\n", obj.Cputime(), Ctxt.Library[i].File, Ctxt.Library[i].Objref)
+ fmt.Fprintf(Bso, "%5.2f autolib: %s (from %s)\n", obj.Cputime(), Ctxt.Library[i].File, Ctxt.Library[i].Objref)
}
objfile(Ctxt.Library[i])
}
@@ -533,7 +530,7 @@ func loadlib() {
for i = 0; i < len(Ctxt.Library); i++ {
if Ctxt.Library[i].Shlib != "" {
if Debug['v'] > 1 {
- fmt.Fprintf(&Bso, "%5.2f autolib: %s (from %s)\n", obj.Cputime(), Ctxt.Library[i].Shlib, Ctxt.Library[i].Objref)
+ fmt.Fprintf(Bso, "%5.2f autolib: %s (from %s)\n", obj.Cputime(), Ctxt.Library[i].Shlib, Ctxt.Library[i].Objref)
}
ldshlibsyms(Ctxt.Library[i].Shlib)
}
@@ -562,7 +559,7 @@ func loadlib() {
// 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 {
+ if SysArch.InFamily(sys.ARM, sys.ARM64) && HEADTYPE == obj.Hdarwin && iscgo {
Linkmode = LinkExternal
}
@@ -621,7 +618,7 @@ func loadlib() {
// a variable to hold g in assembly (currently only intel).
if tlsg.Type == 0 {
tlsg.Type = obj.STLSBSS
- tlsg.Size = int64(Thearch.Ptrsize)
+ tlsg.Size = int64(SysArch.PtrSize)
} else if tlsg.Type != obj.SDYNIMPORT {
Diag("internal error: runtime declared tlsg variable %d", tlsg.Type)
}
@@ -639,7 +636,7 @@ func loadlib() {
// In addition, on ARM, the runtime depends on the linker
// recording the value of GOARM.
- if Thearch.Thechar == '5' {
+ if SysArch.Family == sys.ARM {
s := Linklookup(Ctxt, "runtime.goarm", 0)
s.Type = obj.SRODATA
@@ -696,13 +693,13 @@ func loadlib() {
args := hostlinkArchArgs()
args = append(args, "--print-libgcc-file-name")
if Debug['v'] != 0 {
- fmt.Fprintf(&Bso, "%s %v\n", extld, args)
+ fmt.Fprintf(Bso, "%s %v\n", extld, args)
}
out, err := exec.Command(extld, args...).Output()
if err != nil {
if Debug['v'] != 0 {
- fmt.Fprintln(&Bso, "not using a libgcc file because compiler failed")
- fmt.Fprintf(&Bso, "%v\n%s\n", err, out)
+ fmt.Fprintln(Bso, "not using a libgcc file because compiler failed")
+ fmt.Fprintf(Bso, "%v\n%s\n", err, out)
}
libgccfile = "none"
} else {
@@ -743,17 +740,17 @@ func loadlib() {
* look for the next file in an archive.
* adapted from libmach.
*/
-func nextar(bp *obj.Biobuf, off int64, a *ArHdr) int64 {
+func nextar(bp *bio.Reader, 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
+ bp.Seek(off, 0)
+ var buf [SAR_HDR]byte
+ if n, err := io.ReadFull(bp, buf[:]); err != nil {
+ if n == 0 && err != io.EOF {
+ return -1
}
- return -1
+ return 0
}
a.name = artrim(buf[0:16])
@@ -768,35 +765,38 @@ func nextar(bp *obj.Biobuf, off int64, a *ArHdr) int64 {
if arsize&1 != 0 {
arsize++
}
- return int64(arsize) + SAR_HDR
+ return 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)
+ fmt.Fprintf(Bso, "%5.2f ldobj: %s (%s)\n", obj.Cputime(), lib.File, pkg)
}
Bso.Flush()
- f, err := obj.Bopenr(lib.File)
+ f, err := bio.Open(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) {
+ for i := 0; i < len(ARMAG); i++ {
+ if c, err := f.ReadByte(); err == nil && c == ARMAG[i] {
+ continue
+ }
+
/* load it as a regular file */
- l := obj.Bseek(f, 0, 2)
+ l := f.Seek(0, 2)
- obj.Bseek(f, 0, 0)
+ f.Seek(0, 0)
ldobj(f, pkg, l, lib.File, lib.File, FileObj)
- obj.Bterm(f)
+ f.Close()
return
}
/* process __.PKGDEF */
- off := obj.Boffset(f)
+ off := f.Offset()
var arhdr ArHdr
l := nextar(f, off, &arhdr)
@@ -812,12 +812,14 @@ func objfile(lib *Library) {
}
if Buildmode == BuildmodeShared {
- before := obj.Boffset(f)
+ before := f.Offset()
pkgdefBytes := make([]byte, atolwhex(arhdr.size))
- obj.Bread(f, pkgdefBytes)
+ if _, err := io.ReadFull(f, pkgdefBytes); err != nil {
+ Diag("%s: short read on archive file symbol header: %v", lib.File, err)
+ }
hash := sha1.Sum(pkgdefBytes)
lib.hash = hash[:]
- obj.Bseek(f, before, 0)
+ f.Seek(before, 0)
}
off += l
@@ -853,11 +855,11 @@ func objfile(lib *Library) {
}
out:
- obj.Bterm(f)
+ f.Close()
}
type Hostobj struct {
- ld func(*obj.Biobuf, string, int64, string)
+ ld func(*bio.Reader, string, int64, string)
pkg string
pn string
file string
@@ -878,7 +880,7 @@ var internalpkg = []string{
"runtime/msan",
}
-func ldhostobj(ld func(*obj.Biobuf, string, int64, string), f *obj.Biobuf, pkg string, length int64, pn string, file string) *Hostobj {
+func ldhostobj(ld func(*bio.Reader, string, int64, string), f *bio.Reader, pkg string, length int64, pn string, file string) *Hostobj {
isinternal := false
for i := 0; i < len(internalpkg); i++ {
if pkg == internalpkg[i] {
@@ -909,26 +911,24 @@ func ldhostobj(ld func(*obj.Biobuf, string, int64, string), f *obj.Biobuf, pkg s
h.pkg = pkg
h.pn = pn
h.file = file
- h.off = obj.Boffset(f)
+ h.off = f.Offset()
h.length = length
return h
}
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 {
+ f, err := bio.Open(h.file)
+ if err != nil {
Exitf("cannot reopen %s: %v", h.pn, err)
}
- obj.Bseek(f, h.off, 0)
+ f.Seek(h.off, 0)
h.ld(f, h.pkg, h.length, h.pn)
- obj.Bterm(f)
+ f.Close()
}
}
@@ -1040,7 +1040,7 @@ func archive() {
argv = append(argv, hostobjCopy()...)
if Debug['v'] != 0 {
- fmt.Fprintf(&Bso, "archive: %s\n", strings.Join(argv, " "))
+ fmt.Fprintf(Bso, "archive: %s\n", strings.Join(argv, " "))
Bso.Flush()
}
@@ -1117,6 +1117,23 @@ func hostlink() {
// because lazy PLT resolution can use large amounts of stack at
// times we cannot allow it to do so.
argv = append(argv, "-Wl,-znow")
+
+ // Do not let the host linker generate COPY relocations. These
+ // can move symbols out of sections that rely on stable offsets
+ // from the beginning of the section (like STYPE).
+ argv = append(argv, "-Wl,-znocopyreloc")
+
+ if SysArch.InFamily(sys.ARM, sys.ARM64) {
+ // On ARM, the GNU linker will generate COPY relocations
+ // even with -znocopyreloc set.
+ // https://sourceware.org/bugzilla/show_bug.cgi?id=19962
+ //
+ // On ARM64, the GNU linker will fail instead of
+ // generating COPY relocations.
+ //
+ // In both cases, switch to gold.
+ argv = append(argv, "-fuse-ld=gold")
+ }
}
if Iself && len(buildinfo) > 0 {
@@ -1187,6 +1204,24 @@ func hostlink() {
argv = append(argv, ldflag...)
+ if flag_race != 0 {
+ // On a system where the toolchain creates position independent
+ // executables by default, tsan initialization can fail. So we pass
+ // -no-pie here, but support for that flag is quite new and we test
+ // for its support first.
+ src := filepath.Join(tmpdir, "trivial.c")
+ if err := ioutil.WriteFile(src, []byte{}, 0666); err != nil {
+ Ctxt.Diag("WriteFile trivial.c failed: %v", err)
+ }
+ cmd := exec.Command(argv[0], "-no-pie", "trivial.c")
+ cmd.Dir = tmpdir
+ out, err := cmd.CombinedOutput()
+ supported := err == nil && !bytes.Contains(out, []byte("unrecognized"))
+ if supported {
+ argv = append(argv, "-no-pie")
+ }
+ }
+
for _, p := range strings.Fields(extldflags) {
argv = append(argv, p)
@@ -1209,24 +1244,24 @@ func hostlink() {
}
if Debug['v'] != 0 {
- fmt.Fprintf(&Bso, "host link:")
+ fmt.Fprintf(Bso, "host link:")
for _, v := range argv {
- fmt.Fprintf(&Bso, " %q", v)
+ fmt.Fprintf(Bso, " %q", v)
}
- fmt.Fprintf(&Bso, "\n")
+ 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)
} else if Debug['v'] != 0 && len(out) > 0 {
- fmt.Fprintf(&Bso, "%s", out)
+ fmt.Fprintf(Bso, "%s", out)
Bso.Flush()
}
if Debug['s'] == 0 && debug_s == 0 && HEADTYPE == obj.Hdarwin {
// Skip combining dwarf on arm.
- if Thearch.Thechar != '5' && Thearch.Thechar != '7' {
+ if !SysArch.InFamily(sys.ARM, sys.ARM64) {
dsym := filepath.Join(tmpdir, "go.dwarf")
if out, err := exec.Command("dsymutil", "-f", outfile, "-o", dsym).CombinedOutput(); err != nil {
Ctxt.Cursym = nil
@@ -1254,14 +1289,14 @@ func hostlink() {
// hostlinkArchArgs returns arguments to pass to the external linker
// based on the architecture.
func hostlinkArchArgs() []string {
- switch Thearch.Thechar {
- case '8':
+ switch SysArch.Family {
+ case sys.I386:
return []string{"-m32"}
- case '6', '9', 'z':
+ case sys.AMD64, sys.PPC64, sys.S390X:
return []string{"-m64"}
- case '5':
+ case sys.ARM:
return []string{"-marm"}
- case '7':
+ case sys.ARM64:
// nothing needed
}
return nil
@@ -1270,15 +1305,15 @@ func hostlinkArchArgs() []string {
// ldobj loads an input object. If it is a host object (an object
// compiled by a non-Go compiler) it returns the Hostobj pointer. If
// it is a Go object, it returns nil.
-func ldobj(f *obj.Biobuf, pkg string, length int64, pn string, file string, whence int) *Hostobj {
- eof := obj.Boffset(f) + length
+func ldobj(f *bio.Reader, pkg string, length int64, pn string, file string, whence int) *Hostobj {
+ eof := f.Offset() + 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)
+ start := f.Offset()
+ c1 := bgetc(f)
+ c2 := bgetc(f)
+ c3 := bgetc(f)
+ c4 := bgetc(f)
+ f.Seek(start, 0)
magic := uint32(c1)<<24 | uint32(c2)<<16 | uint32(c3)<<8 | uint32(c4)
if magic == 0x7f454c46 { // \x7F E L F
@@ -1294,22 +1329,19 @@ func ldobj(f *obj.Biobuf, pkg string, length int64, pn string, file string, when
}
/* check the header */
- line := obj.Brdline(f, '\n')
- if line == "" {
- if obj.Blinelen(f) > 0 {
- Diag("%s: not an object file", pn)
- return nil
- }
- Diag("truncated object file: %s", pn)
+ line, err := f.ReadString('\n')
+ if err != nil {
+ Diag("truncated object file: %s: %v", pn, err)
return nil
}
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)
+ Exitf("%s: uncompiled .go source file", pn)
+ return nil
}
- if line == Thestring {
+ if line == SysArch.Name {
// old header format: just $GOOS
Diag("%s: stale object file", pn)
return nil
@@ -1341,28 +1373,28 @@ func ldobj(f *obj.Biobuf, pkg string, length int64, pn string, file string, when
}
/* skip over exports and other info -- ends with \n!\n */
- import0 := obj.Boffset(f)
+ import0 := f.Offset()
c1 = '\n' // the last line ended in \n
- c2 = obj.Bgetc(f)
- c3 = obj.Bgetc(f)
+ c2 = bgetc(f)
+ c3 = bgetc(f)
for c1 != '\n' || c2 != '!' || c3 != '\n' {
c1 = c2
c2 = c3
- c3 = obj.Bgetc(f)
- if c3 == obj.Beof {
+ c3 = bgetc(f)
+ if c3 == -1 {
Diag("truncated object file: %s", pn)
return nil
}
}
- import1 := obj.Boffset(f)
+ import1 := f.Offset()
- obj.Bseek(f, import0, 0)
+ f.Seek(import0, 0)
ldpkg(f, pkg, import1-import0-2, pn, whence) // -2 for !\n
- obj.Bseek(f, import1, 0)
+ f.Seek(import1, 0)
- LoadObjFile(Ctxt, f, pkg, eof-obj.Boffset(f), pn)
+ LoadObjFile(Ctxt, f, pkg, eof-f.Offset(), pn)
return nil
}
@@ -1500,12 +1532,12 @@ func ldshlibsyms(shlib string) {
// the type data.
if strings.HasPrefix(lsym.Name, "type.") && !strings.HasPrefix(lsym.Name, "type..") {
lsym.P = readelfsymboldata(f, &elfsym)
- gcdata_locations[elfsym.Value+2*uint64(Thearch.Ptrsize)+8+1*uint64(Thearch.Ptrsize)] = lsym
+ gcdata_locations[elfsym.Value+2*uint64(SysArch.PtrSize)+8+1*uint64(SysArch.PtrSize)] = lsym
}
}
}
gcdata_addresses := make(map[*LSym]uint64)
- if Thearch.Thechar == '7' {
+ if SysArch.Family == sys.ARM64 {
for _, sect := range f.Sections {
if sect.Type == elf.SHT_RELA {
var rela elf.Rela64
@@ -1532,30 +1564,14 @@ func ldshlibsyms(shlib string) {
// 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
+ // pcln table entries for these any more so remove them from Textp.
+ textp := make([]*LSym, 0, len(Ctxt.Textp))
+ for _, s := range Ctxt.Textp {
+ if s.Type != obj.SDYNIMPORT {
+ textp = append(textp, s)
}
- last = s
- }
-
- if last == nil {
- Ctxt.Textp = nil
- Ctxt.Etextp = nil
- } else {
- last.Next = nil
- Ctxt.Etextp = last
}
+ Ctxt.Textp = textp
Ctxt.Shlibs = append(Ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f, gcdata_addresses: gcdata_addresses})
}
@@ -1564,10 +1580,6 @@ 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.
@@ -1608,7 +1620,7 @@ func addsection(seg *Segment, name string, rwx int) *Section {
sect.Rwx = uint8(rwx)
sect.Name = name
sect.Seg = seg
- sect.Align = int32(Thearch.Ptrsize) // everything is at least pointer-aligned
+ sect.Align = int32(SysArch.PtrSize) // everything is at least pointer-aligned
*l = sect
return sect
}
@@ -1652,7 +1664,7 @@ func callsize() int {
if haslinkregister() {
return 0
}
- return Thearch.Regsize
+ return SysArch.RegSize
}
func dostkcheck() {
@@ -1673,7 +1685,7 @@ func dostkcheck() {
// 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 {
+ for _, s := range Ctxt.Textp {
// 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.
@@ -1688,7 +1700,7 @@ func dostkcheck() {
}
}
- for s := Ctxt.Textp; s != nil; s = s.Next {
+ for _, s := range Ctxt.Textp {
if !s.Attr.NoSplit() {
Ctxt.Cursym = s
ch.sym = s
@@ -1717,7 +1729,7 @@ func stkcheck(up *Chain, depth int) int {
return -1
}
- if s.Attr.External() || s.Pcln == nil {
+ if s.Attr.External() || s.FuncInfo == nil {
// external function.
// should never be called directly.
// only diagnose the direct caller.
@@ -1754,7 +1766,11 @@ func stkcheck(up *Chain, depth int) int {
return 0
}
// Raise limit to allow frame.
- limit = int(obj.StackLimit+s.Locals) + int(Ctxt.FixedFrameSize())
+ locals := int32(0)
+ if s.FuncInfo != nil {
+ locals = s.FuncInfo.Locals
+ }
+ limit = int(obj.StackLimit+locals) + int(Ctxt.FixedFrameSize())
}
// Walk through sp adjustments in function, consuming relocs.
@@ -1764,7 +1780,7 @@ func stkcheck(up *Chain, depth int) int {
var ch1 Chain
var pcsp Pciter
var r *Reloc
- for pciterinit(Ctxt, &pcsp, &s.Pcln.Pcsp); pcsp.done == 0; pciternext(&pcsp) {
+ for pciterinit(Ctxt, &pcsp, &s.FuncInfo.Pcsp); pcsp.done == 0; pciternext(&pcsp) {
// pcsp.value is in effect for [pcsp.pc, pcsp.nextpc).
// Check stack size in effect for this span.
@@ -1956,7 +1972,7 @@ func genasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) {
continue
}
if len(s.P) > 0 {
- Diag("%s should not be bss (size=%d type=%d special=%v)", s.Name, int(len(s.P)), s.Type, s.Attr.Special())
+ Diag("%s should not be bss (size=%d type=%d special=%v)", s.Name, len(s.P), s.Type, s.Attr.Special())
}
put(s, s.Name, 'B', Symaddr(s), s.Size, int(s.Version), s.Gotype)
@@ -1982,13 +1998,20 @@ func genasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) {
}
var off int32
- for s := Ctxt.Textp; s != nil; s = s.Next {
+ for _, s := range Ctxt.Textp {
put(s, s.Name, 'T', s.Value, s.Size, int(s.Version), s.Gotype)
+ locals := int32(0)
+ if s.FuncInfo != nil {
+ locals = s.FuncInfo.Locals
+ }
// 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)
+ put(nil, ".frame", 'm', int64(locals)+int64(SysArch.PtrSize), 0, 0, nil)
- for _, a := range s.Autom {
+ if s.FuncInfo == nil {
+ continue
+ }
+ for _, a := range s.FuncInfo.Autom {
// 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 {
@@ -1999,7 +2022,7 @@ func genasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) {
if a.Name == obj.A_PARAM {
off = a.Aoffset
} else {
- off = a.Aoffset - int32(Thearch.Ptrsize)
+ off = a.Aoffset - int32(SysArch.PtrSize)
}
// FP
@@ -2009,8 +2032,8 @@ func genasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) {
}
// SP
- if off <= int32(-Thearch.Ptrsize) {
- put(nil, a.Asym.Name, 'a', -(int64(off) + int64(Thearch.Ptrsize)), 0, 0, a.Gotype)
+ if off <= int32(-SysArch.PtrSize) {
+ put(nil, a.Asym.Name, 'a', -(int64(off) + int64(SysArch.PtrSize)), 0, 0, a.Gotype)
continue
}
}
@@ -2019,7 +2042,7 @@ func genasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) {
// 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))
+ fmt.Fprintf(Bso, "%5.2f symsize = %d\n", obj.Cputime(), uint32(Symsize))
}
Bso.Flush()
}
@@ -2085,10 +2108,10 @@ func undefsym(s *LSym) {
}
func undef() {
- for s := Ctxt.Textp; s != nil; s = s.Next {
+ for _, s := range Ctxt.Textp {
undefsym(s)
}
- for s := datap; s != nil; s = s.Next {
+ for _, s := range datap {
undefsym(s)
}
if nerrors > 0 {
@@ -2103,14 +2126,14 @@ func callgraph() {
var i int
var r *Reloc
- for s := Ctxt.Textp; s != nil; s = s.Next {
+ for _, s := range Ctxt.Textp {
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.Type == obj.R_CALLMIPS) && r.Sym.Type == obj.STEXT {
- fmt.Fprintf(&Bso, "%s calls %s\n", s.Name, r.Sym.Name)
+ fmt.Fprintf(Bso, "%s calls %s\n", s.Name, r.Sym.Name)
}
}
}
@@ -2145,3 +2168,14 @@ func Rnd(v int64, r int64) int64 {
v -= c
return v
}
+
+func bgetc(r *bio.Reader) int {
+ c, err := r.ReadByte()
+ if err != nil {
+ if err != io.EOF {
+ log.Fatalf("reading input: %v", err)
+ }
+ return -1
+ }
+ return int(c)
+}
diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go
index 67a855933e..d0515d4617 100644
--- a/src/cmd/link/internal/ld/link.go
+++ b/src/cmd/link/internal/ld/link.go
@@ -31,9 +31,9 @@
package ld
import (
- "cmd/internal/obj"
+ "bufio"
+ "cmd/internal/sys"
"debug/elf"
- "encoding/binary"
"fmt"
)
@@ -50,8 +50,6 @@ type LSym struct {
Align int32
Elfsym int32
LocalElfsym int32
- Args int32
- Locals int32
Value int64
Size int64
// ElfType is set for symbols read from shared libraries by ldshlibsyms. It
@@ -67,8 +65,7 @@ type LSym struct {
Dynimplib string
Dynimpvers string
Sect *Section
- Autom []Auto
- Pcln *Pcln
+ FuncInfo *FuncInfo
P []byte
R []Reloc
}
@@ -161,13 +158,11 @@ type Shlib struct {
}
type Link struct {
- Thechar int32
- Thestring string
Goarm int32
Headtype int
- Arch *LinkArch
+ Arch *sys.Arch
Debugvlog int32
- Bso *obj.Biobuf
+ Bso *bufio.Writer
Windows int32
Goroot string
@@ -183,10 +178,8 @@ type Link struct {
Diag func(string, ...interface{})
Cursym *LSym
Version int
- Textp *LSym
- Etextp *LSym
- Nhistfile int32
- Filesyms *LSym
+ Textp []*LSym
+ Filesyms []*LSym
Moduledata *LSym
LSymBatch []LSym
}
@@ -196,15 +189,15 @@ type Link struct {
// on the stack in the function prologue and so always have a pointer between
// the hardware stack pointer and the local variable area.
func (ctxt *Link) FixedFrameSize() int64 {
- switch ctxt.Arch.Thechar {
- case '6', '8':
+ switch ctxt.Arch.Family {
+ case sys.AMD64, sys.I386:
return 0
- case '9':
+ case sys.PPC64:
// PIC code on ppc64le requires 32 bytes of stack, and it's easier to
// just use that much stack always on ppc64x.
- return int64(4 * ctxt.Arch.Ptrsize)
+ return int64(4 * ctxt.Arch.PtrSize)
default:
- return int64(ctxt.Arch.Ptrsize)
+ return int64(ctxt.Arch.PtrSize)
}
}
@@ -213,15 +206,6 @@ func (l *Link) IncVersion() {
l.Hash = append(l.Hash, make(map[string]*LSym))
}
-type LinkArch struct {
- ByteOrder binary.ByteOrder
- Name string
- Thechar int
- Minlc int
- Ptrsize int
- Regsize int
-}
-
type Library struct {
Objref string
Srcref string
@@ -231,7 +215,10 @@ type Library struct {
hash []byte
}
-type Pcln struct {
+type FuncInfo struct {
+ Args int32
+ Locals int32
+ Autom []Auto
Pcsp Pcdata
Pcfile Pcdata
Pcline Pcdata
diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go
index d60203fb91..53cc96275d 100644
--- a/src/cmd/link/internal/ld/macho.go
+++ b/src/cmd/link/internal/ld/macho.go
@@ -6,6 +6,7 @@ package ld
import (
"cmd/internal/obj"
+ "cmd/internal/sys"
"sort"
"strings"
)
@@ -78,6 +79,8 @@ const (
MACHO_X86_64_RELOC_SIGNED_2 = 7
MACHO_X86_64_RELOC_SIGNED_4 = 8
MACHO_ARM_RELOC_VANILLA = 0
+ MACHO_ARM_RELOC_PAIR = 1
+ MACHO_ARM_RELOC_SECTDIFF = 2
MACHO_ARM_RELOC_BR24 = 5
MACHO_ARM64_RELOC_UNSIGNED = 0
MACHO_ARM64_RELOC_BRANCH26 = 2
@@ -131,15 +134,7 @@ var nsortsym int
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
- }
+ macho64 = SysArch.RegSize == 8
}
func getMachoHdr() *MachoHdr {
@@ -356,9 +351,10 @@ func machoshbits(mseg *MachoSeg, sect *Section, segname string) {
buf := "__" + strings.Replace(sect.Name[1:], ".", "_", -1)
var msect *MachoSect
- if sect.Rwx&1 == 0 && (Thearch.Thechar == '7' || // arm64
- (Thearch.Thechar == '6' && (Buildmode == BuildmodeCShared || Buildmode == BuildmodeCArchive))) { // amd64
- // Darwin external linker on arm64 and on amd64 in c-shared/c-archive buildmode
+ if sect.Rwx&1 == 0 && segname != "__DWARF" && (SysArch.Family == sys.ARM64 ||
+ (SysArch.Family == sys.AMD64 && (Buildmode == BuildmodeCShared || Buildmode == BuildmodeCArchive)) ||
+ (SysArch.Family == sys.ARM && (Buildmode == BuildmodeCShared || Buildmode == BuildmodeCArchive))) {
+ // Darwin external linker on arm64 and on amd64 and arm in c-shared/c-archive buildmode
// complains about absolute relocs in __TEXT, so if the section is not
// executable, put it in __DATA segment.
msect = newMachoSect(mseg, buf, "__DATA")
@@ -411,6 +407,10 @@ func machoshbits(mseg *MachoSeg, sect *Section, segname string) {
msect.name = "__mod_init_func"
msect.flag = 9 // S_MOD_INIT_FUNC_POINTERS
}
+
+ if segname == "__DWARF" {
+ msect.flag |= 0x02000000
+ }
}
func Asmbmacho() {
@@ -418,23 +418,23 @@ func Asmbmacho() {
va := INITTEXT - int64(HEADR)
mh := getMachoHdr()
- switch Thearch.Thechar {
+ switch SysArch.Family {
default:
- Exitf("unknown macho architecture: %v", Thearch.Thechar)
+ Exitf("unknown macho architecture: %v", SysArch.Family)
- case '5':
+ case sys.ARM:
mh.cpu = MACHO_CPU_ARM
mh.subcpu = MACHO_SUBCPU_ARMV7
- case '6':
+ case sys.AMD64:
mh.cpu = MACHO_CPU_AMD64
mh.subcpu = MACHO_SUBCPU_X86
- case '7':
+ case sys.ARM64:
mh.cpu = MACHO_CPU_ARM64
mh.subcpu = MACHO_SUBCPU_ARM64_ALL
- case '8':
+ case sys.I386:
mh.cpu = MACHO_CPU_386
mh.subcpu = MACHO_SUBCPU_X86
}
@@ -445,7 +445,7 @@ func Asmbmacho() {
ms = newMachoSeg("", 40)
ms.fileoffset = Segtext.Fileoff
- if Thearch.Thechar == '5' || Buildmode == BuildmodeCArchive {
+ if SysArch.Family == sys.ARM || Buildmode == BuildmodeCArchive {
ms.filesize = Segdata.Fileoff + Segdata.Filelen - Segtext.Fileoff
} else {
ms.filesize = Segdwarf.Fileoff + Segdwarf.Filelen - Segtext.Fileoff
@@ -492,32 +492,46 @@ func Asmbmacho() {
machoshbits(ms, sect, "__DATA")
}
+ /* dwarf */
+ if Debug['w'] == 0 {
+ if Linkmode != LinkExternal {
+ ms = newMachoSeg("__DWARF", 20)
+ ms.vaddr = Segdwarf.Vaddr
+ ms.vsize = 0
+ ms.fileoffset = Segdwarf.Fileoff
+ ms.filesize = Segdwarf.Filelen
+ }
+ for sect := Segdwarf.Sect; sect != nil; sect = sect.Next {
+ machoshbits(ms, sect, "__DWARF")
+ }
+ }
+
if Linkmode != LinkExternal {
- switch Thearch.Thechar {
+ switch SysArch.Family {
default:
- Exitf("unknown macho architecture: %v", Thearch.Thechar)
+ Exitf("unknown macho architecture: %v", SysArch.Family)
- case '5':
+ case sys.ARM:
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':
+ case sys.AMD64:
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':
+ case sys.ARM64:
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':
+ case sys.I386:
ml := newMachoLoad(5, 16+2) /* unix thread */
ml.data[0] = 1 /* thread type */
ml.data[1] = 16 /* word count */
@@ -528,7 +542,6 @@ func Asmbmacho() {
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)
@@ -576,21 +589,13 @@ func Asmbmacho() {
// and we can assume OS X.
//
// See golang.org/issues/12941.
- const (
- LC_VERSION_MIN_MACOSX = 0x24
- LC_VERSION_MIN_IPHONEOS = 0x25
- LC_VERSION_MIN_WATCHOS = 0x30
- )
+ const LC_VERSION_MIN_MACOSX = 0x24
+
ml := newMachoLoad(LC_VERSION_MIN_MACOSX, 2)
ml.data[0] = 10<<16 | 7<<8 | 0<<0 // OS X version 10.7.0
ml.data[1] = 10<<16 | 7<<8 | 0<<0 // SDK 10.7.0
}
- // 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)
@@ -680,15 +685,11 @@ func machosymorder() {
}
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]
+ s := sortsym[i]
Adduint32(Ctxt, symtab, uint32(symstr.Size))
// Only add _ to C symbols. Go symbols have dot in the name.
@@ -697,33 +698,20 @@ func machosymtab() {
}
// 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')
- }
+ Addstring(symstr, strings.Replace(s.Extname, "·", ".", -1))
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
+ adduintxx(Ctxt, symtab, 0, SysArch.PtrSize) // no value
} else {
if s.Attr.CgoExport() {
Adduint8(Ctxt, symtab, 0x0f)
} else {
Adduint8(Ctxt, symtab, 0x0e)
}
- o = s
+ o := s
for o.Outer != nil {
o = o.Outer
}
@@ -734,7 +722,7 @@ func machosymtab() {
Adduint8(Ctxt, symtab, uint8(o.Sect.Extnum))
}
Adduint16(Ctxt, symtab, 0) // desc
- adduintxx(Ctxt, symtab, uint64(Symaddr(s)), Thearch.Ptrsize)
+ adduintxx(Ctxt, symtab, uint64(Symaddr(s)), SysArch.PtrSize)
}
}
}
@@ -821,27 +809,25 @@ func Domacholink() int64 {
return Rnd(int64(size), int64(INITRND))
}
-func machorelocsect(sect *Section, first *LSym) {
+func machorelocsect(sect *Section, syms []*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.Attr.Reachable() {
+ for i, s := range syms {
+ if !s.Attr.Reachable() {
continue
}
- if uint64(sym.Value) >= sect.Vaddr {
+ if uint64(s.Value) >= sect.Vaddr {
+ syms = syms[i:]
break
}
}
eaddr := int32(sect.Vaddr + sect.Length)
- var r *Reloc
- var ri int
- for ; sym != nil; sym = sym.Next {
+ for _, sym := range syms {
if !sym.Attr.Reachable() {
continue
}
@@ -850,8 +836,8 @@ func machorelocsect(sect *Section, first *LSym) {
}
Ctxt.Cursym = sym
- for ri = 0; ri < len(sym.R); ri++ {
- r = &sym.R[ri]
+ for ri := 0; ri < len(sym.R); ri++ {
+ r := &sym.R[ri]
if r.Done != 0 {
continue
}
@@ -876,5 +862,7 @@ func Machoemitreloc() {
for sect := Segdata.Sect; sect != nil; sect = sect.Next {
machorelocsect(sect, datap)
}
- dwarfemitreloc()
+ for sect := Segdwarf.Sect; sect != nil; sect = sect.Next {
+ machorelocsect(sect, list2slice(dwarfp))
+ }
}
diff --git a/src/cmd/link/internal/ld/macho_combine_dwarf.go b/src/cmd/link/internal/ld/macho_combine_dwarf.go
index b5a5a8d429..dcc371ec05 100644
--- a/src/cmd/link/internal/ld/macho_combine_dwarf.go
+++ b/src/cmd/link/internal/ld/macho_combine_dwarf.go
@@ -15,11 +15,9 @@ import (
"unsafe"
)
-var fakedwarf, realdwarf, linkseg *macho.Segment
+var realdwarf, linkseg *macho.Segment
var dwarfstart, linkstart int64
var linkoffset uint32
-var machHeader *macho.FileHeader
-var mappedHeader []byte
const (
LC_ID_DYLIB = 0xd
diff --git a/src/cmd/link/internal/ld/objfile.go b/src/cmd/link/internal/ld/objfile.go
index 8a406d17a6..bcfe52585f 100644
--- a/src/cmd/link/internal/ld/objfile.go
+++ b/src/cmd/link/internal/ld/objfile.go
@@ -110,7 +110,10 @@ package ld
import (
"bufio"
"bytes"
+ "cmd/internal/bio"
"cmd/internal/obj"
+ "crypto/sha1"
+ "encoding/base64"
"io"
"log"
"strconv"
@@ -146,18 +149,18 @@ type objReader struct {
file []*LSym
}
-func LoadObjFile(ctxt *Link, f *obj.Biobuf, pkg string, length int64, pn string) {
- start := obj.Boffset(f)
+func LoadObjFile(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+ start := f.Offset()
r := &objReader{
- rd: f.Reader(),
+ rd: f.Reader,
pkg: pkg,
ctxt: ctxt,
pn: pn,
dupSym: &LSym{Name: ".dup"},
}
r.loadObjFile()
- if obj.Boffset(f) != start+length {
- log.Fatalf("%s: unexpected end at %d, want %d", pn, int64(obj.Boffset(f)), int64(start+length))
+ if f.Offset() != start+length {
+ log.Fatalf("%s: unexpected end at %d, want %d", pn, f.Offset(), start+length)
}
}
@@ -330,8 +333,11 @@ overwrite:
}
if s.Type == obj.STEXT {
- s.Args = r.readInt32()
- s.Locals = r.readInt32()
+ s.FuncInfo = new(FuncInfo)
+ pc := s.FuncInfo
+
+ pc.Args = r.readInt32()
+ pc.Locals = r.readInt32()
if r.readUint8() != 0 {
s.Attr |= AttrNoSplit
}
@@ -340,13 +346,13 @@ overwrite:
s.Attr |= AttrReflectMethod
}
n := r.readInt()
- s.Autom = r.autom[:n:n]
+ pc.Autom = r.autom[:n:n]
if !isdup {
r.autom = r.autom[n:]
}
for i := 0; i < n; i++ {
- s.Autom[i] = Auto{
+ pc.Autom[i] = Auto{
Asym: r.readSymIndex(),
Aoffset: r.readInt32(),
Name: r.readInt16(),
@@ -354,8 +360,6 @@ overwrite:
}
}
- s.Pcln = new(Pcln)
- pc := s.Pcln
pc.Pcsp.P = r.readData()
pc.Pcfile.P = r.readData()
pc.Pcline.P = r.readData()
@@ -394,12 +398,7 @@ overwrite:
log.Fatalf("symbol %s listed multiple times", s.Name)
}
s.Attr |= AttrOnList
- if r.ctxt.Etextp != nil {
- r.ctxt.Etextp.Next = s
- } else {
- r.ctxt.Textp = s
- }
- r.ctxt.Etextp = s
+ r.ctxt.Textp = append(r.ctxt.Textp, s)
}
}
}
@@ -470,7 +469,7 @@ func (r *objReader) readInt64() int64 {
}
}
- return int64(uv>>1) ^ (int64(uint64(uv)<<63) >> 63)
+ return int64(uv>>1) ^ (int64(uv<<63) >> 63)
}
func (r *objReader) readInt() int {
@@ -529,13 +528,18 @@ func (r *objReader) readSymName() string {
r.readInt64()
return ""
}
- origName, err := r.rd.Peek(n)
- if err != nil {
- log.Fatalf("%s: unexpectedly long symbol name", r.pn)
- }
if cap(r.rdBuf) < n {
r.rdBuf = make([]byte, 2*n)
}
+ origName, err := r.rd.Peek(n)
+ if err == bufio.ErrBufferFull {
+ // Long symbol names are rare but exist. One source is type
+ // symbols for types with long string forms. See #15104.
+ origName = make([]byte, n)
+ r.readFull(origName)
+ } else if err != nil {
+ log.Fatalf("%s: error reading symbol: %v", err)
+ }
adjName := r.rdBuf[:0]
for {
i := bytes.Index(origName, emptyPkg)
@@ -544,8 +548,32 @@ func (r *objReader) readSymName() string {
// Read past the peeked origName, now that we're done with it,
// using the rfBuf (also no longer used) as the scratch space.
// TODO: use bufio.Reader.Discard if available instead?
- r.readFull(r.rdBuf[:n])
+ if err == nil {
+ r.readFull(r.rdBuf[:n])
+ }
r.rdBuf = adjName[:0] // in case 2*n wasn't enough
+
+ if DynlinkingGo() {
+ // These types are included in the symbol
+ // table when dynamically linking. To keep
+ // binary size down, we replace the names
+ // with SHA-1 prefixes.
+ //
+ // Keep the type.. prefix, which parts of the
+ // linker (like the DWARF generator) know means
+ // the symbol is not decodable.
+ //
+ // Leave type.runtime. symbols alone, because
+ // other parts of the linker manipulates them.
+ if strings.HasPrefix(s, "type.") && !strings.HasPrefix(s, "type.runtime.") {
+ hash := sha1.Sum([]byte(s))
+ prefix := "type."
+ if s[5] == '.' {
+ prefix = "type.."
+ }
+ s = prefix + base64.StdEncoding.EncodeToString(hash[:6])
+ }
+ }
return s
}
adjName = append(adjName, origName[:i]...)
diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go
index ff29ce2d70..991b9ef2cd 100644
--- a/src/cmd/link/internal/ld/pcln.go
+++ b/src/cmd/link/internal/ld/pcln.go
@@ -93,7 +93,7 @@ func pciterinit(ctxt *Link, it *Pciter, d *Pcdata) {
it.value = -1
it.start = 1
it.done = 0
- it.pcscale = uint32(ctxt.Arch.Minlc)
+ it.pcscale = uint32(ctxt.Arch.MinLC)
pciternext(it)
}
@@ -127,8 +127,7 @@ func addpctab(ftab *LSym, off int32, d *Pcdata) int32 {
var start int32
if len(d.P) > 0 {
start = int32(len(ftab.P))
- Symgrow(Ctxt, ftab, int64(start)+int64(len(d.P)))
- copy(ftab.P[start:], d.P)
+ Addbytes(Ctxt, ftab, d.P)
}
return int32(setuint32(Ctxt, ftab, int64(off), uint32(start)))
}
@@ -148,27 +147,21 @@ func renumberfiles(ctxt *Link, files []*LSym, d *Pcdata) {
for i := 0; i < len(files); i++ {
f = files[i]
if f.Type != obj.SFILEPATH {
- ctxt.Nhistfile++
- f.Value = int64(ctxt.Nhistfile)
+ ctxt.Filesyms = append(ctxt.Filesyms, f)
+ f.Value = int64(len(ctxt.Filesyms))
f.Type = obj.SFILEPATH
- f.Next = ctxt.Filesyms
f.Name = expandGoroot(f.Name)
- 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
+ oldval := it.value
+ var val int32
if oldval == -1 {
val = -1
} else {
@@ -178,9 +171,9 @@ func renumberfiles(ctxt *Link, files []*LSym, d *Pcdata) {
val = int32(files[oldval].Value)
}
- dv = val - newval
+ dv := val - newval
newval = val
- v = (uint32(dv) << 1) ^ uint32(int32(dv>>31))
+ v := (uint32(dv) << 1) ^ uint32(dv>>31)
addvarint(&out, v)
// pc delta
@@ -205,7 +198,7 @@ func container(s *LSym) int {
// pclntab initializes the pclntab symbol with
// runtime function and file name information.
-var pclntab_zpcln Pcln
+var pclntab_zpcln FuncInfo
// These variables are used to initialize runtime.firstmoduledata, see symtab.go:symtab.
var pclntabNfunc int32
@@ -229,40 +222,34 @@ func pclntab() {
nfunc := int32(0)
// Find container symbols, mark them with SCONTAINER
- for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next {
- if Ctxt.Cursym.Outer != nil {
- Ctxt.Cursym.Outer.Type |= obj.SCONTAINER
+ for _, s := range Ctxt.Textp {
+ if s.Outer != nil {
+ s.Outer.Type |= obj.SCONTAINER
}
}
- for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next {
- if container(Ctxt.Cursym) == 0 {
+ for _, s := range Ctxt.Textp {
+ if container(s) == 0 {
nfunc++
}
}
pclntabNfunc = nfunc
- Symgrow(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize)+4)
+ Symgrow(Ctxt, ftab, 8+int64(SysArch.PtrSize)+int64(nfunc)*2*int64(SysArch.PtrSize)+int64(SysArch.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)
+ setuint8(Ctxt, ftab, 6, uint8(SysArch.MinLC))
+ setuint8(Ctxt, ftab, 7, uint8(SysArch.PtrSize))
+ setuintxx(Ctxt, ftab, 8, uint64(nfunc), int64(SysArch.PtrSize))
+ pclntabPclntabOffset = int32(8 + SysArch.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 {
+ for _, Ctxt.Cursym = range Ctxt.Textp {
last = Ctxt.Cursym
if container(Ctxt.Cursym) != 0 {
continue
}
- pcln = Ctxt.Cursym.Pcln
+ pcln := Ctxt.Cursym.FuncInfo
if pcln == nil {
pcln = &pclntab_zpcln
}
@@ -271,17 +258,17 @@ func pclntab() {
pclntabFirstFunc = Ctxt.Cursym
}
- funcstart = int32(len(ftab.P))
- funcstart += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1)
+ funcstart := int32(len(ftab.P))
+ funcstart += int32(-len(ftab.P)) & (int32(SysArch.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))
+ setaddr(Ctxt, ftab, 8+int64(SysArch.PtrSize)+int64(nfunc)*2*int64(SysArch.PtrSize), Ctxt.Cursym)
+ setuintxx(Ctxt, ftab, 8+int64(SysArch.PtrSize)+int64(nfunc)*2*int64(SysArch.PtrSize)+int64(SysArch.PtrSize), uint64(funcstart), int64(SysArch.PtrSize))
// fixed size of struct, checked below
- off = funcstart
+ off := funcstart
- end = funcstart + int32(Thearch.Ptrsize) + 3*4 + 5*4 + int32(len(pcln.Pcdata))*4 + int32(len(pcln.Funcdata))*int32(Thearch.Ptrsize)
- if len(pcln.Funcdata) > 0 && (end&int32(Thearch.Ptrsize-1) != 0) {
+ end := funcstart + int32(SysArch.PtrSize) + 3*4 + 5*4 + int32(len(pcln.Pcdata))*4 + int32(len(pcln.Funcdata))*int32(SysArch.PtrSize)
+ if len(pcln.Funcdata) > 0 && (end&int32(SysArch.PtrSize-1) != 0) {
end += 4
}
Symgrow(Ctxt, ftab, int64(end))
@@ -294,7 +281,11 @@ func pclntab() {
// args int32
// TODO: Move into funcinfo.
- off = int32(setuint32(Ctxt, ftab, int64(off), uint32(Ctxt.Cursym.Args)))
+ args := uint32(0)
+ if Ctxt.Cursym.FuncInfo != nil {
+ args = uint32(Ctxt.Cursym.FuncInfo.Args)
+ }
+ off = int32(setuint32(Ctxt, ftab, int64(off), args))
// frame int32
// This has been removed (it was never set quite correctly anyway).
@@ -307,9 +298,10 @@ func pclntab() {
renumberfiles(Ctxt, pcln.File, &pcln.Pcfile)
if false {
// Sanity check the new numbering
+ var it Pciter
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)
+ if it.value < 1 || it.value > int32(len(Ctxt.Filesyms)) {
+ Diag("bad file number in pcfile: %d not in range [1, %d]\n", it.value, len(Ctxt.Filesyms))
errorexit()
}
}
@@ -323,32 +315,32 @@ func pclntab() {
off = addpctab(ftab, off, &pcln.Pcline)
off = int32(setuint32(Ctxt, ftab, int64(off), uint32(len(pcln.Pcdata))))
off = int32(setuint32(Ctxt, ftab, int64(off), uint32(len(pcln.Funcdata))))
- for i = 0; i < int32(len(pcln.Pcdata)); i++ {
+ for i := 0; i < len(pcln.Pcdata); 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 len(pcln.Funcdata) > 0 {
- if off&int32(Thearch.Ptrsize-1) != 0 {
+ if off&int32(SysArch.PtrSize-1) != 0 {
off += 4
}
- for i = 0; i < int32(len(pcln.Funcdata)); i++ {
+ for i := 0; i < len(pcln.Funcdata); i++ {
if pcln.Funcdata[i] == nil {
- setuintxx(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), uint64(pcln.Funcdataoff[i]), int64(Thearch.Ptrsize))
+ setuintxx(Ctxt, ftab, int64(off)+int64(SysArch.PtrSize)*int64(i), uint64(pcln.Funcdataoff[i]), int64(SysArch.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])
+ setaddrplus(Ctxt, ftab, int64(off)+int64(SysArch.PtrSize)*int64(i), pcln.Funcdata[i], pcln.Funcdataoff[i])
}
}
- off += int32(len(pcln.Funcdata)) * int32(Thearch.Ptrsize)
+ off += int32(len(pcln.Funcdata)) * int32(SysArch.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, len(pcln.Pcdata), len(pcln.Funcdata), Thearch.Ptrsize)
+ Diag("bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, len(pcln.Pcdata), len(pcln.Funcdata), SysArch.PtrSize)
errorexit()
}
@@ -357,25 +349,26 @@ func pclntab() {
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)
+ setaddrplus(Ctxt, ftab, 8+int64(SysArch.PtrSize)+int64(nfunc)*2*int64(SysArch.PtrSize), last, last.Size)
// Start file table.
start := int32(len(ftab.P))
- start += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1)
+ start += int32(-len(ftab.P)) & (int32(SysArch.PtrSize) - 1)
pclntabFiletabOffset = start
- setuint32(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint32(start))
+ setuint32(Ctxt, ftab, 8+int64(SysArch.PtrSize)+int64(nfunc)*2*int64(SysArch.PtrSize)+int64(SysArch.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 {
+ Symgrow(Ctxt, ftab, int64(start)+(int64(len(Ctxt.Filesyms))+1)*4)
+ setuint32(Ctxt, ftab, int64(start), uint32(len(Ctxt.Filesyms)))
+ for i := len(Ctxt.Filesyms) - 1; i >= 0; i-- {
+ s := Ctxt.Filesyms[i]
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))
+ fmt.Fprintf(Bso, "%5.2f pclntab=%d bytes, funcdata total %d bytes\n", obj.Cputime(), ftab.Size, funcdata_bytes)
}
}
@@ -407,10 +400,9 @@ func findfunctab() {
t.Attr |= AttrLocal
// find min and max address
- min := Ctxt.Textp.Value
-
+ min := Ctxt.Textp[0].Value
max := int64(0)
- for s := Ctxt.Textp; s != nil; s = s.Next {
+ for _, s := range Ctxt.Textp {
max = s.Value + s.Size
}
@@ -423,34 +415,34 @@ func findfunctab() {
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 {
+ for i, s := range Ctxt.Textp {
if container(s) != 0 {
continue
}
- p = s.Value
- e = s.Next
- for container(e) != 0 {
- e = e.Next
+ p := s.Value
+ var e *LSym
+ i++
+ if i < len(Ctxt.Textp) {
+ e = Ctxt.Textp[i]
+ }
+ for container(e) != 0 && i < len(Ctxt.Textp) {
+ e = Ctxt.Textp[i]
+ i++
}
+ q := max
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)
+ i = int((p - min) / SUBBUCKETSIZE)
if indexes[i] > idx {
indexes[i] = idx
}
}
- i = int32((q - 1 - min) / SUBBUCKETSIZE)
+ i = int((q - 1 - min) / SUBBUCKETSIZE)
if indexes[i] > idx {
indexes[i] = idx
}
@@ -463,15 +455,13 @@ func findfunctab() {
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]
+ 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++ {
+ for j := int32(0); j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ {
idx = indexes[i*SUBBUCKETS+j]
if idx == NOIDX {
Diag("hole in findfunctab")
diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go
index 56698361d0..839aa6cca7 100644
--- a/src/cmd/link/internal/ld/pe.go
+++ b/src/cmd/link/internal/ld/pe.go
@@ -6,6 +6,7 @@ package ld
import (
"cmd/internal/obj"
+ "cmd/internal/sys"
"encoding/binary"
"fmt"
"os"
@@ -419,9 +420,9 @@ func chksectseg(h *IMAGE_SECTION_HEADER, s *Segment) {
func Peinit() {
var l int
- switch Thearch.Thechar {
+ switch SysArch.Family {
// 64-bit architectures
- case '6':
+ case sys.AMD64:
pe64 = 1
l = binary.Size(&oh64)
@@ -506,7 +507,7 @@ func initdynimport() *Dll {
if err != nil {
Diag("failed to parse stdcall decoration: %v", err)
}
- m.argsize *= Thearch.Ptrsize
+ m.argsize *= SysArch.PtrSize
s.Extname = s.Extname[:i]
}
@@ -520,10 +521,10 @@ func initdynimport() *Dll {
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))
+ Symgrow(Ctxt, m.s, int64(SysArch.PtrSize))
dynName := m.s.Extname
// only windows/386 requires stdcall decoration
- if Thearch.Thechar == '8' && m.argsize >= 0 {
+ if SysArch.Family == sys.I386 && m.argsize >= 0 {
dynName += fmt.Sprintf("@%d", m.argsize)
}
dynSym := Linklookup(Ctxt, dynName, 0)
@@ -532,7 +533,7 @@ func initdynimport() *Dll {
r := Addrel(m.s)
r.Sym = dynSym
r.Off = 0
- r.Siz = uint8(Thearch.Ptrsize)
+ r.Siz = uint8(SysArch.PtrSize)
r.Type = obj.R_ADDR
}
}
@@ -546,10 +547,10 @@ func initdynimport() *Dll {
m.s.Sub = dynamic.Sub
dynamic.Sub = m.s
m.s.Value = dynamic.Size
- dynamic.Size += int64(Thearch.Ptrsize)
+ dynamic.Size += int64(SysArch.PtrSize)
}
- dynamic.Size += int64(Thearch.Ptrsize)
+ dynamic.Size += int64(SysArch.PtrSize)
}
}
@@ -763,7 +764,7 @@ func addexports() {
// perelocsect relocates symbols from first in section sect, and returns
// the total number of relocations emitted.
-func perelocsect(sect *Section, first *LSym) int {
+func perelocsect(sect *Section, syms []*LSym) int {
// If main section has no bits, nothing to relocate.
if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
return 0
@@ -772,20 +773,18 @@ func perelocsect(sect *Section, first *LSym) int {
relocs := 0
sect.Reloff = uint64(Cpos())
- var sym *LSym
- for sym = first; sym != nil; sym = sym.Next {
- if !sym.Attr.Reachable() {
+ for i, s := range syms {
+ if !s.Attr.Reachable() {
continue
}
- if uint64(sym.Value) >= sect.Vaddr {
+ if uint64(s.Value) >= sect.Vaddr {
+ syms = syms[i:]
break
}
}
eaddr := int32(sect.Vaddr + sect.Length)
- var r *Reloc
- var ri int
- for ; sym != nil; sym = sym.Next {
+ for _, sym := range syms {
if !sym.Attr.Reachable() {
continue
}
@@ -794,8 +793,8 @@ func perelocsect(sect *Section, first *LSym) int {
}
Ctxt.Cursym = sym
- for ri = 0; ri < len(sym.R); ri++ {
- r = &sym.R[ri]
+ for ri := 0; ri < len(sym.R); ri++ {
+ r := &sym.R[ri]
if r.Done != 0 {
continue
}
@@ -876,7 +875,7 @@ func peemitreloc(text, data, ctors *IMAGE_SECTION_HEADER) {
ctors.NumberOfRelocations = 1
ctors.PointerToRelocations = uint32(Cpos())
sectoff := ctors.VirtualAddress
- Lputl(uint32(sectoff))
+ Lputl(sectoff)
Lputl(uint32(dottext.Dynid))
switch obj.Getgoarch() {
default:
@@ -946,7 +945,7 @@ func writePESymTableRecords() int {
}
// only windows/386 requires underscore prefix on external symbols
- if Thearch.Thechar == '8' &&
+ if SysArch.Family == sys.I386 &&
Linkmode == LinkExternal &&
(s.Type != obj.SDYNIMPORT || s.Attr.CgoExport()) &&
s.Name == s.Extname &&
@@ -1002,7 +1001,7 @@ func writePESymTableRecords() int {
for d := dr; d != nil; d = d.next {
for m := d.ms; m != nil; m = m.next {
s := m.s.R[0].Xsym
- put(s, s.Name, 'U', 0, int64(Thearch.Ptrsize), 0, nil)
+ put(s, s.Name, 'U', 0, int64(SysArch.PtrSize), 0, nil)
}
}
@@ -1042,7 +1041,7 @@ func addpesymtable() {
// write COFF string table
Lputl(uint32(len(strtbl)) + 4)
for i := 0; i < len(strtbl); i++ {
- Cput(uint8(strtbl[i]))
+ Cput(strtbl[i])
}
if Linkmode != LinkExternal {
strnput("", int(h.SizeOfRawData-uint32(size)))
@@ -1129,12 +1128,12 @@ func addinitarray() (c *IMAGE_SECTION_HEADER) {
}
func Asmbpe() {
- switch Thearch.Thechar {
+ switch SysArch.Family {
default:
- Exitf("unknown PE architecture: %v", Thearch.Thechar)
- case '6':
+ Exitf("unknown PE architecture: %v", SysArch.Family)
+ case sys.AMD64:
fh.Machine = IMAGE_FILE_MACHINE_AMD64
- case '8':
+ case sys.I386:
fh.Machine = IMAGE_FILE_MACHINE_I386
}
diff --git a/src/cmd/link/internal/ld/pobj.go b/src/cmd/link/internal/ld/pobj.go
index f48b54efda..b64bb5deaf 100644
--- a/src/cmd/link/internal/ld/pobj.go
+++ b/src/cmd/link/internal/ld/pobj.go
@@ -31,7 +31,9 @@
package ld
import (
+ "bufio"
"cmd/internal/obj"
+ "cmd/internal/sys"
"flag"
"fmt"
"os"
@@ -44,13 +46,12 @@ var (
)
func Ldmain() {
- Ctxt = linknew(Thelinkarch)
- Ctxt.Thechar = int32(Thearch.Thechar)
- Ctxt.Thestring = Thestring
+ Bso = bufio.NewWriter(os.Stdout)
+
+ Ctxt = linknew(SysArch)
Ctxt.Diag = Diag
- Ctxt.Bso = &Bso
+ Ctxt.Bso = Bso
- Bso = *obj.Binitw(os.Stdout)
Debug = [128]int{}
nerrors = 0
outfile = ""
@@ -70,7 +71,7 @@ func Ldmain() {
}
}
- if Thearch.Thechar == '6' && obj.Getgoos() == "plan9" {
+ if SysArch.Family == sys.AMD64 && obj.Getgoos() == "plan9" {
obj.Flagcount("8", "use 64-bit addresses in symbol table", &Debug['8'])
}
obj.Flagfn1("B", "add an ELF NT_GNU_BUILD_ID `note` when using ELF", addbuildinfo)
@@ -89,6 +90,7 @@ func Ldmain() {
flag.Var(&Buildmode, "buildmode", "set build `mode`")
obj.Flagcount("c", "dump call graph", &Debug['c'])
obj.Flagcount("d", "disable dynamic executable", &Debug['d'])
+ flag.BoolVar(&flag_dumpdep, "dumpdep", false, "dump symbol dependency graph")
obj.Flagstr("extar", "archive program for buildmode=c-archive", &extar)
obj.Flagstr("extld", "use `linker` when linking in external mode", &extld)
obj.Flagstr("extldflags", "pass `flags` to external linker", &extldflags)
@@ -107,7 +109,7 @@ func Ldmain() {
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' {
+ if SysArch.InFamily(sys.ARM, sys.AMD64) {
obj.Flagcount("shared", "generate shared object (implies -linkmode external)", &flagShared)
}
obj.Flagstr("tmpdir", "use `directory` for temporary files", &tmpdir)
@@ -122,7 +124,7 @@ func Ldmain() {
obj.Flagparse(usage)
startProfile()
- Ctxt.Bso = &Bso
+ Ctxt.Bso = Bso
Ctxt.Debugvlog = int32(Debug['v'])
if flagShared != 0 {
if Buildmode == BuildmodeUnset {
@@ -163,7 +165,7 @@ func Ldmain() {
}
if Debug['v'] != 0 {
- fmt.Fprintf(&Bso, "HEADER = -H%d -T0x%x -D0x%x -R0x%x\n", HEADTYPE, uint64(INITTEXT), uint64(INITDAT), uint32(INITRND))
+ fmt.Fprintf(Bso, "HEADER = -H%d -T0x%x -D0x%x -R0x%x\n", HEADTYPE, uint64(INITTEXT), uint64(INITDAT), uint32(INITRND))
}
Bso.Flush()
@@ -214,9 +216,9 @@ func Ldmain() {
hostlink()
archive()
if Debug['v'] != 0 {
- fmt.Fprintf(&Bso, "%5.2f cpu time\n", obj.Cputime())
- fmt.Fprintf(&Bso, "%d symbols\n", len(Ctxt.Allsym))
- fmt.Fprintf(&Bso, "%d liveness data\n", liveness)
+ fmt.Fprintf(Bso, "%5.2f cpu time\n", obj.Cputime())
+ fmt.Fprintf(Bso, "%d symbols\n", len(Ctxt.Allsym))
+ fmt.Fprintf(Bso, "%d liveness data\n", liveness)
}
Bso.Flush()
diff --git a/src/cmd/link/internal/ld/sym.go b/src/cmd/link/internal/ld/sym.go
index 3deb94644e..76fe7dab79 100644
--- a/src/cmd/link/internal/ld/sym.go
+++ b/src/cmd/link/internal/ld/sym.go
@@ -33,6 +33,7 @@ package ld
import (
"cmd/internal/obj"
+ "cmd/internal/sys"
"log"
"strconv"
)
@@ -55,7 +56,7 @@ var headers = []struct {
{"windowsgui", obj.Hwindows},
}
-func linknew(arch *LinkArch) *Link {
+func linknew(arch *sys.Arch) *Link {
ctxt := &Link{
Hash: []map[string]*LSym{
// preallocate about 2mb for hash of
@@ -98,33 +99,33 @@ func linknew(arch *LinkArch) *Link {
obj.Hdragonfly,
obj.Hsolaris:
if obj.Getgoos() == "android" {
- switch ctxt.Arch.Thechar {
- case '6':
+ switch ctxt.Arch.Family {
+ case sys.AMD64:
// Android/amd64 constant - offset from 0(FS) to our TLS slot.
// Explained in src/runtime/cgo/gcc_android_*.c
ctxt.Tlsoffset = 0x1d0
- case '8':
+ case sys.I386:
// Android/386 constant - offset from 0(GS) to our TLS slot.
ctxt.Tlsoffset = 0xf8
default:
- ctxt.Tlsoffset = -1 * ctxt.Arch.Ptrsize
+ ctxt.Tlsoffset = -1 * ctxt.Arch.PtrSize
}
} else {
- ctxt.Tlsoffset = -1 * ctxt.Arch.Ptrsize
+ ctxt.Tlsoffset = -1 * ctxt.Arch.PtrSize
}
case obj.Hnacl:
- switch ctxt.Arch.Thechar {
+ switch ctxt.Arch.Family {
default:
log.Fatalf("unknown thread-local storage offset for nacl/%s", ctxt.Arch.Name)
- case '5':
+ case sys.ARM:
ctxt.Tlsoffset = 0
- case '6':
+ case sys.AMD64:
ctxt.Tlsoffset = 0
- case '8':
+ case sys.I386:
ctxt.Tlsoffset = -8
}
@@ -133,26 +134,26 @@ func linknew(arch *LinkArch) *Link {
* Explained in src/runtime/cgo/gcc_darwin_*.c.
*/
case obj.Hdarwin:
- switch ctxt.Arch.Thechar {
+ switch ctxt.Arch.Family {
default:
log.Fatalf("unknown thread-local storage offset for darwin/%s", ctxt.Arch.Name)
- case '5':
+ case sys.ARM:
ctxt.Tlsoffset = 0 // dummy value, not needed
- case '6':
+ case sys.AMD64:
ctxt.Tlsoffset = 0x8a0
- case '7':
+ case sys.ARM64:
ctxt.Tlsoffset = 0 // dummy value, not needed
- case '8':
+ case sys.I386:
ctxt.Tlsoffset = 0x468
}
}
// On arm, record goarm.
- if ctxt.Arch.Thechar == '5' {
+ if ctxt.Arch.Family == sys.ARM {
ctxt.Goarm = obj.Getgoarm()
}
diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go
index 3258bc1ff9..94a6d0ab29 100644
--- a/src/cmd/link/internal/ld/symtab.go
+++ b/src/cmd/link/internal/ld/symtab.go
@@ -32,6 +32,7 @@ package ld
import (
"cmd/internal/obj"
+ "cmd/internal/sys"
"fmt"
"path/filepath"
"strings"
@@ -53,21 +54,14 @@ func putelfstr(s string) int {
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)
-
+ Elfstrdat = append(Elfstrdat, s...)
+ Elfstrdat = append(Elfstrdat, 0)
return off
}
func putelfsyment(off int, addr int64, size int64, info int, shndx int, other int) {
- switch Thearch.Thechar {
- case '0', '6', '7', '9', 'z':
+ if elf64 {
Thearch.Lput(uint32(off))
Cput(uint8(info))
Cput(uint8(other))
@@ -75,8 +69,7 @@ func putelfsyment(off int, addr int64, size int64, info int, shndx int, other in
Thearch.Vput(uint64(addr))
Thearch.Vput(uint64(size))
Symsize += ELF64SYMSIZE
-
- default:
+ } else {
Thearch.Lput(uint32(off))
Thearch.Lput(uint32(addr))
Thearch.Lput(uint32(size))
@@ -162,7 +155,7 @@ func putelfsym(x *LSym, s string, t int, addr int64, size int64, ver int, go_ *L
if x.Type&obj.SHIDDEN != 0 {
other = STV_HIDDEN
}
- if (Buildmode == BuildmodePIE || DynlinkingGo()) && Thearch.Thechar == '9' && type_ == STT_FUNC && x.Name != "runtime.duffzero" && x.Name != "runtime.duffcopy" {
+ if (Buildmode == BuildmodePIE || DynlinkingGo()) && SysArch.Family == sys.PPC64 && type_ == STT_FUNC && x.Name != "runtime.duffzero" && x.Name != "runtime.duffcopy" {
// On ppc64 the top three bits of the st_other field indicate how
// many instructions separate the global and local entry points. In
// our case it is two instructions, indicated by the value 3.
@@ -197,18 +190,6 @@ func putelfsectionsym(s *LSym, shndx int) {
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)
@@ -217,7 +198,9 @@ func Asmelfsym() {
// Some linkers will add a FILE sym if one is not present.
// Avoid having the working directory inserted into the symbol table.
- putelfsyment(0, 0, 0, STB_LOCAL<<4|STT_FILE, SHN_ABS, 0)
+ // It is added with a name to avoid problems with external linking
+ // encountered on some versions of Solaris. See issue #14957.
+ putelfsyment(putelfstr("go.go"), 0, 0, STB_LOCAL<<4|STT_FILE, SHN_ABS, 0)
numelfsym++
elfbind = STB_LOCAL
@@ -243,7 +226,7 @@ func putplan9sym(x *LSym, s string, t int, addr int64, size int64, ver int, go_
'Z',
'm':
l := 4
- if HEADTYPE == obj.Hplan9 && Thearch.Thechar == '6' && Debug['8'] == 0 {
+ if HEADTYPE == obj.Hplan9 && SysArch.Family == sys.AMD64 && Debug['8'] == 0 {
Lputb(uint32(addr >> 32))
l = 8
}
@@ -253,10 +236,10 @@ func putplan9sym(x *LSym, s string, t int, addr int64, size int64, ver int, go_
var i int
if t == 'z' || t == 'Z' {
- Cput(uint8(s[0]))
+ Cput(s[0])
for i = 1; s[i] != 0 || s[i+1] != 0; i += 2 {
- Cput(uint8(s[i]))
- Cput(uint8(s[i+1]))
+ Cput(s[i])
+ Cput(s[i+1])
}
Cput(0)
@@ -268,7 +251,7 @@ func putplan9sym(x *LSym, s string, t int, addr int64, size int64, ver int, go_
s = s[1:]
}
for i = 0; i < len(s); i++ {
- Cput(uint8(s[i]))
+ Cput(s[i])
}
Cput(0)
}
@@ -346,6 +329,8 @@ func symtab() {
xdefine("runtime.eitablink", obj.SRODATA, 0)
xdefine("runtime.rodata", obj.SRODATA, 0)
xdefine("runtime.erodata", obj.SRODATA, 0)
+ xdefine("runtime.types", obj.SRODATA, 0)
+ xdefine("runtime.etypes", obj.SRODATA, 0)
xdefine("runtime.noptrdata", obj.SNOPTRDATA, 0)
xdefine("runtime.enoptrdata", obj.SNOPTRDATA, 0)
xdefine("runtime.data", obj.SDATA, 0)
@@ -399,33 +384,19 @@ func symtab() {
symtyperel = s
}
- s = Linklookup(Ctxt, "go.string.*", 0)
- s.Type = obj.SGOSTRING
- s.Attr |= AttrLocal
- s.Size = 0
- s.Attr |= AttrReachable
- symgostring := s
-
- s = Linklookup(Ctxt, "go.string.hdr.*", 0)
- s.Type = obj.SGOSTRINGHDR
- s.Attr |= AttrLocal
- s.Size = 0
- s.Attr |= AttrReachable
- symgostringhdr := s
-
- s = Linklookup(Ctxt, "go.func.*", 0)
- s.Type = obj.SGOFUNC
- s.Attr |= AttrLocal
- s.Size = 0
- s.Attr |= AttrReachable
- symgofunc := s
-
- s = Linklookup(Ctxt, "runtime.gcbits.*", 0)
- s.Type = obj.SGCBITS
- s.Attr |= AttrLocal
- s.Size = 0
- s.Attr |= AttrReachable
- symgcbits := s
+ groupSym := func(name string, t int16) *LSym {
+ s := Linklookup(Ctxt, name, 0)
+ s.Type = t
+ s.Size = 0
+ s.Attr |= AttrLocal | AttrReachable
+ return s
+ }
+ var (
+ symgostring = groupSym("go.string.*", obj.SGOSTRING)
+ symgostringhdr = groupSym("go.string.hdr.*", obj.SGOSTRINGHDR)
+ symgofunc = groupSym("go.func.*", obj.SGOFUNC)
+ symgcbits = groupSym("runtime.gcbits.*", obj.SGCBITS)
+ )
symtypelink := Linklookup(Ctxt, "runtime.typelink", 0)
symtypelink.Type = obj.STYPELINK
@@ -451,34 +422,37 @@ func symtab() {
continue
}
- if strings.HasPrefix(s.Name, "type.") {
+ switch {
+ case strings.HasPrefix(s.Name, "type."):
if !DynlinkingGo() {
s.Attr |= AttrHidden
}
- if UseRelro() && len(s.R) > 0 {
+ if UseRelro() {
s.Type = obj.STYPERELRO
s.Outer = symtyperel
} else {
s.Type = obj.STYPE
s.Outer = symtype
}
- }
- if strings.HasPrefix(s.Name, "go.typelink.") {
+ case strings.HasPrefix(s.Name, "go.importpath.") && UseRelro():
+ // Keep go.importpath symbols in the same section as types and
+ // names, as they can be referred to by a section offset.
+ s.Type = obj.STYPERELRO
+
+ case strings.HasPrefix(s.Name, "go.typelink."):
ntypelinks++
s.Type = obj.STYPELINK
s.Attr |= AttrHidden
s.Outer = symtypelink
- }
- if strings.HasPrefix(s.Name, "go.itablink.") {
+ case strings.HasPrefix(s.Name, "go.itablink."):
nitablinks++
s.Type = obj.SITABLINK
s.Attr |= AttrHidden
s.Outer = symitablink
- }
- if strings.HasPrefix(s.Name, "go.string.") {
+ case strings.HasPrefix(s.Name, "go.string."):
s.Type = obj.SGOSTRING
s.Attr |= AttrHidden
s.Outer = symgostring
@@ -486,21 +460,18 @@ func symtab() {
s.Type = obj.SGOSTRINGHDR
s.Outer = symgostringhdr
}
- }
- if strings.HasPrefix(s.Name, "runtime.gcbits.") {
+ case strings.HasPrefix(s.Name, "runtime.gcbits."):
s.Type = obj.SGCBITS
s.Attr |= AttrHidden
s.Outer = symgcbits
- }
- if strings.HasPrefix(s.Name, "go.func.") {
+ case strings.HasPrefix(s.Name, "go.func."):
s.Type = obj.SGOFUNC
s.Attr |= AttrHidden
s.Outer = symgofunc
- }
- if strings.HasPrefix(s.Name, "gcargs.") || strings.HasPrefix(s.Name, "gclocals.") || strings.HasPrefix(s.Name, "gclocals·") {
+ case strings.HasPrefix(s.Name, "gcargs."), strings.HasPrefix(s.Name, "gclocals."), strings.HasPrefix(s.Name, "gclocals·"):
s.Type = obj.SGOFUNC
s.Attr |= AttrHidden
s.Outer = symgofunc
@@ -533,8 +504,8 @@ func symtab() {
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)+1)
- adduint(Ctxt, moduledata, uint64(Ctxt.Nhistfile)+1)
+ adduint(Ctxt, moduledata, uint64(len(Ctxt.Filesyms))+1)
+ adduint(Ctxt, moduledata, uint64(len(Ctxt.Filesyms))+1)
// findfunctab
Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.findfunctab", 0))
// minpc, maxpc
@@ -554,6 +525,8 @@ func symtab() {
Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.end", 0))
Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.gcdata", 0))
Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.gcbss", 0))
+ Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.types", 0))
+ Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.etypes", 0))
// The typelinks slice
Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.typelink", 0))
adduint(Ctxt, moduledata, uint64(ntypelinks))
diff --git a/src/cmd/link/internal/mips64/asm.go b/src/cmd/link/internal/mips64/asm.go
index 8249c54e45..785002b02c 100644
--- a/src/cmd/link/internal/mips64/asm.go
+++ b/src/cmd/link/internal/mips64/asm.go
@@ -32,6 +32,7 @@ package mips64
import (
"cmd/internal/obj"
+ "cmd/internal/sys"
"cmd/link/internal/ld"
"encoding/binary"
"fmt"
@@ -40,10 +41,6 @@ import (
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")
}
@@ -82,8 +79,8 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
// the first instruction is always at the lower address, this is endian neutral;
// but note that o1 and o2 should still use the target endian.
- o1 := ld.Thelinkarch.ByteOrder.Uint32(s.P[r.Off:])
- o2 := ld.Thelinkarch.ByteOrder.Uint32(s.P[r.Off+4:])
+ o1 := ld.SysArch.ByteOrder.Uint32(s.P[r.Off:])
+ o2 := ld.SysArch.ByteOrder.Uint32(s.P[r.Off+4:])
o1 = o1&0xffff0000 | uint32(t>>16)&0xffff
o2 = o2&0xffff0000 | uint32(t)&0xffff
@@ -99,7 +96,7 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
obj.R_JMPMIPS:
// Low 26 bits = (S + A) >> 2
t := ld.Symaddr(r.Sym) + r.Add
- o1 := ld.Thelinkarch.ByteOrder.Uint32(s.P[r.Off:])
+ o1 := ld.SysArch.ByteOrder.Uint32(s.P[r.Off:])
*val = int64(o1&0xfc000000 | uint32(t>>2)&^0xfc000000)
return 0
}
@@ -113,7 +110,7 @@ func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 {
func asmb() {
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime())
+ fmt.Fprintf(ld.Bso, "%5.2f asmb\n", obj.Cputime())
}
ld.Bso.Flush()
@@ -131,7 +128,7 @@ func asmb() {
if ld.Segrodata.Filelen > 0 {
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime())
+ fmt.Fprintf(ld.Bso, "%5.2f rodatblk\n", obj.Cputime())
}
ld.Bso.Flush()
@@ -140,13 +137,16 @@ func asmb() {
}
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime())
+ 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))
+ ld.Cseek(int64(ld.Segdwarf.Fileoff))
+ ld.Dwarfblk(int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+
/* output symbol table */
ld.Symsize = 0
@@ -155,13 +155,13 @@ func asmb() {
if ld.Debug['s'] == 0 {
// TODO: rationalize
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime())
+ 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.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND)))
}
@@ -174,17 +174,12 @@ func asmb() {
default:
if ld.Iself {
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f elfsym\n", obj.Cputime())
+ 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()
}
@@ -198,7 +193,7 @@ func asmb() {
if sym != nil {
ld.Lcsize = int32(len(sym.P))
for i := 0; int32(i) < ld.Lcsize; i++ {
- ld.Cput(uint8(sym.P[i]))
+ ld.Cput(sym.P[i])
}
ld.Cflush()
@@ -208,7 +203,7 @@ func asmb() {
ld.Ctxt.Cursym = nil
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f header\n", obj.Cputime())
+ fmt.Fprintf(ld.Bso, "%5.2f header\n", obj.Cputime())
}
ld.Bso.Flush()
ld.Cseek(0)
@@ -216,10 +211,10 @@ func asmb() {
default:
case obj.Hplan9: /* plan 9 */
magic := uint32(4*18*18 + 7)
- if ld.Thestring == "mips64le" {
+ if ld.SysArch == sys.ArchMIPS64LE {
magic = uint32(4*26*26 + 7)
}
- ld.Thearch.Lput(uint32(magic)) /* magic */
+ ld.Thearch.Lput(magic) /* 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))
diff --git a/src/cmd/link/internal/mips64/l.go b/src/cmd/link/internal/mips64/l.go
index 003ee5ce71..f4191e69ab 100644
--- a/src/cmd/link/internal/mips64/l.go
+++ b/src/cmd/link/internal/mips64/l.go
@@ -62,11 +62,9 @@ package mips64
// THE SOFTWARE.
const (
- thechar = '0'
MaxAlign = 32 // max data alignment
MinAlign = 1 // min data alignment
FuncAlign = 8
- MINLC = 4
)
/* Used by ../internal/ld/dwarf.go */
diff --git a/src/cmd/link/internal/mips64/obj.go b/src/cmd/link/internal/mips64/obj.go
index 57a1b2ab14..87bb3a079b 100644
--- a/src/cmd/link/internal/mips64/obj.go
+++ b/src/cmd/link/internal/mips64/obj.go
@@ -32,6 +32,7 @@ package mips64
import (
"cmd/internal/obj"
+ "cmd/internal/sys"
"cmd/link/internal/ld"
"fmt"
"log"
@@ -45,21 +46,15 @@ func Main() {
}
func linkarchinit() {
- ld.Thestring = obj.Getgoarch()
- if ld.Thestring == "mips64le" {
- ld.Thelinkarch = &ld.Linkmips64le
+ if obj.Getgoarch() == "mips64le" {
+ ld.SysArch = sys.ArchMIPS64LE
} else {
- ld.Thelinkarch = &ld.Linkmips64
+ ld.SysArch = sys.ArchMIPS64
}
- 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.Minalign = MinAlign
- ld.Thearch.Minlc = MINLC
ld.Thearch.Dwarfregsp = DWARFREGSP
ld.Thearch.Dwarfreglr = DWARFREGLR
@@ -72,7 +67,7 @@ func linkarchinit() {
ld.Thearch.Elfsetupplt = elfsetupplt
ld.Thearch.Gentext = gentext
ld.Thearch.Machoreloc1 = machoreloc1
- if ld.Thelinkarch == &ld.Linkmips64le {
+ if ld.SysArch == sys.ArchMIPS64LE {
ld.Thearch.Lput = ld.Lputl
ld.Thearch.Wput = ld.Wputl
ld.Thearch.Vput = ld.Vputl
diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go
index ae69799abf..dbf5fac0de 100644
--- a/src/cmd/link/internal/ppc64/asm.go
+++ b/src/cmd/link/internal/ppc64/asm.go
@@ -39,14 +39,6 @@ import (
)
func genplt() {
- 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
@@ -95,11 +87,9 @@ func genplt() {
//
// 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]
+ for _, s := range ld.Ctxt.Textp {
+ for i := range s.R {
+ r := &s.R[i]
if r.Type != 256+ld.R_PPC64_REL24 || r.Sym.Type != obj.SDYNIMPORT {
continue
}
@@ -109,24 +99,16 @@ func genplt() {
addpltsym(ld.Ctxt, r.Sym)
// Generate call stub
- n = fmt.Sprintf("%s.%s", s.Name, r.Sym.Name)
+ n := fmt.Sprintf("%s.%s", s.Name, r.Sym.Name)
- stub = ld.Linklookup(ld.Ctxt, n, 0)
+ stub := ld.Linklookup(ld.Ctxt, n, 0)
if s.Attr.Reachable() {
stub.Attr |= ld.AttrReachable
}
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
-
+ ld.Ctxt.Textp = append(ld.Ctxt.Textp, stub)
gencallstub(1, stub, r.Sym)
}
@@ -135,11 +117,10 @@ func genplt() {
// Restore TOC after bl. The compiler put a
// nop here for us to overwrite.
- o1 = 0xe8410018 // ld r2,24(r1)
+ const o1 = 0xe8410018 // ld r2,24(r1)
ld.Ctxt.Arch.ByteOrder.PutUint32(s.P[r.Off+4:], o1)
}
}
-
}
func genaddmoduledata() {
@@ -195,13 +176,7 @@ func genaddmoduledata() {
// blr
o(0x4e800020)
- if ld.Ctxt.Etextp != nil {
- ld.Ctxt.Etextp.Next = initfunc
- } else {
- ld.Ctxt.Textp = initfunc
- }
- ld.Ctxt.Etextp = initfunc
-
+ ld.Ctxt.Textp = append(ld.Ctxt.Textp, initfunc)
initarray_entry := ld.Linklookup(ld.Ctxt, "go.link.addmoduledatainit", 0)
initarray_entry.Attr |= ld.AttrReachable
initarray_entry.Attr |= ld.AttrLocal
@@ -265,10 +240,6 @@ func gencallstub(abicase int, stub *ld.LSym, targ *ld.LSym) {
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
@@ -834,7 +805,7 @@ func ensureglinkresolver() *ld.LSym {
func asmb() {
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime())
+ fmt.Fprintf(ld.Bso, "%5.2f asmb\n", obj.Cputime())
}
ld.Bso.Flush()
@@ -852,7 +823,7 @@ func asmb() {
if ld.Segrodata.Filelen > 0 {
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime())
+ fmt.Fprintf(ld.Bso, "%5.2f rodatblk\n", obj.Cputime())
}
ld.Bso.Flush()
@@ -861,13 +832,16 @@ func asmb() {
}
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime())
+ 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))
+ ld.Cseek(int64(ld.Segdwarf.Fileoff))
+ ld.Dwarfblk(int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+
/* output symbol table */
ld.Symsize = 0
@@ -876,13 +850,13 @@ func asmb() {
if ld.Debug['s'] == 0 {
// TODO: rationalize
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime())
+ 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.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND)))
}
@@ -895,17 +869,12 @@ func asmb() {
default:
if ld.Iself {
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f elfsym\n", obj.Cputime())
+ 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()
}
@@ -919,7 +888,7 @@ func asmb() {
if sym != nil {
ld.Lcsize = int32(len(sym.P))
for i := 0; int32(i) < ld.Lcsize; i++ {
- ld.Cput(uint8(sym.P[i]))
+ ld.Cput(sym.P[i])
}
ld.Cflush()
@@ -929,7 +898,7 @@ func asmb() {
ld.Ctxt.Cursym = nil
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f header\n", obj.Cputime())
+ fmt.Fprintf(ld.Bso, "%5.2f header\n", obj.Cputime())
}
ld.Bso.Flush()
ld.Cseek(0)
diff --git a/src/cmd/link/internal/ppc64/l.go b/src/cmd/link/internal/ppc64/l.go
index 622d6bb12e..a720993fbc 100644
--- a/src/cmd/link/internal/ppc64/l.go
+++ b/src/cmd/link/internal/ppc64/l.go
@@ -62,11 +62,9 @@ package ppc64
// THE SOFTWARE.
const (
- thechar = '9'
MaxAlign = 32 // max data alignment
MinAlign = 1 // min data alignment
FuncAlign = 8
- MINLC = 4
)
/* Used by ../internal/ld/dwarf.go */
diff --git a/src/cmd/link/internal/ppc64/obj.go b/src/cmd/link/internal/ppc64/obj.go
index 539ab1ac02..a540ab85b5 100644
--- a/src/cmd/link/internal/ppc64/obj.go
+++ b/src/cmd/link/internal/ppc64/obj.go
@@ -32,6 +32,7 @@ package ppc64
import (
"cmd/internal/obj"
+ "cmd/internal/sys"
"cmd/link/internal/ld"
"fmt"
"log"
@@ -45,21 +46,15 @@ func Main() {
}
func linkarchinit() {
- ld.Thestring = obj.Getgoarch()
- if ld.Thestring == "ppc64le" {
- ld.Thelinkarch = &ld.Linkppc64le
+ if obj.Getgoarch() == "ppc64le" {
+ ld.SysArch = sys.ArchPPC64LE
} else {
- ld.Thelinkarch = &ld.Linkppc64
+ ld.SysArch = sys.ArchPPC64
}
- 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.Minalign = MinAlign
- ld.Thearch.Minlc = MINLC
ld.Thearch.Dwarfregsp = DWARFREGSP
ld.Thearch.Dwarfreglr = DWARFREGLR
@@ -72,7 +67,7 @@ func linkarchinit() {
ld.Thearch.Elfsetupplt = elfsetupplt
ld.Thearch.Gentext = gentext
ld.Thearch.Machoreloc1 = machoreloc1
- if ld.Thelinkarch == &ld.Linkppc64le {
+ if ld.SysArch == sys.ArchPPC64LE {
ld.Thearch.Lput = ld.Lputl
ld.Thearch.Wput = ld.Wputl
ld.Thearch.Vput = ld.Vputl
@@ -150,7 +145,7 @@ func archinit() {
}
case obj.Hlinux: /* ppc64 elf */
- if ld.Thestring == "ppc64" {
+ if ld.SysArch == sys.ArchPPC64 {
ld.Debug['d'] = 1 // TODO(austin): ELF ABI v1 not supported yet
}
ld.Elfinit()
diff --git a/src/cmd/link/internal/s390x/asm.go b/src/cmd/link/internal/s390x/asm.go
index 75a8206174..7c2e3358ff 100644
--- a/src/cmd/link/internal/s390x/asm.go
+++ b/src/cmd/link/internal/s390x/asm.go
@@ -90,12 +90,7 @@ func gentext() {
// undef (for debugging)
ld.Adduint32(ld.Ctxt, initfunc, 0)
- if ld.Ctxt.Etextp != nil {
- ld.Ctxt.Etextp.Next = initfunc
- } else {
- ld.Ctxt.Textp = initfunc
- }
- ld.Ctxt.Etextp = initfunc
+ ld.Ctxt.Textp = append(ld.Ctxt.Textp, initfunc)
initarray_entry := ld.Linklookup(ld.Ctxt, "go.link.addmoduledatainit", 0)
initarray_entry.Attr |= ld.AttrLocal
initarray_entry.Attr |= ld.AttrReachable
@@ -505,7 +500,7 @@ func addgotsym(s *ld.LSym) {
func asmb() {
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime())
+ fmt.Fprintf(ld.Bso, "%5.2f asmb\n", obj.Cputime())
}
ld.Bso.Flush()
@@ -523,7 +518,7 @@ func asmb() {
if ld.Segrodata.Filelen > 0 {
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime())
+ fmt.Fprintf(ld.Bso, "%5.2f rodatblk\n", obj.Cputime())
}
ld.Bso.Flush()
@@ -532,13 +527,16 @@ func asmb() {
}
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime())
+ 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))
+ ld.Cseek(int64(ld.Segdwarf.Fileoff))
+ ld.Dwarfblk(int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+
/* output symbol table */
ld.Symsize = 0
@@ -549,24 +547,23 @@ func asmb() {
ld.Diag("unsupported executable format")
}
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime())
+ fmt.Fprintf(ld.Bso, "%5.2f sym\n", obj.Cputime())
}
ld.Bso.Flush()
- symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen)
+ symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND)))
ld.Cseek(int64(symo))
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f elfsym\n", obj.Cputime())
+ 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())
+ fmt.Fprintf(ld.Bso, "%5.2f dwarf\n", obj.Cputime())
}
- ld.Dwarfemitdebugsections()
if ld.Linkmode == ld.LinkExternal {
ld.Elfemitreloc()
@@ -575,7 +572,7 @@ func asmb() {
ld.Ctxt.Cursym = nil
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f header\n", obj.Cputime())
+ fmt.Fprintf(ld.Bso, "%5.2f header\n", obj.Cputime())
}
ld.Bso.Flush()
ld.Cseek(0)
diff --git a/src/cmd/link/internal/s390x/l.go b/src/cmd/link/internal/s390x/l.go
index 839a9849c8..42cf15ee85 100644
--- a/src/cmd/link/internal/s390x/l.go
+++ b/src/cmd/link/internal/s390x/l.go
@@ -62,14 +62,9 @@ package s390x
// THE SOFTWARE.
const (
- thechar = 'z'
- PtrSize = 8
- IntSize = 8
- RegSize = 8
MaxAlign = 32 // max data alignment
MinAlign = 2 // min data alignment
FuncAlign = 16
- MINLC = 2
)
/* Used by ../internal/ld/dwarf.go */
diff --git a/src/cmd/link/internal/s390x/obj.go b/src/cmd/link/internal/s390x/obj.go
index ef88d22bbd..fdb9898181 100644
--- a/src/cmd/link/internal/s390x/obj.go
+++ b/src/cmd/link/internal/s390x/obj.go
@@ -32,6 +32,7 @@ package s390x
import (
"cmd/internal/obj"
+ "cmd/internal/sys"
"cmd/link/internal/ld"
"fmt"
)
@@ -44,17 +45,11 @@ func Main() {
}
func linkarchinit() {
- ld.Thestring = obj.Getgoarch()
- ld.Thelinkarch = &ld.Links390x
+ ld.SysArch = sys.ArchS390X
- 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.Minalign = MinAlign
- ld.Thearch.Minlc = MINLC
ld.Thearch.Dwarfregsp = DWARFREGSP
ld.Thearch.Dwarfreglr = DWARFREGLR
diff --git a/src/cmd/link/internal/x86/asm.go b/src/cmd/link/internal/x86/asm.go
index 97fccf3ee6..4a55b535ac 100644
--- a/src/cmd/link/internal/x86/asm.go
+++ b/src/cmd/link/internal/x86/asm.go
@@ -69,12 +69,7 @@ func gentext() {
// c3 ret
o(0xc3)
- if ld.Ctxt.Etextp != nil {
- ld.Ctxt.Etextp.Next = thunkfunc
- } else {
- ld.Ctxt.Textp = thunkfunc
- }
- ld.Ctxt.Etextp = thunkfunc
+ ld.Ctxt.Textp = append(ld.Ctxt.Textp, thunkfunc)
addmoduledata := ld.Linklookup(ld.Ctxt, "runtime.addmoduledata", 0)
if addmoduledata.Type == obj.STEXT {
@@ -130,8 +125,7 @@ func gentext() {
o(0xc3)
- ld.Ctxt.Etextp.Next = initfunc
- ld.Ctxt.Etextp = initfunc
+ ld.Ctxt.Textp = append(ld.Ctxt.Textp, initfunc)
initarray_entry := ld.Linklookup(ld.Ctxt, "go.link.addmoduledatainit", 0)
initarray_entry.Attr |= ld.AttrReachable
initarray_entry.Attr |= ld.AttrLocal
@@ -139,10 +133,6 @@ func gentext() {
ld.Addaddr(ld.Ctxt, initarray_entry, initfunc)
}
-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
@@ -292,7 +282,7 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
return
}
- if ld.HEADTYPE == obj.Hdarwin && s.Size == PtrSize && r.Off == 0 {
+ if ld.HEADTYPE == obj.Hdarwin && s.Size == int64(ld.SysArch.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.
@@ -317,7 +307,7 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
return
}
- if ld.HEADTYPE == obj.Hwindows && s.Size == PtrSize {
+ if ld.HEADTYPE == obj.Hwindows && s.Size == int64(ld.SysArch.PtrSize) {
// nothing to do, the relocation will be laid out in pereloc1
return
}
@@ -609,7 +599,7 @@ func addgotsym(ctxt *ld.Link, s *ld.LSym) {
func asmb() {
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime())
+ fmt.Fprintf(ld.Bso, "%5.2f asmb\n", obj.Cputime())
}
ld.Bso.Flush()
@@ -627,7 +617,7 @@ func asmb() {
if ld.Segrodata.Filelen > 0 {
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime())
+ fmt.Fprintf(ld.Bso, "%5.2f rodatblk\n", obj.Cputime())
}
ld.Bso.Flush()
@@ -636,26 +626,18 @@ func asmb() {
}
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime())
+ 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))
+ ld.Cseek(int64(ld.Segdwarf.Fileoff))
+ ld.Dwarfblk(int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.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())
}
@@ -666,13 +648,13 @@ func asmb() {
if ld.Debug['s'] == 0 {
// TODO: rationalize
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime())
+ 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.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND)))
}
@@ -683,7 +665,7 @@ func asmb() {
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.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
symo = uint32(ld.Rnd(int64(symo), ld.PEFILEALIGN))
}
@@ -692,17 +674,12 @@ func asmb() {
default:
if ld.Iself {
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f elfsym\n", obj.Cputime())
+ 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()
}
@@ -716,7 +693,7 @@ func asmb() {
if sym != nil {
ld.Lcsize = int32(len(sym.P))
for i := 0; int32(i) < ld.Lcsize; i++ {
- ld.Cput(uint8(sym.P[i]))
+ ld.Cput(sym.P[i])
}
ld.Cflush()
@@ -724,9 +701,8 @@ func asmb() {
case obj.Hwindows:
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime())
+ fmt.Fprintf(ld.Bso, "%5.2f dwarf\n", obj.Cputime())
}
- ld.Dwarfemitdebugsections()
case obj.Hdarwin:
if ld.Linkmode == ld.LinkExternal {
@@ -736,7 +712,7 @@ func asmb() {
}
if ld.Debug['v'] != 0 {
- fmt.Fprintf(&ld.Bso, "%5.2f headr\n", obj.Cputime())
+ fmt.Fprintf(ld.Bso, "%5.2f headr\n", obj.Cputime())
}
ld.Bso.Flush()
ld.Cseek(0)
diff --git a/src/cmd/link/internal/x86/l.go b/src/cmd/link/internal/x86/l.go
index 068fed9c8d..2043f9bb4e 100644
--- a/src/cmd/link/internal/x86/l.go
+++ b/src/cmd/link/internal/x86/l.go
@@ -31,12 +31,9 @@
package x86
const (
- thechar = '8'
- PtrSize = 4
MaxAlign = 32 // max data alignment
MinAlign = 1 // min data alignment
FuncAlign = 16
- MINLC = 1
)
/* Used by ../internal/ld/dwarf.go */
diff --git a/src/cmd/link/internal/x86/obj.go b/src/cmd/link/internal/x86/obj.go
index 4380c41ebb..574c0dad2d 100644
--- a/src/cmd/link/internal/x86/obj.go
+++ b/src/cmd/link/internal/x86/obj.go
@@ -32,6 +32,7 @@ package x86
import (
"cmd/internal/obj"
+ "cmd/internal/sys"
"cmd/link/internal/ld"
"fmt"
"log"
@@ -45,17 +46,11 @@ func Main() {
}
func linkarchinit() {
- ld.Thestring = "386"
- ld.Thelinkarch = &ld.Link386
+ ld.SysArch = sys.Arch386
- 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.Minalign = MinAlign
- ld.Thearch.Minlc = MINLC
ld.Thearch.Dwarfregsp = DWARFREGSP
ld.Thearch.Dwarfreglr = DWARFREGLR
diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go
new file mode 100644
index 0000000000..4ef184518e
--- /dev/null
+++ b/src/cmd/link/link_test.go
@@ -0,0 +1,30 @@
+package main
+
+import "testing"
+
+var AuthorPaidByTheColumnInch struct {
+ fog int `
+ London. Michaelmas term lately over, and the Lord Chancellor sitting in Lincoln’s Inn Hall. Implacable November weather. As much mud in the streets as if the waters had but newly retired from the face of the earth, and it would not be wonderful to meet a Megalosaurus, forty feet long or so, waddling like an elephantine lizard up Holborn Hill. Smoke lowering down from chimney-pots, making a soft black drizzle, with flakes of soot in it as big as full-grown snowflakes—gone into mourning, one might imagine, for the death of the sun. Dogs, undistinguishable in mire. Horses, scarcely better; splashed to their very blinkers. Foot passengers, jostling one another’s umbrellas in a general infection of ill temper, and losing their foot-hold at street-corners, where tens of thousands of other foot passengers have been slipping and sliding since the day broke (if this day ever broke), adding new deposits to the crust upon crust of mud, sticking at those points tenaciously to the pavement, and accumulating at compound interest.
+
+ Fog everywhere. Fog up the river, where it flows among green aits and meadows; fog down the river, where it rolls defiled among the tiers of shipping and the waterside pollutions of a great (and dirty) city. Fog on the Essex marshes, fog on the Kentish heights. Fog creeping into the cabooses of collier-brigs; fog lying out on the yards and hovering in the rigging of great ships; fog drooping on the gunwales of barges and small boats. Fog in the eyes and throats of ancient Greenwich pensioners, wheezing by the firesides of their wards; fog in the stem and bowl of the afternoon pipe of the wrathful skipper, down in his close cabin; fog cruelly pinching the toes and fingers of his shivering little ‘prentice boy on deck. Chance people on the bridges peeping over the parapets into a nether sky of fog, with fog all round them, as if they were up in a balloon and hanging in the misty clouds.
+
+ Gas looming through the fog in divers places in the streets, much as the sun may, from the spongey fields, be seen to loom by husbandman and ploughboy. Most of the shops lighted two hours before their time—as the gas seems to know, for it has a haggard and unwilling look.
+
+ The raw afternoon is rawest, and the dense fog is densest, and the muddy streets are muddiest near that leaden-headed old obstruction, appropriate ornament for the threshold of a leaden-headed old corporation, Temple Bar. And hard by Temple Bar, in Lincoln’s Inn Hall, at the very heart of the fog, sits the Lord High Chancellor in his High Court of Chancery.`
+
+ wind int `
+ It was grand to see how the wind awoke, and bent the trees, and drove the rain before it like a cloud of smoke; and to hear the solemn thunder, and to see the lightning; and while thinking with awe of the tremendous powers by which our little lives are encompassed, to consider how beneficent they are, and how upon the smallest flower and leaf there was already a freshness poured from all this seeming rage, which seemed to make creation new again.`
+
+ jarndyce int `
+ Jarndyce and Jarndyce drones on. This scarecrow of a suit has, over the course of time, become so complicated, that no man alive knows what it means. The parties to it understand it least; but it has been observed that no two Chancery lawyers can talk about it for five minutes, without coming to a total disagreement as to all the premises. Innumerable children have been born into the cause; innumerable young people have married into it; innumerable old people have died out of it. Scores of persons have deliriously found themselves made parties in Jarndyce and Jarndyce, without knowing how or why; whole families have inherited legendary hatreds with the suit. The little plaintiff or defendant, who was promised a new rocking-horse when Jarndyce and Jarndyce should be settled, has grown up, possessed himself of a real horse, and trotted away into the other world. Fair wards of court have faded into mothers and grandmothers; a long procession of Chancellors has come in and gone out; the legion of bills in the suit have been transformed into mere bills of mortality; there are not three Jarndyces left upon the earth perhaps, since old Tom Jarndyce in despair blew his brains out at a coffee-house in Chancery Lane; but Jarndyce and Jarndyce still drags its dreary length before the Court, perennially hopeless.`
+
+ principle int `
+ The one great principle of the English law is, to make business for itself. There is no other principle distinctly, certainly, and consistently maintained through all its narrow turnings. Viewed by this light it becomes a coherent scheme, and not the monstrous maze the laity are apt to think it. Let them but once clearly perceive that its grand principle is to make business for itself at their expense, and surely they will cease to grumble.`
+}
+
+func TestLargeSymName(t *testing.T) {
+ // The compiler generates a symbol name using the string form of the
+ // type. This tests that the linker can read symbol names larger than
+ // the bufio buffer. Issue #15104.
+ _ = AuthorPaidByTheColumnInch
+}
diff --git a/src/cmd/link/main.go b/src/cmd/link/main.go
index e52b718699..f92e02eac3 100644
--- a/src/cmd/link/main.go
+++ b/src/cmd/link/main.go
@@ -11,6 +11,7 @@ import (
"cmd/link/internal/arm64"
"cmd/link/internal/mips64"
"cmd/link/internal/ppc64"
+ "cmd/link/internal/s390x"
"cmd/link/internal/x86"
"fmt"
"os"
@@ -33,5 +34,7 @@ func main() {
mips64.Main()
case "ppc64", "ppc64le":
ppc64.Main()
+ case "s390x":
+ s390x.Main()
}
}
diff --git a/src/cmd/objdump/objdump_test.go b/src/cmd/objdump/objdump_test.go
index 8ceaba078c..899db06324 100644
--- a/src/cmd/objdump/objdump_test.go
+++ b/src/cmd/objdump/objdump_test.go
@@ -107,6 +107,8 @@ func TestDisasm(t *testing.T) {
t.Skipf("skipping on %s, issue 10106", runtime.GOARCH)
case "mips64", "mips64le":
t.Skipf("skipping on %s, issue 12559", runtime.GOARCH)
+ case "s390x":
+ t.Skipf("skipping on %s, issue 15255", runtime.GOARCH)
}
testDisasm(t)
}
@@ -123,6 +125,8 @@ func TestDisasmExtld(t *testing.T) {
t.Skipf("skipping on %s, issue 10106", runtime.GOARCH)
case "mips64", "mips64le":
t.Skipf("skipping on %s, issue 12559 and 12560", runtime.GOARCH)
+ case "s390x":
+ t.Skipf("skipping on %s, issue 15255", runtime.GOARCH)
}
// TODO(jsing): Reenable once openbsd/arm has external linking support.
if runtime.GOOS == "openbsd" && runtime.GOARCH == "arm" {
diff --git a/src/cmd/pprof/pprof.go b/src/cmd/pprof/pprof.go
index 1c55d05d5d..bce37dcb97 100644
--- a/src/cmd/pprof/pprof.go
+++ b/src/cmd/pprof/pprof.go
@@ -15,13 +15,13 @@ import (
"sync"
"cmd/internal/objfile"
- "cmd/pprof/internal/commands"
- "cmd/pprof/internal/driver"
- "cmd/pprof/internal/fetch"
- "cmd/pprof/internal/plugin"
- "cmd/pprof/internal/profile"
- "cmd/pprof/internal/symbolizer"
- "cmd/pprof/internal/symbolz"
+ "cmd/internal/pprof/commands"
+ "cmd/internal/pprof/driver"
+ "cmd/internal/pprof/fetch"
+ "cmd/internal/pprof/plugin"
+ "cmd/internal/pprof/profile"
+ "cmd/internal/pprof/symbolizer"
+ "cmd/internal/pprof/symbolz"
)
func main() {
diff --git a/src/cmd/trace/main.go b/src/cmd/trace/main.go
index e493be91b7..cfd222e132 100644
--- a/src/cmd/trace/main.go
+++ b/src/cmd/trace/main.go
@@ -99,16 +99,11 @@ func parseEvents() ([]*trace.Event, error) {
defer tracef.Close()
// Parse and symbolize.
- events, err := trace.Parse(bufio.NewReader(tracef))
+ events, err := trace.Parse(bufio.NewReader(tracef), programBinary)
if err != nil {
loader.err = fmt.Errorf("failed to parse trace: %v", err)
return
}
- err = trace.Symbolize(events, programBinary)
- if err != nil {
- loader.err = fmt.Errorf("failed to symbolize trace: %v", err)
- return
- }
loader.events = events
})
return loader.events, loader.err
diff --git a/src/cmd/trace/pprof.go b/src/cmd/trace/pprof.go
index eaddf63ee8..fdda6d89be 100644
--- a/src/cmd/trace/pprof.go
+++ b/src/cmd/trace/pprof.go
@@ -8,6 +8,7 @@ package main
import (
"bufio"
+ "cmd/internal/pprof/profile"
"fmt"
"internal/trace"
"io/ioutil"
@@ -133,34 +134,79 @@ func serveSVGProfile(w http.ResponseWriter, r *http.Request, prof map[uint64]Rec
http.Error(w, fmt.Sprintf("failed to create temp file: %v", err), http.StatusInternalServerError)
return
}
- defer os.Remove(blockf.Name())
+ defer func() {
+ blockf.Close()
+ os.Remove(blockf.Name())
+ }()
blockb := bufio.NewWriter(blockf)
- fmt.Fprintf(blockb, "--- contention:\ncycles/second=1000000000\n")
- for _, rec := range prof {
- fmt.Fprintf(blockb, "%v %v @", rec.time, rec.n)
- for _, f := range rec.stk {
- fmt.Fprintf(blockb, " 0x%x", f.PC)
- }
- fmt.Fprintf(blockb, "\n")
+ if err := buildProfile(prof).Write(blockb); err != nil {
+ http.Error(w, fmt.Sprintf("failed to write profile: %v", err), http.StatusInternalServerError)
+ return
}
- err = blockb.Flush()
- if err != nil {
+ if err := blockb.Flush(); err != nil {
http.Error(w, fmt.Sprintf("failed to flush temp file: %v", err), http.StatusInternalServerError)
return
}
- err = blockf.Close()
- if err != nil {
+ if err := blockf.Close(); err != nil {
http.Error(w, fmt.Sprintf("failed to close temp file: %v", err), http.StatusInternalServerError)
return
}
-
svgFilename := blockf.Name() + ".svg"
- _, err = exec.Command("go", "tool", "pprof", "-svg", "-output", svgFilename, programBinary, blockf.Name()).CombinedOutput()
- if err != nil {
- http.Error(w, fmt.Sprintf("failed to execute go tool pprof: %v", err), http.StatusInternalServerError)
+ if output, err := exec.Command("go", "tool", "pprof", "-svg", "-output", svgFilename, blockf.Name()).CombinedOutput(); err != nil {
+ http.Error(w, fmt.Sprintf("failed to execute go tool pprof: %v\n%s", err, output), http.StatusInternalServerError)
return
}
defer os.Remove(svgFilename)
w.Header().Set("Content-Type", "image/svg+xml")
http.ServeFile(w, r, svgFilename)
}
+
+func buildProfile(prof map[uint64]Record) *profile.Profile {
+ p := &profile.Profile{
+ PeriodType: &profile.ValueType{Type: "trace", Unit: "count"},
+ Period: 1,
+ SampleType: []*profile.ValueType{
+ {Type: "contentions", Unit: "count"},
+ {Type: "delay", Unit: "nanoseconds"},
+ },
+ }
+ locs := make(map[uint64]*profile.Location)
+ funcs := make(map[string]*profile.Function)
+ for _, rec := range prof {
+ var sloc []*profile.Location
+ for _, frame := range rec.stk {
+ loc := locs[frame.PC]
+ if loc == nil {
+ fn := funcs[frame.File+frame.Fn]
+ if fn == nil {
+ fn = &profile.Function{
+ ID: uint64(len(p.Function) + 1),
+ Name: frame.Fn,
+ SystemName: frame.Fn,
+ Filename: frame.File,
+ }
+ p.Function = append(p.Function, fn)
+ funcs[frame.File+frame.Fn] = fn
+ }
+ loc = &profile.Location{
+ ID: uint64(len(p.Location) + 1),
+ Address: frame.PC,
+ Line: []profile.Line{
+ profile.Line{
+ Function: fn,
+ Line: int64(frame.Line),
+ },
+ },
+ }
+ p.Location = append(p.Location, loc)
+ locs[frame.PC] = loc
+ }
+ sloc = append(sloc, loc)
+ }
+ p.Sample = append(p.Sample, &profile.Sample{
+ Value: []int64{int64(rec.n), rec.time},
+ Location: sloc,
+ })
+ }
+ return p
+}
diff --git a/src/cmd/trace/trace.go b/src/cmd/trace/trace.go
index fff732b856..7782a5efc8 100644
--- a/src/cmd/trace/trace.go
+++ b/src/cmd/trace/trace.go
@@ -44,19 +44,88 @@ func httpTrace(w http.ResponseWriter, r *http.Request) {
}
+// See https://github.com/catapult-project/catapult/blob/master/tracing/docs/embedding-trace-viewer.md
+// This is almost verbatim copy of:
+// https://github.com/catapult-project/catapult/blob/master/tracing/bin/index.html
+// on revision 623a005a3ffa9de13c4b92bc72290e7bcd1ca591.
var templTrace = `
<html>
- <head>
- <link href="/trace_viewer_html" rel="import">
- <script>
- document.addEventListener("DOMContentLoaded", function(event) {
- var viewer = new tr.TraceViewer('/jsontrace{{PARAMS}}');
- document.body.appendChild(viewer);
- });
- </script>
- </head>
- <body>
- </body>
+<head>
+<link href="/trace_viewer_html" rel="import">
+<script>
+(function() {
+ var viewer;
+ var url;
+ var model;
+
+ function load() {
+ var req = new XMLHttpRequest();
+ var is_binary = /[.]gz$/.test(url) || /[.]zip$/.test(url);
+ req.overrideMimeType('text/plain; charset=x-user-defined');
+ req.open('GET', url, true);
+ if (is_binary)
+ req.responseType = 'arraybuffer';
+
+ req.onreadystatechange = function(event) {
+ if (req.readyState !== 4)
+ return;
+
+ window.setTimeout(function() {
+ if (req.status === 200)
+ onResult(is_binary ? req.response : req.responseText);
+ else
+ onResultFail(req.status);
+ }, 0);
+ };
+ req.send(null);
+ }
+
+ function onResultFail(err) {
+ var overlay = new tr.ui.b.Overlay();
+ overlay.textContent = err + ': ' + url + ' could not be loaded';
+ overlay.title = 'Failed to fetch data';
+ overlay.visible = true;
+ }
+
+ function onResult(result) {
+ model = new tr.Model();
+ var i = new tr.importer.Import(model);
+ var p = i.importTracesWithProgressDialog([result]);
+ p.then(onModelLoaded, onImportFail);
+ }
+
+ function onModelLoaded() {
+ viewer.model = model;
+ viewer.viewTitle = "trace";
+ }
+
+ function onImportFail() {
+ var overlay = new tr.ui.b.Overlay();
+ overlay.textContent = tr.b.normalizeException(err).message;
+ overlay.title = 'Import error';
+ overlay.visible = true;
+ }
+
+ document.addEventListener('DOMContentLoaded', function() {
+ var container = document.createElement('track-view-container');
+ container.id = 'track_view_container';
+
+ viewer = document.createElement('tr-ui-timeline-view');
+ viewer.track_view_container = container;
+ viewer.appendChild(container);
+
+ viewer.id = 'trace-viewer';
+ viewer.globalMode = true;
+ document.body.appendChild(viewer);
+
+ url = '/jsontrace{{PARAMS}}';
+ load();
+ });
+}());
+</script>
+</head>
+<body>
+</body>
</html>
`
diff --git a/src/cmd/vet/asmdecl.go b/src/cmd/vet/asmdecl.go
index 1b313cfe1b..389da353fc 100644
--- a/src/cmd/vet/asmdecl.go
+++ b/src/cmd/vet/asmdecl.go
@@ -559,6 +559,11 @@ func asmCheckVar(badf func(string, ...interface{}), fn *asmFunc, line, expr stri
src = 8
break
}
+ if strings.HasPrefix(op, "P") && strings.HasSuffix(op, "RD") {
+ // PINSRD, PEXTRD, etc
+ src = 4
+ break
+ }
if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "F") || strings.HasSuffix(op, "FP")) {
// FMOVFP, FXCHF, etc
src = 4
diff --git a/src/cmd/vet/atomic.go b/src/cmd/vet/atomic.go
index c084f13ab3..b2ca2d80f3 100644
--- a/src/cmd/vet/atomic.go
+++ b/src/cmd/vet/atomic.go
@@ -23,6 +23,9 @@ func checkAtomicAssignment(f *File, node ast.Node) {
if len(n.Lhs) != len(n.Rhs) {
return
}
+ if len(n.Lhs) == 1 && n.Tok == token.DEFINE {
+ return
+ }
for i, right := range n.Rhs {
call, ok := right.(*ast.CallExpr)
diff --git a/src/cmd/vet/composite.go b/src/cmd/vet/composite.go
index 731c793eac..ac6a598b0b 100644
--- a/src/cmd/vet/composite.go
+++ b/src/cmd/vet/composite.go
@@ -68,8 +68,10 @@ func checkUnkeyedLiteral(f *File, node ast.Node) {
allKeyValue := true
for _, e := range c.Elts {
if _, ok := e.(*ast.KeyValueExpr); !ok {
- allKeyValue = false
- break
+ if cl, ok := e.(*ast.CompositeLit); !ok || cl.Type != nil {
+ allKeyValue = false
+ break
+ }
}
}
if allKeyValue {
diff --git a/src/cmd/vet/doc.go b/src/cmd/vet/doc.go
index d295fb4345..c697f3bc36 100644
--- a/src/cmd/vet/doc.go
+++ b/src/cmd/vet/doc.go
@@ -29,11 +29,10 @@ check every possible problem and depends on unreliable heuristics
so it should be used as guidance only, not as a firm indicator of
program correctness.
-By default all checks are performed. If any flags are explicitly set
-to true, only those tests are run. Conversely, if any flag is
-explicitly set to false, only those tests are disabled.
-Thus -printf=true runs the printf check, -printf=false runs all checks
-except the printf check.
+By default the -all flag is set so all checks are performed.
+If any flags are explicitly set to true, only those tests are run. Conversely, if
+any flag is explicitly set to false, only those tests are disabled. Thus -printf=true
+runs the printf check, -printf=false runs all checks except the printf check.
Available checks:
@@ -188,15 +187,10 @@ These flags configure the behavior of vet:
-v
Verbose mode
-printfuncs
- A comma-separated list of print-like functions to supplement the
- standard list. Each entry is in the form Name:N where N is the
- zero-based argument position of the first argument involved in the
- print: either the format or the first print argument for non-formatted
- prints. For example, if you have Warn and Warnf functions that
- take an io.Writer as their first argument, like Fprintf,
- -printfuncs=Warn:1,Warnf:1
+ A comma-separated list of print-like function names
+ to supplement the standard list.
For more information, see the discussion of the -printf flag.
-shadowstrict
Whether to be strict about shadowing; can be noisy.
*/
-package main // import "golang.org/x/tools/cmd/vet"
+package main
diff --git a/src/cmd/vet/internal/whitelist/whitelist.go b/src/cmd/vet/internal/whitelist/whitelist.go
index b6c85850f3..696f7a533d 100644
--- a/src/cmd/vet/internal/whitelist/whitelist.go
+++ b/src/cmd/vet/internal/whitelist/whitelist.go
@@ -11,7 +11,8 @@ package whitelist
// library's exported slice types.
var UnkeyedLiteral = map[string]bool{
/*
- find $GOROOT/src -type f | grep -v _test.go | xargs grep '^type.*\[\]' | \
+ find $GOROOT/src -type f | grep -v _test.go | grep -v /internal/ | grep -v /testdata/ | \
+ xargs grep '^type.*\[\]' | grep -v ' func(' | \
grep -v ' map\[' | sed 's,/[^/]*go.type,,' | sed 's,.*src/,,' | \
sed 's, ,.,' | sed 's, .*,,' | grep -v '\.[a-z]' | \
sort | awk '{ print "\"" $0 "\": true," }'
diff --git a/src/cmd/vet/main.go b/src/cmd/vet/main.go
index a2142dcabb..8212a14f03 100644
--- a/src/cmd/vet/main.go
+++ b/src/cmd/vet/main.go
@@ -100,7 +100,7 @@ func (ts *triState) Set(value string) error {
func (ts *triState) String() string {
switch *ts {
case unset:
- return "unset"
+ return "true" // An unset flag will be set by -all, so defaults to true.
case setTrue:
return "true"
case setFalse:
@@ -164,6 +164,7 @@ func Usage() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
fmt.Fprintf(os.Stderr, "\tvet [flags] directory...\n")
fmt.Fprintf(os.Stderr, "\tvet [flags] files... # Must be a single package\n")
+ fmt.Fprintf(os.Stderr, "By default, -all is set and all non-experimental checks are run.\n")
fmt.Fprintf(os.Stderr, "For more information run\n")
fmt.Fprintf(os.Stderr, "\tgo doc cmd/vet\n\n")
fmt.Fprintf(os.Stderr, "Flags:\n")
diff --git a/src/cmd/vet/print.go b/src/cmd/vet/print.go
index 4e3252f2fb..07499e6ae6 100644
--- a/src/cmd/vet/print.go
+++ b/src/cmd/vet/print.go
@@ -35,20 +35,18 @@ func initPrintFlags() {
if len(name) == 0 {
flag.Usage()
}
- skip := 0
+
+ // Backwards compatibility: skip optional first argument
+ // index after the colon.
if colon := strings.LastIndex(name, ":"); colon > 0 {
- var err error
- skip, err = strconv.Atoi(name[colon+1:])
- if err != nil {
- errorf(`illegal format for "Func:N" argument %q; %s`, name, err)
- }
name = name[:colon]
}
+
name = strings.ToLower(name)
if name[len(name)-1] == 'f' {
isFormattedPrint[name] = true
} else {
- printList[name] = skip
+ isPrint[name] = true
}
}
}
@@ -65,17 +63,20 @@ var isFormattedPrint = map[string]bool{
"sprintf": true,
}
-// printList records the unformatted-print functions. The value is the location
-// of the first parameter to be printed. Names are lower-cased so the lookup is
-// case insensitive.
-var printList = map[string]int{
- "error": 0,
- "fatal": 0,
- "fprint": 1, "fprintln": 1,
- "log": 0,
- "panic": 0, "panicln": 0,
- "print": 0, "println": 0,
- "sprint": 0, "sprintln": 0,
+// isPrint records the unformatted-print functions. Names are lower-cased
+// so the lookup is case insensitive.
+var isPrint = map[string]bool{
+ "error": true,
+ "fatal": true,
+ "fprint": true,
+ "fprintln": true,
+ "log": true,
+ "panic": true,
+ "panicln": true,
+ "print": true,
+ "println": true,
+ "sprint": true,
+ "sprintln": true,
}
// formatString returns the format string argument and its index within
@@ -171,8 +172,8 @@ func checkFmtPrintfCall(f *File, node ast.Node) {
f.checkPrintf(call, Name)
return
}
- if skip, ok := printList[name]; ok {
- f.checkPrint(call, Name, skip)
+ if _, ok := isPrint[name]; ok {
+ f.checkPrint(call, Name)
return
}
}
@@ -583,25 +584,36 @@ func (f *File) argCanBeChecked(call *ast.CallExpr, formatArg int, isStar bool, s
}
// checkPrint checks a call to an unformatted print routine such as Println.
-// call.Args[firstArg] is the first argument to be printed.
-func (f *File) checkPrint(call *ast.CallExpr, name string, firstArg int) {
- isLn := strings.HasSuffix(name, "ln")
- isF := strings.HasPrefix(name, "F")
- args := call.Args
- if name == "Log" && len(args) > 0 {
- // Special case: Don't complain about math.Log or cmplx.Log.
- // Not strictly necessary because the only complaint likely is for Log("%d")
- // but it feels wrong to check that math.Log is a good print function.
- if sel, ok := args[0].(*ast.SelectorExpr); ok {
- if x, ok := sel.X.(*ast.Ident); ok {
- if x.Name == "math" || x.Name == "cmplx" {
- return
- }
+func (f *File) checkPrint(call *ast.CallExpr, name string) {
+ firstArg := 0
+ typ := f.pkg.types[call.Fun].Type
+ if typ != nil {
+ if sig, ok := typ.(*types.Signature); ok {
+ if !sig.Variadic() {
+ // Skip checking non-variadic functions.
+ return
+ }
+ params := sig.Params()
+ firstArg = params.Len() - 1
+
+ typ := params.At(firstArg).Type()
+ typ = typ.(*types.Slice).Elem()
+ it, ok := typ.(*types.Interface)
+ if !ok || !it.Empty() {
+ // Skip variadic functions accepting non-interface{} args.
+ return
}
}
}
+ args := call.Args
+ if len(args) <= firstArg {
+ // Skip calls without variadic args.
+ return
+ }
+ args = args[firstArg:]
+
// check for Println(os.Stderr, ...)
- if firstArg == 0 && !isF && len(args) > 0 {
+ if firstArg == 0 {
if sel, ok := args[0].(*ast.SelectorExpr); ok {
if x, ok := sel.X.(*ast.Ident); ok {
if x.Name == "os" && strings.HasPrefix(sel.Sel.Name, "Std") {
@@ -610,31 +622,15 @@ func (f *File) checkPrint(call *ast.CallExpr, name string, firstArg int) {
}
}
}
- if len(args) <= firstArg {
- // If we have a call to a method called Error that satisfies the Error interface,
- // then it's ok. Otherwise it's something like (*T).Error from the testing package
- // and we need to check it.
- if name == "Error" && f.isErrorMethodCall(call) {
- return
- }
- // If it's an Error call now, it's probably for printing errors.
- if !isLn {
- // Check the signature to be sure: there are niladic functions called "error".
- if firstArg != 0 || f.numArgsInSignature(call) != firstArg {
- f.Badf(call.Pos(), "no args in %s call", name)
- }
- }
- return
- }
- arg := args[firstArg]
+ arg := args[0]
if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
if strings.Contains(lit.Value, "%") {
f.Badf(call.Pos(), "possible formatting directive in %s call", name)
}
}
- if isLn {
+ if strings.HasSuffix(name, "ln") {
// The last item, if a string, should not have a newline.
- arg = args[len(call.Args)-1]
+ arg = args[len(args)-1]
if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
if strings.HasSuffix(lit.Value, `\n"`) {
f.Badf(call.Pos(), "%s call ends with newline", name)
diff --git a/src/cmd/vet/structtag.go b/src/cmd/vet/structtag.go
index e8164a46f9..abff14fb1d 100644
--- a/src/cmd/vet/structtag.go
+++ b/src/cmd/vet/structtag.go
@@ -111,7 +111,7 @@ func validateStructTag(tag string) error {
if i >= len(tag) {
return errTagValueSyntax
}
- qvalue := string(tag[:i+1])
+ qvalue := tag[:i+1]
tag = tag[i+1:]
if _, err := strconv.Unquote(qvalue); err != nil {
diff --git a/src/cmd/vet/testdata/asm.go b/src/cmd/vet/testdata/asm.go
index 9a3d5315ad..81947102ec 100644
--- a/src/cmd/vet/testdata/asm.go
+++ b/src/cmd/vet/testdata/asm.go
@@ -31,3 +31,5 @@ func nosplit(x int)
func rodata(x int)
func noptr(x int)
func wrapper(x int)
+
+func f15271() (x uint32)
diff --git a/src/cmd/vet/testdata/asm1.s b/src/cmd/vet/testdata/asm1.s
index 62f423cd8b..2c6f13b137 100644
--- a/src/cmd/vet/testdata/asm1.s
+++ b/src/cmd/vet/testdata/asm1.s
@@ -252,3 +252,14 @@ TEXT ·returnnamed(SB),0,$0-41
TEXT ·returnintmissing(SB),0,$0-8
RET // ERROR "RET without writing to 8-byte ret\+0\(FP\)"
+
+
+// issue 15271
+TEXT ·f15271(SB), NOSPLIT, $0-4
+ // Stick 123 into the low 32 bits of X0.
+ MOVQ $123, AX
+ PINSRD $0, AX, X0
+
+ // Return them.
+ PEXTRD $0, X0, x+0(FP)
+ RET
diff --git a/src/cmd/vet/testdata/atomic.go b/src/cmd/vet/testdata/atomic.go
index 1ba261d941..d5a8e61184 100644
--- a/src/cmd/vet/testdata/atomic.go
+++ b/src/cmd/vet/testdata/atomic.go
@@ -40,4 +40,13 @@ func AtomicTests() {
*ap[1] = atomic.AddUint64(ap[0], 1)
x = atomic.AddUint64() // Used to make vet crash; now silently ignored.
+
+ {
+ // A variable declaration creates a new variable in the current scope.
+ x := atomic.AddUint64(&x, 1) // ERROR "declaration of .x. shadows declaration at testdata/atomic.go:16"
+
+ // Re-declaration assigns a new value.
+ x, w := atomic.AddUint64(&x, 1), 10 // ERROR "direct assignment to atomic value"
+ _ = w
+ }
}
diff --git a/src/cmd/vet/testdata/composite.go b/src/cmd/vet/testdata/composite.go
index 69e7d7ccb0..0355c0b692 100644
--- a/src/cmd/vet/testdata/composite.go
+++ b/src/cmd/vet/testdata/composite.go
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// This file contains tests for the untagged struct literal checker.
-
// This file contains the test for untagged struct literals.
package testdata
@@ -11,6 +9,7 @@ package testdata
import (
"flag"
"go/scanner"
+ "unicode"
)
var Okay1 = []string{
@@ -57,6 +56,11 @@ var BadStructLiteralUsedInTests = flag.Flag{ // ERROR "unkeyed fields"
"DefValue",
}
+// SpecialCase is an (aptly named) slice of CaseRange to test issue 9171.
+var GoodNamedSliceLiteralUsedInTests = unicode.SpecialCase{
+ {Lo: 1, Hi: 2},
+}
+
// Used to test the check for slices and arrays: If that test is disabled and
// vet is run with --compositewhitelist=false, this line triggers an error.
// Clumsy but sufficient.
diff --git a/src/cmd/vet/testdata/print.go b/src/cmd/vet/testdata/print.go
index 5c7ff35c90..261ee788c7 100644
--- a/src/cmd/vet/testdata/print.go
+++ b/src/cmd/vet/testdata/print.go
@@ -185,11 +185,11 @@ func PrintfTests() {
// Something that looks like an error interface but isn't, such as the (*T).Error method
// in the testing package.
var et1 errorTest1
- fmt.Println(et1.Error()) // ERROR "no args in Error call"
+ fmt.Println(et1.Error()) // ok
fmt.Println(et1.Error("hi")) // ok
fmt.Println(et1.Error("%d", 3)) // ERROR "possible formatting directive in Error call"
var et2 errorTest2
- et2.Error() // ERROR "no args in Error call"
+ et2.Error() // ok
et2.Error("hi") // ok, not an error method.
et2.Error("%d", 3) // ERROR "possible formatting directive in Error call"
var et3 errorTest3
@@ -231,11 +231,41 @@ func PrintfTests() {
externalprintf.Logf(level, "%d", 42) // OK
externalprintf.Errorf(level, level, "foo %q bar", "foobar") // OK
externalprintf.Logf(level, "%d") // ERROR "format reads arg 1, have only 0 args"
+
+ // user-defined Println-like functions
+ ss := &someStruct{}
+ ss.Log(someFunction, "foo") // OK
+ ss.Error(someFunction, someFunction) // OK
+ ss.Println() // OK
+ ss.Println(1.234, "foo") // OK
+ ss.Println(1, someFunction) // ERROR "arg someFunction in Println call is a function value, not a function call"
+ ss.log(someFunction) // OK
+ ss.log(someFunction, "bar", 1.33) // OK
+ ss.log(someFunction, someFunction) // ERROR "arg someFunction in log call is a function value, not a function call"
}
+type someStruct struct{}
+
+// Log is non-variadic user-define Println-like function.
+// Calls to this func must be skipped when checking
+// for Println-like arguments.
+func (ss *someStruct) Log(f func(), s string) {}
+
+// Error is variadic user-define Println-like function.
+// Calls to this func mustn't be checked for Println-like arguments,
+// since variadic arguments type isn't interface{}.
+func (ss *someStruct) Error(args ...func()) {}
+
+// Println is variadic user-defined Println-like function.
+// Calls to this func must be checked for Println-like arguments.
+func (ss *someStruct) Println(args ...interface{}) {}
+
+// log is variadic user-defined Println-like function.
+// Calls to this func must be checked for Println-like arguments.
+func (ss *someStruct) log(f func(), args ...interface{}) {}
+
// A function we use as a function value; it has no other purpose.
-func someFunction() {
-}
+func someFunction() {}
// Printf is used by the test so we must declare it.
func Printf(format string, args ...interface{}) {
diff --git a/src/cmd/vet/types.go b/src/cmd/vet/types.go
index 692bae6192..4358955d93 100644
--- a/src/cmd/vet/types.go
+++ b/src/cmd/vet/types.go
@@ -292,72 +292,6 @@ func (f *File) matchStructArgType(t printfArgType, typ *types.Struct, arg ast.Ex
return true
}
-// numArgsInSignature tells how many formal arguments the function type
-// being called has.
-func (f *File) numArgsInSignature(call *ast.CallExpr) int {
- // Check the type of the function or method declaration
- typ := f.pkg.types[call.Fun].Type
- if typ == nil {
- return 0
- }
- // The type must be a signature, but be sure for safety.
- sig, ok := typ.(*types.Signature)
- if !ok {
- return 0
- }
- return sig.Params().Len()
-}
-
-// isErrorMethodCall reports whether the call is of a method with signature
-// func Error() string
-// where "string" is the universe's string type. We know the method is called "Error".
-func (f *File) isErrorMethodCall(call *ast.CallExpr) bool {
- typ := f.pkg.types[call].Type
- if typ != nil {
- // We know it's called "Error", so just check the function signature
- // (stringerType has exactly one method, String).
- if stringerType != nil && stringerType.NumMethods() == 1 {
- return types.Identical(f.pkg.types[call.Fun].Type, stringerType.Method(0).Type())
- }
- }
- // Without types, we can still check by hand.
- // Is it a selector expression? Otherwise it's a function call, not a method call.
- sel, ok := call.Fun.(*ast.SelectorExpr)
- if !ok {
- return false
- }
- // The package is type-checked, so if there are no arguments, we're done.
- if len(call.Args) > 0 {
- return false
- }
- // Check the type of the method declaration
- typ = f.pkg.types[sel].Type
- if typ == nil {
- return false
- }
- // The type must be a signature, but be sure for safety.
- sig, ok := typ.(*types.Signature)
- if !ok {
- return false
- }
- // There must be a receiver for it to be a method call. Otherwise it is
- // a function, not something that satisfies the error interface.
- if sig.Recv() == nil {
- return false
- }
- // There must be no arguments. Already verified by type checking, but be thorough.
- if sig.Params().Len() > 0 {
- return false
- }
- // Finally the real questions.
- // There must be one result.
- if sig.Results().Len() != 1 {
- return false
- }
- // It must have return type "string" from the universe.
- return sig.Results().At(0).Type() == types.Typ[types.String]
-}
-
// hasMethod reports whether the type contains a method with the given name.
// It is part of the workaround for Formatters and should be deleted when
// that workaround is no longer necessary.
diff --git a/src/cmp.bash b/src/cmp.bash
new file mode 100644
index 0000000000..68086c31f2
--- /dev/null
+++ b/src/cmp.bash
@@ -0,0 +1,61 @@
+#!/usr/bin/env bash
+
+# Copyright 2016 The Go 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 simple script to compare differences between
+# assembly listings for packages built with different
+# compiler flags. It is useful to inspect the impact
+# of a compiler change across all std lib packages.
+#
+# The script builds the std library (make.bash) once
+# with FLAGS1 and once with FLAGS2 and compares the
+# "go build <pkg>" assembly output for each package
+# and lists the packages with differences.
+#
+# It leaves and old.txt and new.txt file in the package
+# directories for the packages with differences.
+
+FLAGS1="-newexport=0"
+FLAGS2="-newexport=1"
+
+echo
+echo
+echo "1a) clean build using $FLAGS1"
+(export GO_GCFLAGS="$FLAGS1"; sh make.bash)
+
+echo
+echo
+echo "1b) save go build output for all packages"
+for pkg in `go list std`; do
+ echo $pkg
+ DIR=$GOROOT/src/$pkg
+ go build -gcflags "$FLAGS1 -S" -o /dev/null $pkg &> $DIR/old.txt
+done
+
+echo
+echo
+echo "2a) clean build using $FLAGS2"
+(export GO_GCFLAGS="$FLAGS2"; sh make.bash)
+
+echo
+echo
+echo "2b) save go build output for all packages"
+for pkg in `go list std`; do
+ echo $pkg
+ DIR=$GOROOT/src/$pkg
+ go build -gcflags "$FLAGS2 -S" -o /dev/null $pkg &> $DIR/new.txt
+done
+
+echo
+echo
+echo "3) compare assembly files"
+for pkg in `go list std`; do
+ DIR=$GOROOT/src/$pkg
+
+ if cmp $DIR/old.txt $DIR/new.txt &> /dev/null
+ then rm $DIR/old.txt $DIR/new.txt
+ else echo "==> $DIR"
+ fi
+done
diff --git a/src/compress/bzip2/bzip2.go b/src/compress/bzip2/bzip2.go
index 90e9aebab6..42788443bc 100644
--- a/src/compress/bzip2/bzip2.go
+++ b/src/compress/bzip2/bzip2.go
@@ -75,7 +75,7 @@ func (bz2 *reader) setup(needMagic bool) error {
}
bz2.fileCRC = 0
- bz2.blockSize = 100 * 1000 * (int(level) - '0')
+ bz2.blockSize = 100 * 1000 * (level - '0')
if bz2.blockSize > len(bz2.tt) {
bz2.tt = make([]uint32, bz2.blockSize)
}
@@ -293,7 +293,7 @@ func (bz2 *reader) readBlock() (err error) {
if c >= numHuffmanTrees {
return StructuralError("tree index too large")
}
- treeIndexes[i] = uint8(mtfTreeDecoder.Decode(c))
+ treeIndexes[i] = mtfTreeDecoder.Decode(c)
}
// The list of symbols for the move-to-front transform is taken from
@@ -399,7 +399,7 @@ func (bz2 *reader) readBlock() (err error) {
return StructuralError("repeats past end of block")
}
for i := 0; i < repeat; i++ {
- b := byte(mtf.First())
+ b := mtf.First()
bz2.tt[bufIndex] = uint32(b)
bz2.c[b]++
bufIndex++
@@ -420,7 +420,7 @@ func (bz2 *reader) readBlock() (err error) {
// it's always referenced with a run-length of 1. Thus 0
// doesn't need to be encoded and we have |v-1| in the next
// line.
- b := byte(mtf.Decode(int(v - 1)))
+ b := mtf.Decode(int(v - 1))
if bufIndex >= bz2.blockSize {
return StructuralError("data exceeds block size")
}
diff --git a/src/compress/flate/deflate.go b/src/compress/flate/deflate.go
index 3bb8b5e02a..d8bbffbc66 100644
--- a/src/compress/flate/deflate.go
+++ b/src/compress/flate/deflate.go
@@ -73,8 +73,8 @@ type compressor struct {
// hashPrev[hashHead[hashValue] & windowMask] contains the previous index
// with the same hash value.
chainHead int
- hashHead []uint32
- hashPrev []uint32
+ hashHead [hashSize]uint32
+ hashPrev [windowSize]uint32
hashOffset int
// input window: unprocessed data is window[index:windowEnd]
@@ -188,12 +188,13 @@ func (d *compressor) fillWindow(b []byte) {
var newH uint32
for i, val := range dst {
di := i + index
- newH = val & hashMask
+ newH = val
+ hh := &d.hashHead[newH&hashMask]
// Get previous value with the same hash.
// Our chain should point to the previous value.
- d.hashPrev[di&windowMask] = d.hashHead[newH]
+ d.hashPrev[di&windowMask] = *hh
// Set the head of the hash chain to us.
- d.hashHead[newH] = uint32(di + d.hashOffset)
+ *hh = uint32(di + d.hashOffset)
}
d.hash = newH
}
@@ -293,6 +294,7 @@ func bulkHash4(b []byte, dst []uint32) {
// bytes in size.
func matchLen(a, b []byte, max int) int {
a = a[:max]
+ b = b[:len(a)]
for i, av := range a {
if b[i] != av {
return i
@@ -302,8 +304,6 @@ func matchLen(a, b []byte, max int) int {
}
func (d *compressor) initDeflate() {
- d.hashHead = make([]uint32, hashSize)
- d.hashPrev = make([]uint32, windowSize)
d.window = make([]byte, 2*windowSize)
d.hashOffset = 1
d.tokens = make([]token, 0, maxFlateBlockTokens+1)
@@ -358,9 +358,10 @@ Loop:
if d.index < d.maxInsertIndex {
// Update the hash
d.hash = hash4(d.window[d.index : d.index+minMatchLength])
- d.chainHead = int(d.hashHead[d.hash])
+ hh := &d.hashHead[d.hash&hashMask]
+ d.chainHead = int(*hh)
d.hashPrev[d.index&windowMask] = uint32(d.chainHead)
- d.hashHead[d.hash] = uint32(d.index + d.hashOffset)
+ *hh = uint32(d.index + d.hashOffset)
}
prevLength := d.length
prevOffset := d.offset
@@ -404,9 +405,10 @@ Loop:
d.hash = hash4(d.window[d.index : d.index+minMatchLength])
// Get previous value with the same hash.
// Our chain should point to the previous value.
- d.hashPrev[d.index&windowMask] = d.hashHead[d.hash]
+ hh := &d.hashHead[d.hash&hashMask]
+ d.hashPrev[d.index&windowMask] = *hh
// Set the head of the hash chain to us.
- d.hashHead[d.hash] = uint32(d.index + d.hashOffset)
+ *hh = uint32(d.index + d.hashOffset)
}
}
if d.fastSkipHashing == skipNever {
@@ -531,9 +533,6 @@ func (d *compressor) init(w io.Writer, level int) (err error) {
return nil
}
-// hzeroes is used for zeroing the hash slice.
-var hzeroes [256]uint32
-
func (d *compressor) reset(w io.Writer) {
d.w.reset(w)
d.sync = false
@@ -543,15 +542,13 @@ func (d *compressor) reset(w io.Writer) {
d.windowEnd = 0
default:
d.chainHead = -1
- for s := d.hashHead; len(s) > 0; {
- n := copy(s, hzeroes[:])
- s = s[n:]
+ for i := range d.hashHead {
+ d.hashHead[i] = 0
}
- for s := d.hashPrev; len(s) > 0; s = s[len(hzeroes):] {
- copy(s, hzeroes[:])
+ for i := range d.hashPrev {
+ d.hashPrev[i] = 0
}
d.hashOffset = 1
-
d.index, d.windowEnd = 0, 0
d.blockStart, d.byteAvailable = 0, false
d.tokens = d.tokens[:0]
diff --git a/src/compress/flate/huffman_bit_writer.go b/src/compress/flate/huffman_bit_writer.go
index b99f86ea13..c4adef9ff5 100644
--- a/src/compress/flate/huffman_bit_writer.go
+++ b/src/compress/flate/huffman_bit_writer.go
@@ -6,7 +6,6 @@ package flate
import (
"io"
- "math"
)
const (
@@ -84,11 +83,11 @@ type huffmanBitWriter struct {
bits uint64
nbits uint
bytes [bufferSize]byte
+ codegenFreq [codegenCodeCount]int32
nbytes int
literalFreq []int32
offsetFreq []int32
codegen []uint8
- codegenFreq []int32
literalEncoding *huffmanEncoder
offsetEncoding *huffmanEncoder
codegenEncoding *huffmanEncoder
@@ -101,7 +100,6 @@ func newHuffmanBitWriter(w io.Writer) *huffmanBitWriter {
literalFreq: make([]int32, maxNumLit),
offsetFreq: make([]int32, offsetCodeCount),
codegen: make([]uint8, maxNumLit+offsetCodeCount+1),
- codegenFreq: make([]int32, codegenCodeCount),
literalEncoding: newHuffmanEncoder(maxNumLit),
codegenEncoding: newHuffmanEncoder(codegenCodeCount),
offsetEncoding: newHuffmanEncoder(offsetCodeCount),
@@ -143,12 +141,13 @@ func (w *huffmanBitWriter) writeBits(b int32, nb uint) {
w.bits >>= 48
w.nbits -= 48
n := w.nbytes
- w.bytes[n+0] = byte(bits)
- w.bytes[n+1] = byte(bits >> 8)
- w.bytes[n+2] = byte(bits >> 16)
- w.bytes[n+3] = byte(bits >> 24)
- w.bytes[n+4] = byte(bits >> 32)
- w.bytes[n+5] = byte(bits >> 40)
+ bytes := w.bytes[n : n+6]
+ bytes[0] = byte(bits)
+ bytes[1] = byte(bits >> 8)
+ bytes[2] = byte(bits >> 16)
+ bytes[3] = byte(bits >> 24)
+ bytes[4] = byte(bits >> 32)
+ bytes[5] = byte(bits >> 40)
n += 6
if n >= bufferFlushSize {
_, w.err = w.w.Write(w.bytes[:n])
@@ -282,6 +281,46 @@ func (w *huffmanBitWriter) generateCodegen(numLiterals int, numOffsets int, litE
codegen[outIndex] = badCode
}
+// dynamicSize returns the size of dynamically encoded data in bits.
+func (w *huffmanBitWriter) dynamicSize(litEnc, offEnc *huffmanEncoder, extraBits int) (size, numCodegens int) {
+ numCodegens = len(w.codegenFreq)
+ for numCodegens > 4 && w.codegenFreq[codegenOrder[numCodegens-1]] == 0 {
+ numCodegens--
+ }
+ header := 3 + 5 + 5 + 4 + (3 * numCodegens) +
+ w.codegenEncoding.bitLength(w.codegenFreq[:]) +
+ int(w.codegenFreq[16])*2 +
+ int(w.codegenFreq[17])*3 +
+ int(w.codegenFreq[18])*7
+ size = header +
+ litEnc.bitLength(w.literalFreq) +
+ offEnc.bitLength(w.offsetFreq) +
+ extraBits
+
+ return size, numCodegens
+}
+
+// fixedSize returns the size of dynamically encoded data in bits.
+func (w *huffmanBitWriter) fixedSize(extraBits int) int {
+ return 3 +
+ fixedLiteralEncoding.bitLength(w.literalFreq) +
+ fixedOffsetEncoding.bitLength(w.offsetFreq) +
+ extraBits
+}
+
+// storedSize calculates the stored size, including header.
+// The function returns the size in bits and whether the block
+// fits inside a single block.
+func (w *huffmanBitWriter) storedSize(in []byte) (int, bool) {
+ if in == nil {
+ return 0, false
+ }
+ if len(in) <= maxStoreBlockSize {
+ return (len(in) + 5) * 8, true
+ }
+ return 0, false
+}
+
func (w *huffmanBitWriter) writeCode(c hcode) {
if w.err != nil {
return
@@ -293,12 +332,13 @@ func (w *huffmanBitWriter) writeCode(c hcode) {
w.bits >>= 48
w.nbits -= 48
n := w.nbytes
- w.bytes[n+0] = byte(bits)
- w.bytes[n+1] = byte(bits >> 8)
- w.bytes[n+2] = byte(bits >> 16)
- w.bytes[n+3] = byte(bits >> 24)
- w.bytes[n+4] = byte(bits >> 32)
- w.bytes[n+5] = byte(bits >> 40)
+ bytes := w.bytes[n : n+6]
+ bytes[0] = byte(bits)
+ bytes[1] = byte(bits >> 8)
+ bytes[2] = byte(bits >> 16)
+ bytes[3] = byte(bits >> 24)
+ bytes[4] = byte(bits >> 32)
+ bytes[5] = byte(bits >> 40)
n += 6
if n >= bufferFlushSize {
_, w.err = w.w.Write(w.bytes[:n])
@@ -383,6 +423,11 @@ func (w *huffmanBitWriter) writeFixedHeader(isEof bool) {
w.writeBits(value, 3)
}
+// writeBlock will write a block of tokens with the smallest encoding.
+// The original input can be supplied, and if the huffman encoded data
+// is larger than the original bytes, the data will be written as a
+// stored block.
+// If the input is nil, the tokens will always be Huffman encoded.
func (w *huffmanBitWriter) writeBlock(tokens []token, eof bool, input []byte) {
if w.err != nil {
return
@@ -391,36 +436,28 @@ func (w *huffmanBitWriter) writeBlock(tokens []token, eof bool, input []byte) {
tokens = append(tokens, endBlockMarker)
numLiterals, numOffsets := w.indexTokens(tokens)
- storedBytes := 0
- if input != nil {
- storedBytes = len(input)
- }
- var extraBits int64
- var storedSize int64 = math.MaxInt64
- if storedBytes <= maxStoreBlockSize && input != nil {
- storedSize = int64((storedBytes + 5) * 8)
+ var extraBits int
+ storedSize, storable := w.storedSize(input)
+ if storable {
// We only bother calculating the costs of the extra bits required by
// the length of offset fields (which will be the same for both fixed
// and dynamic encoding), if we need to compare those two encodings
// against stored encoding.
for lengthCode := lengthCodesStart + 8; lengthCode < numLiterals; lengthCode++ {
// First eight length codes have extra size = 0.
- extraBits += int64(w.literalFreq[lengthCode]) * int64(lengthExtraBits[lengthCode-lengthCodesStart])
+ extraBits += int(w.literalFreq[lengthCode]) * int(lengthExtraBits[lengthCode-lengthCodesStart])
}
for offsetCode := 4; offsetCode < numOffsets; offsetCode++ {
// First four offset codes have extra size = 0.
- extraBits += int64(w.offsetFreq[offsetCode]) * int64(offsetExtraBits[offsetCode])
+ extraBits += int(w.offsetFreq[offsetCode]) * int(offsetExtraBits[offsetCode])
}
}
// Figure out smallest code.
// Fixed Huffman baseline.
- var size = int64(3) +
- fixedLiteralEncoding.bitLength(w.literalFreq) +
- fixedOffsetEncoding.bitLength(w.offsetFreq) +
- extraBits
var literalEncoding = fixedLiteralEncoding
var offsetEncoding = fixedOffsetEncoding
+ var size = w.fixedSize(extraBits)
// Dynamic Huffman?
var numCodegens int
@@ -428,20 +465,8 @@ func (w *huffmanBitWriter) writeBlock(tokens []token, eof bool, input []byte) {
// Generate codegen and codegenFrequencies, which indicates how to encode
// the literalEncoding and the offsetEncoding.
w.generateCodegen(numLiterals, numOffsets, w.literalEncoding, w.offsetEncoding)
- w.codegenEncoding.generate(w.codegenFreq, 7)
- numCodegens = len(w.codegenFreq)
- for numCodegens > 4 && w.codegenFreq[codegenOrder[numCodegens-1]] == 0 {
- numCodegens--
- }
- dynamicHeader := int64(3+5+5+4+(3*numCodegens)) +
- w.codegenEncoding.bitLength(w.codegenFreq) +
- int64(extraBits) +
- int64(w.codegenFreq[16]*2) +
- int64(w.codegenFreq[17]*3) +
- int64(w.codegenFreq[18]*7)
- dynamicSize := dynamicHeader +
- w.literalEncoding.bitLength(w.literalFreq) +
- w.offsetEncoding.bitLength(w.offsetFreq)
+ w.codegenEncoding.generate(w.codegenFreq[:], 7)
+ dynamicSize, numCodegens := w.dynamicSize(w.literalEncoding, w.offsetEncoding, extraBits)
if dynamicSize < size {
size = dynamicSize
@@ -450,9 +475,9 @@ func (w *huffmanBitWriter) writeBlock(tokens []token, eof bool, input []byte) {
}
// Stored bytes?
- if storedSize < size {
- w.writeStoredHeader(storedBytes, eof)
- w.writeBytes(input[:storedBytes])
+ if storable && storedSize < size {
+ w.writeStoredHeader(len(input), eof)
+ w.writeBytes(input)
return
}
@@ -465,12 +490,13 @@ func (w *huffmanBitWriter) writeBlock(tokens []token, eof bool, input []byte) {
// Write the tokens.
w.writeTokens(tokens, literalEncoding.codes, offsetEncoding.codes)
-
}
// writeBlockDynamic encodes a block using a dynamic Huffman table.
// This should be used if the symbols used have a disproportionate
// histogram distribution.
+// If input is supplied and the compression savings are below 1/16th of the
+// input size the block is stored.
func (w *huffmanBitWriter) writeBlockDynamic(tokens []token, eof bool, input []byte) {
if w.err != nil {
return
@@ -482,10 +508,14 @@ func (w *huffmanBitWriter) writeBlockDynamic(tokens []token, eof bool, input []b
// Generate codegen and codegenFrequencies, which indicates how to encode
// the literalEncoding and the offsetEncoding.
w.generateCodegen(numLiterals, numOffsets, w.literalEncoding, w.offsetEncoding)
- w.codegenEncoding.generate(w.codegenFreq, 7)
- numCodegens := len(w.codegenFreq)
- for numCodegens > 4 && w.codegenFreq[codegenOrder[numCodegens-1]] == 0 {
- numCodegens--
+ w.codegenEncoding.generate(w.codegenFreq[:], 7)
+ size, numCodegens := w.dynamicSize(w.literalEncoding, huffOffset, 0)
+
+ // Store bytes, if we don't get a reasonable improvement.
+ if ssize, storable := w.storedSize(input); storable && ssize < (size+size>>4) {
+ w.writeStoredHeader(len(input), eof)
+ w.writeBytes(input)
+ return
}
// Write Huffman table.
@@ -609,37 +639,19 @@ func (w *huffmanBitWriter) writeBlockHuff(eof bool, input []byte) {
// Generate codegen and codegenFrequencies, which indicates how to encode
// the literalEncoding and the offsetEncoding.
w.generateCodegen(numLiterals, numOffsets, w.literalEncoding, huffOffset)
- w.codegenEncoding.generate(w.codegenFreq, 7)
- numCodegens = len(w.codegenFreq)
- for numCodegens > 4 && w.codegenFreq[codegenOrder[numCodegens-1]] == 0 {
- numCodegens--
- }
- headerSize := int64(3+5+5+4+(3*numCodegens)) +
- w.codegenEncoding.bitLength(w.codegenFreq) +
- int64(w.codegenFreq[16]*2) +
- int64(w.codegenFreq[17]*3) +
- int64(w.codegenFreq[18]*7)
-
- // Includes EOB marker
- size := headerSize + w.literalEncoding.bitLength(w.literalFreq)
-
- // Calculate stored size
- var storedSize int64 = math.MaxInt64
- var storedBytes = len(input)
- if storedBytes <= maxStoreBlockSize {
- storedSize = int64(storedBytes+5) * 8
- }
+ w.codegenEncoding.generate(w.codegenFreq[:], 7)
+ size, numCodegens := w.dynamicSize(w.literalEncoding, huffOffset, 0)
// Store bytes, if we don't get a reasonable improvement.
- if storedSize < (size + size>>4) {
- w.writeStoredHeader(storedBytes, eof)
+ if ssize, storable := w.storedSize(input); storable && ssize < (size+size>>4) {
+ w.writeStoredHeader(len(input), eof)
w.writeBytes(input)
return
}
// Huffman.
w.writeDynamicHeader(numLiterals, numOffsets, numCodegens, eof)
- encoding := w.literalEncoding.codes
+ encoding := w.literalEncoding.codes[:257]
n := w.nbytes
for _, t := range input {
// Bitwriting inlined, ~30% speedup
@@ -653,12 +665,13 @@ func (w *huffmanBitWriter) writeBlockHuff(eof bool, input []byte) {
bits := w.bits
w.bits >>= 48
w.nbits -= 48
- w.bytes[n+0] = byte(bits)
- w.bytes[n+1] = byte(bits >> 8)
- w.bytes[n+2] = byte(bits >> 16)
- w.bytes[n+3] = byte(bits >> 24)
- w.bytes[n+4] = byte(bits >> 32)
- w.bytes[n+5] = byte(bits >> 40)
+ bytes := w.bytes[n : n+6]
+ bytes[0] = byte(bits)
+ bytes[1] = byte(bits >> 8)
+ bytes[2] = byte(bits >> 16)
+ bytes[3] = byte(bits >> 24)
+ bytes[4] = byte(bits >> 32)
+ bytes[5] = byte(bits >> 40)
n += 6
if n < bufferFlushSize {
continue
@@ -677,6 +690,7 @@ func (w *huffmanBitWriter) writeBlockHuff(eof bool, input []byte) {
//
// len(h) must be >= 256, and h's elements must be all zeroes.
func histogram(b []byte, h []int32) {
+ h = h[:256]
for _, t := range b {
h[t]++
}
diff --git a/src/compress/flate/huffman_code.go b/src/compress/flate/huffman_code.go
index b0328c6e08..bdcbd823b0 100644
--- a/src/compress/flate/huffman_code.go
+++ b/src/compress/flate/huffman_code.go
@@ -96,8 +96,8 @@ func generateFixedLiteralEncoding() *huffmanEncoder {
func generateFixedOffsetEncoding() *huffmanEncoder {
h := newHuffmanEncoder(30)
codes := h.codes
- for ch := uint16(0); ch < 30; ch++ {
- codes[ch] = hcode{code: reverseBits(ch, 5), len: 5}
+ for ch := range codes {
+ codes[ch] = hcode{code: reverseBits(uint16(ch), 5), len: 5}
}
return h
}
@@ -105,11 +105,11 @@ func generateFixedOffsetEncoding() *huffmanEncoder {
var fixedLiteralEncoding *huffmanEncoder = generateFixedLiteralEncoding()
var fixedOffsetEncoding *huffmanEncoder = generateFixedOffsetEncoding()
-func (h *huffmanEncoder) bitLength(freq []int32) int64 {
- var total int64
+func (h *huffmanEncoder) bitLength(freq []int32) int {
+ var total int
for i, f := range freq {
if f != 0 {
- total += int64(f) * int64(h.codes[i].len)
+ total += int(f) * int(h.codes[i].len)
}
}
return total
diff --git a/src/compress/flate/reverse_bits.go b/src/compress/flate/reverse_bits.go
index c1a02720d1..6b222900c1 100644
--- a/src/compress/flate/reverse_bits.go
+++ b/src/compress/flate/reverse_bits.go
@@ -44,5 +44,5 @@ func reverseUint16(v uint16) uint16 {
}
func reverseBits(number uint16, bitLength byte) uint16 {
- return reverseUint16(number << uint8(16-bitLength))
+ return reverseUint16(number << (16 - bitLength))
}
diff --git a/src/compress/flate/testdata/huffman-rand-1k.dyn.expect b/src/compress/flate/testdata/huffman-rand-1k.dyn.expect
index 0c24742fde..09dc798ee3 100644
--- a/src/compress/flate/testdata/huffman-rand-1k.dyn.expect
+++ b/src/compress/flate/testdata/huffman-rand-1k.dyn.expect
Binary files differ
diff --git a/src/compress/flate/writer_test.go b/src/compress/flate/writer_test.go
index 633cadf2b7..7967cd739c 100644
--- a/src/compress/flate/writer_test.go
+++ b/src/compress/flate/writer_test.go
@@ -86,14 +86,18 @@ func (e *errorWriter) Write(b []byte) (int, error) {
// Test if errors from the underlying writer is passed upwards.
func TestWriteError(t *testing.T) {
buf := new(bytes.Buffer)
- for i := 0; i < 1024*1024; i++ {
- buf.WriteString(fmt.Sprintf("asdasfasf%d%dfghfgujyut%dyutyu\n", i, i, i))
+ n := 65536
+ if !testing.Short() {
+ n *= 4
+ }
+ for i := 0; i < n; i++ {
+ fmt.Fprintf(buf, "asdasfasf%d%dfghfgujyut%dyutyu\n", i, i, i)
}
in := buf.Bytes()
// We create our own buffer to control number of writes.
- copyBuffer := make([]byte, 1024)
+ copyBuffer := make([]byte, 128)
for l := 0; l < 10; l++ {
- for fail := 1; fail <= 512; fail *= 2 {
+ for fail := 1; fail <= 256; fail *= 2 {
// Fail after 'fail' writes
ew := &errorWriter{N: fail}
w, err := NewWriter(ew, l)
diff --git a/src/compress/gzip/gunzip.go b/src/compress/gzip/gunzip.go
index 5d072878ee..926bae88c7 100644
--- a/src/compress/gzip/gunzip.go
+++ b/src/compress/gzip/gunzip.go
@@ -9,6 +9,7 @@ package gzip
import (
"bufio"
"compress/flate"
+ "encoding/binary"
"errors"
"hash/crc32"
"io"
@@ -33,6 +34,16 @@ var (
ErrHeader = errors.New("gzip: invalid header")
)
+var le = binary.LittleEndian
+
+// noEOF converts io.EOF to io.ErrUnexpectedEOF.
+func noEOF(err error) error {
+ if err == io.EOF {
+ return io.ErrUnexpectedEOF
+ }
+ return err
+}
+
// The gzip file stores a header giving metadata about the compressed file.
// That header is exposed as the fields of the Writer and Reader structs.
//
@@ -99,7 +110,8 @@ func (z *Reader) Reset(r io.Reader) error {
} else {
z.r = bufio.NewReader(r)
}
- return z.readHeader(true)
+ z.Header, z.err = z.readHeader()
+ return z.err
}
// Multistream controls whether the reader supports multistream files.
@@ -122,14 +134,13 @@ func (z *Reader) Multistream(ok bool) {
z.multistream = ok
}
-// GZIP (RFC 1952) is little-endian, unlike ZLIB (RFC 1950).
-func get4(p []byte) uint32 {
- return uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24
-}
-
+// readString reads a NUL-terminated string from z.r.
+// It treats the bytes read as being encoded as ISO 8859-1 (Latin-1) and
+// will output a string encoded using UTF-8.
+// This method always updates z.digest with the data read.
func (z *Reader) readString() (string, error) {
var err error
- needconv := false
+ needConv := false
for i := 0; ; i++ {
if i >= len(z.buf) {
return "", ErrHeader
@@ -139,11 +150,14 @@ func (z *Reader) readString() (string, error) {
return "", err
}
if z.buf[i] > 0x7f {
- needconv = true
+ needConv = true
}
if z.buf[i] == 0 {
- // GZIP (RFC 1952) specifies that strings are NUL-terminated ISO 8859-1 (Latin-1).
- if needconv {
+ // Digest covers the NUL terminator.
+ z.digest = crc32.Update(z.digest, crc32.IEEETable, z.buf[:i+1])
+
+ // Strings are ISO 8859-1, Latin-1 (RFC 1952, section 2.3.1).
+ if needConv {
s := make([]rune, 0, i)
for _, v := range z.buf[:i] {
s = append(s, rune(v))
@@ -155,20 +169,10 @@ func (z *Reader) readString() (string, error) {
}
}
-func (z *Reader) read2() (uint32, error) {
- _, err := io.ReadFull(z.r, z.buf[:2])
- if err != nil {
- if err == io.EOF {
- err = io.ErrUnexpectedEOF
- }
- return 0, err
- }
- return uint32(z.buf[0]) | uint32(z.buf[1])<<8, nil
-}
-
-func (z *Reader) readHeader(save bool) error {
- _, err := io.ReadFull(z.r, z.buf[:10])
- if err != nil {
+// readHeader reads the GZIP header according to section 2.3.1.
+// This method does not set z.err.
+func (z *Reader) readHeader() (hdr Header, err error) {
+ if _, err = io.ReadFull(z.r, z.buf[:10]); err != nil {
// RFC 1952, section 2.2, says the following:
// A gzip file consists of a series of "members" (compressed data sets).
//
@@ -176,63 +180,52 @@ func (z *Reader) readHeader(save bool) error {
// "series" is defined as "one or more" or "zero or more". To err on the
// side of caution, Go interprets this to mean "zero or more".
// Thus, it is okay to return io.EOF here.
- return err
+ return hdr, err
}
if z.buf[0] != gzipID1 || z.buf[1] != gzipID2 || z.buf[2] != gzipDeflate {
- return ErrHeader
+ return hdr, ErrHeader
}
flg := z.buf[3]
- if save {
- z.ModTime = time.Unix(int64(get4(z.buf[4:8])), 0)
- // z.buf[8] is xfl, ignored
- z.OS = z.buf[9]
- }
- z.digest = crc32.Update(0, crc32.IEEETable, z.buf[:10])
+ hdr.ModTime = time.Unix(int64(le.Uint32(z.buf[4:8])), 0)
+ // z.buf[8] is XFL and is currently ignored.
+ hdr.OS = z.buf[9]
+ z.digest = crc32.ChecksumIEEE(z.buf[:10])
if flg&flagExtra != 0 {
- n, err := z.read2()
- if err != nil {
- return err
+ if _, err = io.ReadFull(z.r, z.buf[:2]); err != nil {
+ return hdr, noEOF(err)
}
- data := make([]byte, n)
+ z.digest = crc32.Update(z.digest, crc32.IEEETable, z.buf[:2])
+ data := make([]byte, le.Uint16(z.buf[:2]))
if _, err = io.ReadFull(z.r, data); err != nil {
- if err == io.EOF {
- err = io.ErrUnexpectedEOF
- }
- return err
- }
- if save {
- z.Extra = data
+ return hdr, noEOF(err)
}
+ z.digest = crc32.Update(z.digest, crc32.IEEETable, data)
+ hdr.Extra = data
}
var s string
if flg&flagName != 0 {
if s, err = z.readString(); err != nil {
- return err
- }
- if save {
- z.Name = s
+ return hdr, err
}
+ hdr.Name = s
}
if flg&flagComment != 0 {
if s, err = z.readString(); err != nil {
- return err
- }
- if save {
- z.Comment = s
+ return hdr, err
}
+ hdr.Comment = s
}
if flg&flagHdrCrc != 0 {
- n, err := z.read2()
- if err != nil {
- return err
+ if _, err = io.ReadFull(z.r, z.buf[:2]); err != nil {
+ return hdr, noEOF(err)
}
- sum := z.digest & 0xFFFF
- if n != sum {
- return ErrHeader
+ digest := le.Uint16(z.buf[:2])
+ if digest != uint16(z.digest) {
+ return hdr, ErrHeader
}
}
@@ -242,7 +235,7 @@ func (z *Reader) readHeader(save bool) error {
} else {
z.decompressor.(flate.Resetter).Reset(z.r, nil)
}
- return nil
+ return hdr, nil
}
func (z *Reader) Read(p []byte) (n int, err error) {
@@ -260,13 +253,11 @@ func (z *Reader) Read(p []byte) (n int, err error) {
// Finished file; check checksum and size.
if _, err := io.ReadFull(z.r, z.buf[:8]); err != nil {
- if err == io.EOF {
- err = io.ErrUnexpectedEOF
- }
- z.err = err
- return n, err
+ z.err = noEOF(err)
+ return n, z.err
}
- digest, size := get4(z.buf[:4]), get4(z.buf[4:8])
+ digest := le.Uint32(z.buf[:4])
+ size := le.Uint32(z.buf[4:8])
if digest != z.digest || size != z.size {
z.err = ErrChecksum
return n, z.err
@@ -279,7 +270,7 @@ func (z *Reader) Read(p []byte) (n int, err error) {
}
z.err = nil // Remove io.EOF
- if z.err = z.readHeader(false); z.err != nil {
+ if _, z.err = z.readHeader(); z.err != nil {
return n, z.err
}
diff --git a/src/compress/gzip/gunzip_test.go b/src/compress/gzip/gunzip_test.go
index e1f79e3db3..fdce91989a 100644
--- a/src/compress/gzip/gunzip_test.go
+++ b/src/compress/gzip/gunzip_test.go
@@ -292,6 +292,53 @@ var gunzipTests = []gunzipTest{
},
ErrChecksum,
},
+ {
+ "f1l3n4m3.tXt",
+ "header with all fields used",
+ "",
+ []byte{
+ 0x1f, 0x8b, 0x08, 0x1e, 0x70, 0xf0, 0xf9, 0x4a,
+ 0x00, 0xaa, 0x09, 0x00, 0x7a, 0x7a, 0x05, 0x00,
+ 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x31, 0x6c,
+ 0x33, 0x6e, 0x34, 0x6d, 0x33, 0x2e, 0x74, 0x58,
+ 0x74, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+ 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
+ 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26,
+ 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e,
+ 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
+ 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
+ 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
+ 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
+ 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56,
+ 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e,
+ 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
+ 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
+ 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
+ 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e,
+ 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86,
+ 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
+ 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96,
+ 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e,
+ 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6,
+ 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae,
+ 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
+ 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe,
+ 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6,
+ 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce,
+ 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6,
+ 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde,
+ 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
+ 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee,
+ 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
+ 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe,
+ 0xff, 0x00, 0x92, 0xfd, 0x01, 0x00, 0x00, 0xff,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00,
+ },
+ nil,
+ },
}
func TestDecompressor(t *testing.T) {
diff --git a/src/compress/gzip/gzip.go b/src/compress/gzip/gzip.go
index 4651298585..c702c493c1 100644
--- a/src/compress/gzip/gzip.go
+++ b/src/compress/gzip/gzip.go
@@ -87,25 +87,12 @@ func (z *Writer) Reset(w io.Writer) {
z.init(w, z.level)
}
-// GZIP (RFC 1952) is little-endian, unlike ZLIB (RFC 1950).
-func put2(p []byte, v uint16) {
- p[0] = uint8(v >> 0)
- p[1] = uint8(v >> 8)
-}
-
-func put4(p []byte, v uint32) {
- p[0] = uint8(v >> 0)
- p[1] = uint8(v >> 8)
- p[2] = uint8(v >> 16)
- p[3] = uint8(v >> 24)
-}
-
// writeBytes writes a length-prefixed byte slice to z.w.
func (z *Writer) writeBytes(b []byte) error {
if len(b) > 0xffff {
return errors.New("gzip.Write: Extra data is too large")
}
- put2(z.buf[:2], uint16(len(b)))
+ le.PutUint16(z.buf[:2], uint16(len(b)))
_, err := z.w.Write(z.buf[:2])
if err != nil {
return err
@@ -168,7 +155,7 @@ func (z *Writer) Write(p []byte) (int, error) {
if z.Comment != "" {
z.buf[3] |= 0x10
}
- put4(z.buf[4:8], uint32(z.ModTime.Unix()))
+ le.PutUint32(z.buf[4:8], uint32(z.ModTime.Unix()))
if z.level == BestCompression {
z.buf[8] = 2
} else if z.level == BestSpeed {
@@ -254,8 +241,8 @@ func (z *Writer) Close() error {
if z.err != nil {
return z.err
}
- put4(z.buf[:4], z.digest)
- put4(z.buf[4:8], z.size)
+ le.PutUint32(z.buf[:4], z.digest)
+ le.PutUint32(z.buf[4:8], z.size)
_, z.err = z.w.Write(z.buf[:8])
return z.err
}
diff --git a/src/compress/lzw/writer.go b/src/compress/lzw/writer.go
index 7367c29651..6ddb335f31 100644
--- a/src/compress/lzw/writer.go
+++ b/src/compress/lzw/writer.go
@@ -119,7 +119,7 @@ func (e *encoder) incHi() error {
if err := e.write(e, clear); err != nil {
return err
}
- e.width = uint(e.litWidth) + 1
+ e.width = e.litWidth + 1
e.hi = clear + 1
e.overflow = clear << 1
for i := range e.table {
diff --git a/src/container/heap/heap_test.go b/src/container/heap/heap_test.go
index b3d054c5f3..d41110422e 100644
--- a/src/container/heap/heap_test.go
+++ b/src/container/heap/heap_test.go
@@ -173,7 +173,7 @@ func TestRemove2(t *testing.T) {
func BenchmarkDup(b *testing.B) {
const n = 10000
- h := make(myHeap, n)
+ h := make(myHeap, 0, n)
for i := 0; i < b.N; i++ {
for j := 0; j < n; j++ {
Push(&h, 0) // all elements are the same
diff --git a/src/context/context.go b/src/context/context.go
index 21dc8676bf..da294b1292 100644
--- a/src/context/context.go
+++ b/src/context/context.go
@@ -39,6 +39,7 @@ package context
import (
"errors"
"fmt"
+ "reflect"
"sync"
"time"
)
@@ -66,7 +67,7 @@ type Context interface {
//
// // Stream generates values with DoSomething and sends them to out
// // until DoSomething returns an error or ctx.Done is closed.
- // func Stream(ctx context.Context, out <-chan Value) error {
+ // func Stream(ctx context.Context, out chan<- Value) error {
// for {
// v, err := DoSomething(ctx)
// if err != nil {
@@ -424,7 +425,12 @@ func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
//
// Use context Values only for request-scoped data that transits processes and
// APIs, not for passing optional parameters to functions.
-func WithValue(parent Context, key interface{}, val interface{}) Context {
+//
+// The provided key must be comparable.
+func WithValue(parent Context, key, val interface{}) Context {
+ if !reflect.TypeOf(key).Comparable() {
+ panic("key is not comparable")
+ }
return &valueCtx{parent, key, val}
}
diff --git a/src/context/context_test.go b/src/context/context_test.go
index 05345fc5e5..aa26161d2b 100644
--- a/src/context/context_test.go
+++ b/src/context/context_test.go
@@ -229,55 +229,55 @@ func TestChildFinishesFirst(t *testing.T) {
}
}
-func testDeadline(c Context, wait time.Duration, t *testing.T) {
+func testDeadline(c Context, name string, failAfter time.Duration, t *testing.T) {
select {
- case <-time.After(wait):
- t.Fatalf("context should have timed out")
+ case <-time.After(failAfter):
+ t.Fatalf("%s: context should have timed out", name)
case <-c.Done():
}
if e := c.Err(); e != DeadlineExceeded {
- t.Errorf("c.Err() == %v want %v", e, DeadlineExceeded)
+ t.Errorf("%s: c.Err() == %v; want %v", name, e, DeadlineExceeded)
}
}
func TestDeadline(t *testing.T) {
- c, _ := WithDeadline(Background(), time.Now().Add(100*time.Millisecond))
+ c, _ := WithDeadline(Background(), time.Now().Add(50*time.Millisecond))
if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
t.Errorf("c.String() = %q want prefix %q", got, prefix)
}
- testDeadline(c, 200*time.Millisecond, t)
+ testDeadline(c, "WithDeadline", time.Second, t)
- c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond))
+ c, _ = WithDeadline(Background(), time.Now().Add(50*time.Millisecond))
o := otherContext{c}
- testDeadline(o, 200*time.Millisecond, t)
+ testDeadline(o, "WithDeadline+otherContext", time.Second, t)
- c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond))
+ c, _ = WithDeadline(Background(), time.Now().Add(50*time.Millisecond))
o = otherContext{c}
- c, _ = WithDeadline(o, time.Now().Add(300*time.Millisecond))
- testDeadline(c, 200*time.Millisecond, t)
+ c, _ = WithDeadline(o, time.Now().Add(4*time.Second))
+ testDeadline(c, "WithDeadline+otherContext+WithDeadline", 2*time.Second, t)
}
func TestTimeout(t *testing.T) {
- c, _ := WithTimeout(Background(), 100*time.Millisecond)
+ c, _ := WithTimeout(Background(), 50*time.Millisecond)
if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
t.Errorf("c.String() = %q want prefix %q", got, prefix)
}
- testDeadline(c, 200*time.Millisecond, t)
+ testDeadline(c, "WithTimeout", time.Second, t)
- c, _ = WithTimeout(Background(), 100*time.Millisecond)
+ c, _ = WithTimeout(Background(), 50*time.Millisecond)
o := otherContext{c}
- testDeadline(o, 200*time.Millisecond, t)
+ testDeadline(o, "WithTimeout+otherContext", time.Second, t)
- c, _ = WithTimeout(Background(), 100*time.Millisecond)
+ c, _ = WithTimeout(Background(), 50*time.Millisecond)
o = otherContext{c}
- c, _ = WithTimeout(o, 300*time.Millisecond)
- testDeadline(c, 200*time.Millisecond, t)
+ c, _ = WithTimeout(o, 3*time.Second)
+ testDeadline(c, "WithTimeout+otherContext+WithTimeout", 2*time.Second, t)
}
func TestCanceledTimeout(t *testing.T) {
- c, _ := WithTimeout(Background(), 200*time.Millisecond)
+ c, _ := WithTimeout(Background(), time.Second)
o := otherContext{c}
- c, cancel := WithTimeout(o, 400*time.Millisecond)
+ c, cancel := WithTimeout(o, 2*time.Second)
cancel()
time.Sleep(100 * time.Millisecond) // let cancelation propagate
select {
@@ -388,9 +388,9 @@ func TestAllocs(t *testing.T) {
gccgoLimit: 8,
},
{
- desc: "WithTimeout(bg, 100*time.Millisecond)",
+ desc: "WithTimeout(bg, 5*time.Millisecond)",
f: func() {
- c, cancel := WithTimeout(bg, 100*time.Millisecond)
+ c, cancel := WithTimeout(bg, 5*time.Millisecond)
cancel()
<-c.Done()
},
@@ -404,7 +404,11 @@ func TestAllocs(t *testing.T) {
// TOOD(iant): Remove this when gccgo does do escape analysis.
limit = test.gccgoLimit
}
- if n := testing.AllocsPerRun(100, test.f); n > limit {
+ numRuns := 100
+ if testing.Short() {
+ numRuns = 10
+ }
+ if n := testing.AllocsPerRun(numRuns, test.f); n > limit {
t.Errorf("%s allocs = %f want %d", test.desc, n, int(limit))
}
}
@@ -536,7 +540,7 @@ func testLayers(t *testing.T, seed int64, testTimeout bool) {
if testTimeout {
select {
case <-ctx.Done():
- case <-time.After(timeout + 100*time.Millisecond):
+ case <-time.After(timeout + time.Second):
errorf("ctx should have timed out")
}
checkValues("after timeout")
@@ -573,3 +577,16 @@ func TestCancelRemoves(t *testing.T) {
cancel()
checkChildren("after cancelling WithTimeout child", ctx, 0)
}
+
+func TestWithValueChecksKey(t *testing.T) {
+ panicVal := recoveredValue(func() { WithValue(Background(), []byte("foo"), "bar") })
+ if panicVal == nil {
+ t.Error("expected panic")
+ }
+}
+
+func recoveredValue(fn func()) (v interface{}) {
+ defer func() { v = recover() }()
+ fn()
+ return
+}
diff --git a/src/context/withtimeout_test.go b/src/context/withtimeout_test.go
index 3ab6fc347f..2aea303bed 100644
--- a/src/context/withtimeout_test.go
+++ b/src/context/withtimeout_test.go
@@ -13,9 +13,9 @@ import (
func ExampleWithTimeout() {
// Pass a context with a timeout to tell a blocking function that it
// should abandon its work after the timeout elapses.
- ctx, _ := context.WithTimeout(context.Background(), 100*time.Millisecond)
+ ctx, _ := context.WithTimeout(context.Background(), 50*time.Millisecond)
select {
- case <-time.After(200 * time.Millisecond):
+ case <-time.After(1 * time.Second):
fmt.Println("overslept")
case <-ctx.Done():
fmt.Println(ctx.Err()) // prints "context deadline exceeded"
diff --git a/src/crypto/aes/aes_gcm.go b/src/crypto/aes/aes_gcm.go
index 1377578950..b55714d57a 100644
--- a/src/crypto/aes/aes_gcm.go
+++ b/src/crypto/aes/aes_gcm.go
@@ -45,7 +45,7 @@ var errOpen = errors.New("cipher: message authentication failed")
// will use the optimised implementation in this file when possible. Instances
// of this type only exist when hasGCMAsm returns true.
type aesCipherGCM struct {
- aesCipher
+ aesCipherAsm
}
// NewGCM returns the AES cipher wrapped in Galois Counter Mode. This is only
diff --git a/src/crypto/aes/aes_test.go b/src/crypto/aes/aes_test.go
index 363180931c..28144968fc 100644
--- a/src/crypto/aes/aes_test.go
+++ b/src/crypto/aes/aes_test.go
@@ -280,42 +280,6 @@ var encryptTests = []CryptTest{
},
}
-// Test encryptBlock against FIPS 197 examples.
-func TestEncryptBlock(t *testing.T) {
- for i, tt := range encryptTests {
- n := len(tt.key) + 28
- enc := make([]uint32, n)
- dec := make([]uint32, n)
- expandKey(tt.key, enc, dec)
- out := make([]byte, len(tt.in))
- encryptBlock(enc, out, tt.in)
- for j, v := range out {
- if v != tt.out[j] {
- t.Errorf("encryptBlock %d: out[%d] = %#x, want %#x", i, j, v, tt.out[j])
- break
- }
- }
- }
-}
-
-// Test decryptBlock against FIPS 197 examples.
-func TestDecryptBlock(t *testing.T) {
- for i, tt := range encryptTests {
- n := len(tt.key) + 28
- enc := make([]uint32, n)
- dec := make([]uint32, n)
- expandKey(tt.key, enc, dec)
- plain := make([]byte, len(tt.in))
- decryptBlock(dec, plain, tt.out)
- for j, v := range plain {
- if v != tt.in[j] {
- t.Errorf("decryptBlock %d: plain[%d] = %#x, want %#x", i, j, v, tt.in[j])
- break
- }
- }
- }
-}
-
// Test Cipher Encrypt method against FIPS 197 examples.
func TestCipherEncrypt(t *testing.T) {
for i, tt := range encryptTests {
diff --git a/src/crypto/aes/asm_s390x.s b/src/crypto/aes/asm_s390x.s
new file mode 100644
index 0000000000..4a0720ca17
--- /dev/null
+++ b/src/crypto/aes/asm_s390x.s
@@ -0,0 +1,35 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+// func hasAsm() bool
+TEXT ·hasAsm(SB),NOSPLIT,$16-1
+ XOR R0, R0 // set function code to 0 (query)
+ LA mask-16(SP), R1 // 16-byte stack variable for mask
+ WORD $0xB92E0024 // cipher message (KM)
+
+ // check if bits 18-20 (big endian) are set
+ MOVD mask-16(SP), R2
+ MOVD $(0x38<<40), R3
+ AND R3, R2
+ CMPBNE R2, R3, notfound
+ MOVB $1, ret+0(FP)
+ RET
+notfound:
+ MOVB $0, ret+0(FP)
+ RET
+
+// func cryptBlocks(function code, key, dst, src *byte, length int)
+TEXT ·cryptBlocks(SB),NOSPLIT,$0-40
+ MOVD key+8(FP), R1
+ MOVD dst+16(FP), R2
+ MOVD src+24(FP), R4
+ MOVD length+32(FP), R5
+ MOVD function+0(FP), R0
+loop:
+ WORD $0xB92E0024 // cipher message (KM)
+ BVS loop // branch back if interrupted
+ XOR R0, R0
+ RET
diff --git a/src/crypto/aes/cipher.go b/src/crypto/aes/cipher.go
index 04d2be1283..c5a8e91d00 100644
--- a/src/crypto/aes/cipher.go
+++ b/src/crypto/aes/cipher.go
@@ -36,15 +36,15 @@ func NewCipher(key []byte) (cipher.Block, error) {
case 16, 24, 32:
break
}
+ return newCipher(key)
+}
- n := k + 28
+// newCipherGeneric creates and returns a new cipher.Block
+// implemented in pure Go.
+func newCipherGeneric(key []byte) (cipher.Block, error) {
+ n := len(key) + 28
c := aesCipher{make([]uint32, n), make([]uint32, n)}
- expandKey(key, c.enc, c.dec)
-
- if hasGCMAsm() {
- return &aesCipherGCM{c}, nil
- }
-
+ expandKeyGo(key, c.enc, c.dec)
return &c, nil
}
@@ -57,7 +57,7 @@ func (c *aesCipher) Encrypt(dst, src []byte) {
if len(dst) < BlockSize {
panic("crypto/aes: output not full block")
}
- encryptBlock(c.enc, dst, src)
+ encryptBlockGo(c.enc, dst, src)
}
func (c *aesCipher) Decrypt(dst, src []byte) {
@@ -67,5 +67,5 @@ func (c *aesCipher) Decrypt(dst, src []byte) {
if len(dst) < BlockSize {
panic("crypto/aes: output not full block")
}
- decryptBlock(c.dec, dst, src)
+ decryptBlockGo(c.dec, dst, src)
}
diff --git a/src/crypto/aes/cipher_amd64.go b/src/crypto/aes/cipher_amd64.go
new file mode 100644
index 0000000000..b33c8ff251
--- /dev/null
+++ b/src/crypto/aes/cipher_amd64.go
@@ -0,0 +1,83 @@
+// 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 aes
+
+import (
+ "crypto/cipher"
+)
+
+// defined in asm_amd64.s
+func hasAsm() bool
+func encryptBlockAsm(nr int, xk *uint32, dst, src *byte)
+func decryptBlockAsm(nr int, xk *uint32, dst, src *byte)
+func expandKeyAsm(nr int, key *byte, enc *uint32, dec *uint32)
+
+type aesCipherAsm struct {
+ aesCipher
+}
+
+var useAsm = hasAsm()
+
+func newCipher(key []byte) (cipher.Block, error) {
+ if !useAsm {
+ return newCipherGeneric(key)
+ }
+ n := len(key) + 28
+ c := aesCipherAsm{aesCipher{make([]uint32, n), make([]uint32, n)}}
+ rounds := 10
+ switch len(key) {
+ case 128 / 8:
+ rounds = 10
+ case 192 / 8:
+ rounds = 12
+ case 256 / 8:
+ rounds = 14
+ }
+ expandKeyAsm(rounds, &key[0], &c.enc[0], &c.dec[0])
+ if hasGCMAsm() {
+ return &aesCipherGCM{c}, nil
+ }
+
+ return &c, nil
+}
+
+func (c *aesCipherAsm) BlockSize() int { return BlockSize }
+
+func (c *aesCipherAsm) Encrypt(dst, src []byte) {
+ if len(src) < BlockSize {
+ panic("crypto/aes: input not full block")
+ }
+ if len(dst) < BlockSize {
+ panic("crypto/aes: output not full block")
+ }
+ encryptBlockAsm(len(c.enc)/4-1, &c.enc[0], &dst[0], &src[0])
+}
+
+func (c *aesCipherAsm) Decrypt(dst, src []byte) {
+ if len(src) < BlockSize {
+ panic("crypto/aes: input not full block")
+ }
+ if len(dst) < BlockSize {
+ panic("crypto/aes: output not full block")
+ }
+ decryptBlockAsm(len(c.dec)/4-1, &c.dec[0], &dst[0], &src[0])
+}
+
+// expandKey is used by BenchmarkExpand to ensure that the asm implementation
+// of key expansion is used for the benchmark when it is available.
+func expandKey(key []byte, enc, dec []uint32) {
+ if useAsm {
+ rounds := 10 // rounds needed for AES128
+ switch len(key) {
+ case 192 / 8:
+ rounds = 12
+ case 256 / 8:
+ rounds = 14
+ }
+ expandKeyAsm(rounds, &key[0], &enc[0], &dec[0])
+ } else {
+ expandKeyGo(key, enc, dec)
+ }
+}
diff --git a/src/crypto/aes/cipher_asm.go b/src/crypto/aes/cipher_asm.go
deleted file mode 100644
index 964eaaa6f8..0000000000
--- a/src/crypto/aes/cipher_asm.go
+++ /dev/null
@@ -1,48 +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.
-
-// +build amd64
-
-package aes
-
-// defined in asm_$GOARCH.s
-func hasAsm() bool
-func encryptBlockAsm(nr int, xk *uint32, dst, src *byte)
-func decryptBlockAsm(nr int, xk *uint32, dst, src *byte)
-func expandKeyAsm(nr int, key *byte, enc *uint32, dec *uint32)
-
-var useAsm = hasAsm()
-
-func encryptBlock(xk []uint32, dst, src []byte) {
- if useAsm {
- encryptBlockAsm(len(xk)/4-1, &xk[0], &dst[0], &src[0])
- } else {
- encryptBlockGo(xk, dst, src)
- }
-}
-
-func decryptBlock(xk []uint32, dst, src []byte) {
- if useAsm {
- decryptBlockAsm(len(xk)/4-1, &xk[0], &dst[0], &src[0])
- } else {
- decryptBlockGo(xk, dst, src)
- }
-}
-
-func expandKey(key []byte, enc, dec []uint32) {
- if useAsm {
- rounds := 10
- switch len(key) {
- case 128 / 8:
- rounds = 10
- case 192 / 8:
- rounds = 12
- case 256 / 8:
- rounds = 14
- }
- expandKeyAsm(rounds, &key[0], &enc[0], &dec[0])
- } else {
- expandKeyGo(key, enc, dec)
- }
-}
diff --git a/src/crypto/aes/cipher_generic.go b/src/crypto/aes/cipher_generic.go
index 32b2b3cc56..f8070346e3 100644
--- a/src/crypto/aes/cipher_generic.go
+++ b/src/crypto/aes/cipher_generic.go
@@ -2,26 +2,25 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build !amd64
+// +build !amd64,!s390x
package aes
-func encryptBlock(xk []uint32, dst, src []byte) {
- encryptBlockGo(xk, dst, src)
-}
+import (
+ "crypto/cipher"
+)
-func decryptBlock(xk []uint32, dst, src []byte) {
- decryptBlockGo(xk, dst, src)
+// newCipher calls the newCipherGeneric function
+// directly. Platforms with hardware accelerated
+// implementations of AES should implement their
+// own version of newCipher (which may then call
+// newCipherGeneric if needed).
+func newCipher(key []byte) (cipher.Block, error) {
+ return newCipherGeneric(key)
}
+// expandKey is used by BenchmarkExpand and should
+// call an assembly implementation if one is available.
func expandKey(key []byte, enc, dec []uint32) {
expandKeyGo(key, enc, dec)
}
-
-func hasGCMAsm() bool {
- return false
-}
-
-type aesCipherGCM struct {
- aesCipher
-}
diff --git a/src/crypto/aes/cipher_s390x.go b/src/crypto/aes/cipher_s390x.go
new file mode 100644
index 0000000000..bec5933013
--- /dev/null
+++ b/src/crypto/aes/cipher_s390x.go
@@ -0,0 +1,90 @@
+// Copyright 2016 The Go 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 aes
+
+import (
+ "crypto/cipher"
+)
+
+type code int
+
+// Function codes for the cipher message family of instructions.
+const (
+ aes128 code = 18
+ aes192 = 19
+ aes256 = 20
+)
+
+type aesCipherAsm struct {
+ function code // code for cipher message instruction
+ key []byte // key (128, 192 or 256 bytes)
+ storage [256]byte // array backing key slice
+}
+
+// hasAsm reports whether the AES-128, AES-192 and AES-256
+// cipher message (KM) function codes are supported.
+// Note: this function call is expensive.
+func hasAsm() bool
+
+// cryptBlocks invokes the cipher message (KM) instruction with
+// the given function code. This is equivalent to AES in ECB
+// mode. The length must be a multiple of BlockSize (16).
+//go:noesape
+func cryptBlocks(c code, key, dst, src *byte, length int)
+
+var useAsm = hasAsm()
+
+func newCipher(key []byte) (cipher.Block, error) {
+ if !useAsm {
+ return newCipherGeneric(key)
+ }
+
+ var function code
+ switch len(key) {
+ case 128 / 8:
+ function = aes128
+ case 192 / 8:
+ function = aes192
+ case 256 / 8:
+ function = aes256
+ default:
+ return nil, KeySizeError(len(key))
+ }
+
+ var c aesCipherAsm
+ c.function = function
+ c.key = c.storage[:len(key)]
+ copy(c.key, key)
+ return &c, nil
+}
+
+func (c *aesCipherAsm) BlockSize() int { return BlockSize }
+
+func (c *aesCipherAsm) Encrypt(dst, src []byte) {
+ if len(src) < BlockSize {
+ panic("crypto/aes: input not full block")
+ }
+ if len(dst) < BlockSize {
+ panic("crypto/aes: output not full block")
+ }
+ cryptBlocks(c.function, &c.key[0], &dst[0], &src[0], BlockSize)
+}
+
+func (c *aesCipherAsm) Decrypt(dst, src []byte) {
+ if len(src) < BlockSize {
+ panic("crypto/aes: input not full block")
+ }
+ if len(dst) < BlockSize {
+ panic("crypto/aes: output not full block")
+ }
+ // The decrypt function code is equal to the function code + 128.
+ cryptBlocks(c.function+128, &c.key[0], &dst[0], &src[0], BlockSize)
+}
+
+// expandKey is used by BenchmarkExpand. cipher message (KM) does not need key
+// expansion so there is no assembly equivalent.
+func expandKey(key []byte, enc, dec []uint32) {
+ expandKeyGo(key, enc, dec)
+}
diff --git a/src/crypto/aes/gcm_amd64.s b/src/crypto/aes/gcm_amd64.s
index cabb028f75..c25badd558 100644
--- a/src/crypto/aes/gcm_amd64.s
+++ b/src/crypto/aes/gcm_amd64.s
@@ -89,8 +89,8 @@ TEXT ·hasGCMAsm(SB),NOSPLIT,$0
TEXT ·aesEncBlock(SB),NOSPLIT,$0
MOVQ dst+0(FP), DI
MOVQ src+8(FP), SI
- MOVQ ks+16(FP), DX
- MOVQ ks+24(FP), CX
+ MOVQ ks_base+16(FP), DX
+ MOVQ ks_len+24(FP), CX
SHRQ $2, CX
DECQ CX
@@ -211,8 +211,8 @@ TEXT ·gcmAesInit(SB),NOSPLIT,$0
#define NR DX
MOVQ productTable+0(FP), dst
- MOVQ ks+8(FP), KS
- MOVQ ks+16(FP), NR
+ MOVQ ks_base+8(FP), KS
+ MOVQ ks_len+16(FP), NR
SHRQ $2, NR
DECQ NR
@@ -325,8 +325,8 @@ TEXT ·gcmAesData(SB),NOSPLIT,$0
#define autLen DX
MOVQ productTable+0(FP), pTbl
- MOVQ data+8(FP), aut
- MOVQ data+16(FP), autLen
+ MOVQ data_base+8(FP), aut
+ MOVQ data_len+16(FP), autLen
MOVQ T+32(FP), tPtr
PXOR ACC0, ACC0
@@ -421,7 +421,7 @@ dataBail:
#undef autLen
// func gcmAesEnc(productTable *[256]byte, dst, src []byte, ctr, T *[16]byte, ks []uint32)
-TEXT ·gcmAesEnc(SB),0,$256-144
+TEXT ·gcmAesEnc(SB),0,$256-96
#define pTbl DI
#define ctx DX
#define ctrPtr CX
@@ -477,12 +477,12 @@ TEXT ·gcmAesEnc(SB),0,$256-144
MOVQ productTable+0(FP), pTbl
MOVQ dst+8(FP), ctx
- MOVQ src+32(FP), ptx
- MOVQ src+40(FP), ptxLen
+ MOVQ src_base+32(FP), ptx
+ MOVQ src_len+40(FP), ptxLen
MOVQ ctr+56(FP), ctrPtr
MOVQ T+64(FP), tPtr
- MOVQ KS+72(FP), ks
- MOVQ nr+80(FP), NR
+ MOVQ ks_base+72(FP), ks
+ MOVQ ks_len+80(FP), NR
SHRQ $2, NR
DECQ NR
@@ -932,7 +932,7 @@ gcmAesEncDone:
#undef increment
// func gcmAesDec(productTable *[256]byte, dst, src []byte, ctr, T *[16]byte, ks []uint32)
-TEXT ·gcmAesDec(SB),0,$128-144
+TEXT ·gcmAesDec(SB),0,$128-96
#define increment(i) ADDL $1, aluCTR; MOVL aluCTR, aluTMP; XORL aluK, aluTMP; BSWAPL aluTMP; MOVL aluTMP, (3*4 + i*16)(SP)
#define combinedDecRound(i) \
MOVOU (16*i)(ks), T0;\
@@ -960,12 +960,12 @@ TEXT ·gcmAesDec(SB),0,$128-144
MOVQ productTable+0(FP), pTbl
MOVQ dst+8(FP), ptx
- MOVQ src+32(FP), ctx
- MOVQ src+40(FP), ptxLen
+ MOVQ src_base+32(FP), ctx
+ MOVQ src_len+40(FP), ptxLen
MOVQ ctr+56(FP), ctrPtr
MOVQ T+64(FP), tPtr
- MOVQ KS+72(FP), ks
- MOVQ nr+80(FP), NR
+ MOVQ ks_base+72(FP), ks
+ MOVQ ks_len+80(FP), NR
SHRQ $2, NR
DECQ NR
diff --git a/src/crypto/cipher/xor.go b/src/crypto/cipher/xor.go
index 020c9e1730..01ca0a9f08 100644
--- a/src/crypto/cipher/xor.go
+++ b/src/crypto/cipher/xor.go
@@ -10,7 +10,7 @@ import (
)
const wordSize = int(unsafe.Sizeof(uintptr(0)))
-const supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "amd64" || runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le"
+const supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "amd64" || runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "s390x"
// fastXORBytes xors in bulk. It only works on architectures that
// support unaligned read/writes.
diff --git a/src/crypto/des/block.go b/src/crypto/des/block.go
index 26355a22e7..99338d62a6 100644
--- a/src/crypto/des/block.go
+++ b/src/crypto/des/block.go
@@ -72,7 +72,7 @@ func init() {
for i := 0; i < 4; i++ {
for j := 0; j < 16; j++ {
f := uint64(sBoxes[s][i][j]) << (4 * (7 - uint(s)))
- f = permuteBlock(uint64(f), permutationFunction[:])
+ f = permuteBlock(f, permutationFunction[:])
feistelBox[s][16*i+j] = uint32(f)
}
}
diff --git a/src/crypto/ecdsa/ecdsa.go b/src/crypto/ecdsa/ecdsa.go
index 42ec92b6f9..e63bd8669e 100644
--- a/src/crypto/ecdsa/ecdsa.go
+++ b/src/crypto/ecdsa/ecdsa.go
@@ -23,6 +23,7 @@ import (
"crypto/elliptic"
"crypto/sha512"
"encoding/asn1"
+ "errors"
"io"
"math/big"
)
@@ -140,6 +141,8 @@ func fermatInverse(k, N *big.Int) *big.Int {
return new(big.Int).Exp(k, nMinus2, N)
}
+var errZeroParam = errors.New("zero parameter")
+
// Sign signs an arbitrary length hash (which should be the result of hashing a
// larger message) using the private key, priv. It returns the signature as a
// pair of integers. The security of the private key depends on the entropy of
@@ -180,7 +183,9 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
// See [NSA] 3.4.1
c := priv.PublicKey.Curve
N := c.Params().N
-
+ if N.Sign() == 0 {
+ return nil, nil, errZeroParam
+ }
var k, kInv *big.Int
for {
for {
@@ -193,7 +198,7 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
if in, ok := priv.Curve.(invertible); ok {
kInv = in.Inverse(k)
} else {
- kInv = fermatInverse(k, N)
+ kInv = fermatInverse(k, N) // N != 0
}
r, _ = priv.Curve.ScalarBaseMult(k.Bytes())
@@ -207,7 +212,7 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
s = new(big.Int).Mul(priv.D, r)
s.Add(s, e)
s.Mul(s, kInv)
- s.Mod(s, N)
+ s.Mod(s, N) // N != 0
if s.Sign() != 0 {
break
}
diff --git a/src/crypto/md5/md5block_decl.go b/src/crypto/md5/md5block_decl.go
index a0c3fd1a95..de2da1a346 100644
--- a/src/crypto/md5/md5block_decl.go
+++ b/src/crypto/md5/md5block_decl.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build amd64 amd64p32 386 arm
+// +build amd64 amd64p32 386 arm ppc64le
package md5
diff --git a/src/crypto/md5/md5block_generic.go b/src/crypto/md5/md5block_generic.go
index 263463e51c..d000ef948c 100644
--- a/src/crypto/md5/md5block_generic.go
+++ b/src/crypto/md5/md5block_generic.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build !amd64,!amd64p32,!386,!arm
+// +build !amd64,!amd64p32,!386,!arm,!ppc64le
package md5
diff --git a/src/crypto/md5/md5block_ppc64le.s b/src/crypto/md5/md5block_ppc64le.s
new file mode 100644
index 0000000000..3b95da5e8a
--- /dev/null
+++ b/src/crypto/md5/md5block_ppc64le.s
@@ -0,0 +1,192 @@
+// Original source:
+// http://www.zorinaq.com/papers/md5-amd64.html
+// http://www.zorinaq.com/papers/md5-amd64.tar.bz2
+//
+// MD5 optimized for ppc64le using Go's assembler for
+// ppc64le, based on md5block_amd64.s implementation by
+// the Go authors.
+//
+// Author: Marc Bevand <bevand_m (at) epita.fr>
+// Licence: I hereby disclaim the copyright on this code and place it
+// in the public domain.
+
+#include "textflag.h"
+
+// TODO: Could be updated for ppc64 big endian
+// by using the correct byte reverse instruction.
+// Changes required in the Go assembler to make
+// that instruction work.
+
+#define MOVE_LITTLE_ENDIAN MOVWZ
+
+TEXT ·block(SB),NOSPLIT,$0-32
+ MOVD dig+0(FP), R10
+ MOVD p+8(FP), R6
+ MOVD p_len+16(FP), R5
+ SLD $6, R5
+ SRD $6, R5
+ ADD R6, R5, R7
+
+ MOVWZ 0(R10), R22
+ MOVWZ 4(R10), R3
+ MOVWZ 8(R10), R4
+ MOVWZ 12(R10), R5
+ CMP R6, R7
+ BEQ end
+
+loop:
+ MOVWZ R22, R14
+ MOVWZ R3, R15
+ MOVWZ R4, R16
+ MOVWZ R5, R17
+
+ MOVE_LITTLE_ENDIAN 0(R6), R8
+ MOVWZ R5, R9
+
+#define ROUND1(a, b, c, d, index, const, shift) \
+ XOR c, R9; \
+ ADD $const, a; \
+ ADD R8, a; \
+ AND b, R9; \
+ XOR d, R9; \
+ MOVE_LITTLE_ENDIAN (index*4)(R6), R8; \
+ ADD R9, a; \
+ RLWMI $shift, a, $0xffffffff, a; \
+ MOVWZ c, R9; \
+ ADD b, a; \
+ MOVWZ a, a
+
+ ROUND1(R22,R3,R4,R5, 1,0xd76aa478, 7);
+ ROUND1(R5,R22,R3,R4, 2,0xe8c7b756,12);
+ ROUND1(R4,R5,R22,R3, 3,0x242070db,17);
+ ROUND1(R3,R4,R5,R22, 4,0xc1bdceee,22);
+ ROUND1(R22,R3,R4,R5, 5,0xf57c0faf, 7);
+ ROUND1(R5,R22,R3,R4, 6,0x4787c62a,12);
+ ROUND1(R4,R5,R22,R3, 7,0xa8304613,17);
+ ROUND1(R3,R4,R5,R22, 8,0xfd469501,22);
+ ROUND1(R22,R3,R4,R5, 9,0x698098d8, 7);
+ ROUND1(R5,R22,R3,R4,10,0x8b44f7af,12);
+ ROUND1(R4,R5,R22,R3,11,0xffff5bb1,17);
+ ROUND1(R3,R4,R5,R22,12,0x895cd7be,22);
+ ROUND1(R22,R3,R4,R5,13,0x6b901122, 7);
+ ROUND1(R5,R22,R3,R4,14,0xfd987193,12);
+ ROUND1(R4,R5,R22,R3,15,0xa679438e,17);
+ ROUND1(R3,R4,R5,R22, 0,0x49b40821,22);
+
+ MOVE_LITTLE_ENDIAN (1*4)(R6), R8
+ MOVWZ R5, R9
+ MOVWZ R5, R10
+
+#define ROUND2(a, b, c, d, index, const, shift) \
+ XOR $0xffffffff, R9; \ // NOTW R9
+ ADD $const, a; \
+ ADD R8, a; \
+ AND b, R10; \
+ AND c, R9; \
+ MOVE_LITTLE_ENDIAN (index*4)(R6), R8; \
+ OR R9, R10; \
+ MOVWZ c, R9; \
+ ADD R10, a; \
+ MOVWZ c, R10; \
+ RLWMI $shift, a, $0xffffffff, a; \
+ ADD b, a; \
+ MOVWZ a, a
+
+ ROUND2(R22,R3,R4,R5, 6,0xf61e2562, 5);
+ ROUND2(R5,R22,R3,R4,11,0xc040b340, 9);
+ ROUND2(R4,R5,R22,R3, 0,0x265e5a51,14);
+ ROUND2(R3,R4,R5,R22, 5,0xe9b6c7aa,20);
+ ROUND2(R22,R3,R4,R5,10,0xd62f105d, 5);
+ ROUND2(R5,R22,R3,R4,15, 0x2441453, 9);
+ ROUND2(R4,R5,R22,R3, 4,0xd8a1e681,14);
+ ROUND2(R3,R4,R5,R22, 9,0xe7d3fbc8,20);
+ ROUND2(R22,R3,R4,R5,14,0x21e1cde6, 5);
+ ROUND2(R5,R22,R3,R4, 3,0xc33707d6, 9);
+ ROUND2(R4,R5,R22,R3, 8,0xf4d50d87,14);
+ ROUND2(R3,R4,R5,R22,13,0x455a14ed,20);
+ ROUND2(R22,R3,R4,R5, 2,0xa9e3e905, 5);
+ ROUND2(R5,R22,R3,R4, 7,0xfcefa3f8, 9);
+ ROUND2(R4,R5,R22,R3,12,0x676f02d9,14);
+ ROUND2(R3,R4,R5,R22, 0,0x8d2a4c8a,20);
+
+ MOVE_LITTLE_ENDIAN (5*4)(R6), R8
+ MOVWZ R4, R9
+
+#define ROUND3(a, b, c, d, index, const, shift) \
+ ADD $const, a; \
+ ADD R8, a; \
+ MOVE_LITTLE_ENDIAN (index*4)(R6), R8; \
+ XOR d, R9; \
+ XOR b, R9; \
+ ADD R9, a; \
+ RLWMI $shift, a, $0xffffffff, a; \
+ MOVWZ b, R9; \
+ ADD b, a; \
+ MOVWZ a, a
+
+ ROUND3(R22,R3,R4,R5, 8,0xfffa3942, 4);
+ ROUND3(R5,R22,R3,R4,11,0x8771f681,11);
+ ROUND3(R4,R5,R22,R3,14,0x6d9d6122,16);
+ ROUND3(R3,R4,R5,R22, 1,0xfde5380c,23);
+ ROUND3(R22,R3,R4,R5, 4,0xa4beea44, 4);
+ ROUND3(R5,R22,R3,R4, 7,0x4bdecfa9,11);
+ ROUND3(R4,R5,R22,R3,10,0xf6bb4b60,16);
+ ROUND3(R3,R4,R5,R22,13,0xbebfbc70,23);
+ ROUND3(R22,R3,R4,R5, 0,0x289b7ec6, 4);
+ ROUND3(R5,R22,R3,R4, 3,0xeaa127fa,11);
+ ROUND3(R4,R5,R22,R3, 6,0xd4ef3085,16);
+ ROUND3(R3,R4,R5,R22, 9, 0x4881d05,23);
+ ROUND3(R22,R3,R4,R5,12,0xd9d4d039, 4);
+ ROUND3(R5,R22,R3,R4,15,0xe6db99e5,11);
+ ROUND3(R4,R5,R22,R3, 2,0x1fa27cf8,16);
+ ROUND3(R3,R4,R5,R22, 0,0xc4ac5665,23);
+
+ MOVE_LITTLE_ENDIAN (0*4)(R6), R8
+ MOVWZ $0xffffffff, R9
+ XOR R5, R9
+
+#define ROUND4(a, b, c, d, index, const, shift) \
+ ADD $const, a; \
+ ADD R8, a; \
+ OR b, R9; \
+ XOR c, R9; \
+ ADD R9, a; \
+ MOVE_LITTLE_ENDIAN (index*4)(R6), R8; \
+ MOVWZ $0xffffffff, R9; \
+ RLWMI $shift, a, $0xffffffff, a; \
+ XOR c, R9; \
+ ADD b, a; \
+ MOVWZ a, a
+
+ ROUND4(R22,R3,R4,R5, 7,0xf4292244, 6);
+ ROUND4(R5,R22,R3,R4,14,0x432aff97,10);
+ ROUND4(R4,R5,R22,R3, 5,0xab9423a7,15);
+ ROUND4(R3,R4,R5,R22,12,0xfc93a039,21);
+ ROUND4(R22,R3,R4,R5, 3,0x655b59c3, 6);
+ ROUND4(R5,R22,R3,R4,10,0x8f0ccc92,10);
+ ROUND4(R4,R5,R22,R3, 1,0xffeff47d,15);
+ ROUND4(R3,R4,R5,R22, 8,0x85845dd1,21);
+ ROUND4(R22,R3,R4,R5,15,0x6fa87e4f, 6);
+ ROUND4(R5,R22,R3,R4, 6,0xfe2ce6e0,10);
+ ROUND4(R4,R5,R22,R3,13,0xa3014314,15);
+ ROUND4(R3,R4,R5,R22, 4,0x4e0811a1,21);
+ ROUND4(R22,R3,R4,R5,11,0xf7537e82, 6);
+ ROUND4(R5,R22,R3,R4, 2,0xbd3af235,10);
+ ROUND4(R4,R5,R22,R3, 9,0x2ad7d2bb,15);
+ ROUND4(R3,R4,R5,R22, 0,0xeb86d391,21);
+
+ ADD R14, R22
+ ADD R15, R3
+ ADD R16, R4
+ ADD R17, R5
+ ADD $64, R6
+ CMP R6, R7
+ BLT loop
+
+end:
+ MOVD dig+0(FP), R10
+ MOVWZ R22, 0(R10)
+ MOVWZ R3, 4(R10)
+ MOVWZ R4, 8(R10)
+ MOVWZ R5, 12(R10)
+ RET
diff --git a/src/crypto/rsa/rsa.go b/src/crypto/rsa/rsa.go
index 3f353f891f..594305631b 100644
--- a/src/crypto/rsa/rsa.go
+++ b/src/crypto/rsa/rsa.go
@@ -465,6 +465,9 @@ func decrypt(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err er
err = ErrDecryption
return
}
+ if priv.N.Sign() == 0 {
+ return nil, ErrDecryption
+ }
var ir *big.Int
if random != nil {
@@ -490,7 +493,7 @@ func decrypt(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err er
}
}
bigE := big.NewInt(int64(priv.E))
- rpowe := new(big.Int).Exp(r, bigE, priv.N)
+ rpowe := new(big.Int).Exp(r, bigE, priv.N) // N != 0
cCopy := new(big.Int).Set(c)
cCopy.Mul(cCopy, rpowe)
cCopy.Mod(cCopy, priv.N)
diff --git a/src/crypto/sha1/fallback_test.go b/src/crypto/sha1/fallback_test.go
new file mode 100644
index 0000000000..08acd044d0
--- /dev/null
+++ b/src/crypto/sha1/fallback_test.go
@@ -0,0 +1,34 @@
+// Copyright 2016 The Go 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 s390x
+
+package sha1
+
+import (
+ "fmt"
+ "io"
+ "testing"
+)
+
+// Tests the fallback code path in case the optimized asm
+// implementation cannot be used.
+// See also TestBlockGeneric.
+func TestGenericPath(t *testing.T) {
+ if useAsm == false {
+ t.Skipf("assembly implementation unavailable")
+ }
+ useAsm = false
+ defer func() { useAsm = true }()
+ c := New()
+ in := "ΑΒΓΔΕϜΖΗΘΙΚΛΜΝΞΟΠϺϘΡΣΤΥΦΧΨΩ"
+ gold := "0f58c2bb130f8182375f325c18342215255387e5"
+ if _, err := io.WriteString(c, in); err != nil {
+ t.Fatalf("could not write to c: %v", err)
+ }
+ out := fmt.Sprintf("%x", c.Sum(nil))
+ if out != gold {
+ t.Fatalf("mismatch: got %s, wanted %s", out, gold)
+ }
+}
diff --git a/src/crypto/sha1/sha1_test.go b/src/crypto/sha1/sha1_test.go
index 80ac5e9f74..9202e682a8 100644
--- a/src/crypto/sha1/sha1_test.go
+++ b/src/crypto/sha1/sha1_test.go
@@ -91,7 +91,7 @@ func TestBlockSize(t *testing.T) {
}
}
-// Tests that blockGeneric (pure Go) and block (in assembly for amd64, 386, arm) match.
+// Tests that blockGeneric (pure Go) and block (in assembly for some architectures) match.
func TestBlockGeneric(t *testing.T) {
gen, asm := New().(*digest), New().(*digest)
buf := make([]byte, BlockSize*20) // arbitrary factor
diff --git a/src/crypto/sha1/sha1block_decl.go b/src/crypto/sha1/sha1block_decl.go
index 082735f064..a85b74b878 100644
--- a/src/crypto/sha1/sha1block_decl.go
+++ b/src/crypto/sha1/sha1block_decl.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build amd64 amd64p32 arm 386
+// +build amd64 amd64p32 arm 386 s390x
package sha1
diff --git a/src/crypto/sha1/sha1block_generic.go b/src/crypto/sha1/sha1block_generic.go
index 696e26b625..f0194626a6 100644
--- a/src/crypto/sha1/sha1block_generic.go
+++ b/src/crypto/sha1/sha1block_generic.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build !amd64,!amd64p32,!386,!arm
+// +build !amd64,!amd64p32,!386,!arm,!s390x
package sha1
diff --git a/src/crypto/sha1/sha1block_s390x.go b/src/crypto/sha1/sha1block_s390x.go
new file mode 100644
index 0000000000..aac7c1182d
--- /dev/null
+++ b/src/crypto/sha1/sha1block_s390x.go
@@ -0,0 +1,12 @@
+// Copyright 2016 The Go 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 sha1
+
+// featureCheck reports whether the CPU supports the
+// SHA1 compute intermediate message digest (KIMD)
+// function code.
+func featureCheck() bool
+
+var useAsm = featureCheck()
diff --git a/src/crypto/sha1/sha1block_s390x.s b/src/crypto/sha1/sha1block_s390x.s
new file mode 100644
index 0000000000..a9c4b085ed
--- /dev/null
+++ b/src/crypto/sha1/sha1block_s390x.s
@@ -0,0 +1,34 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+// func featureCheck() bool
+TEXT ·featureCheck(SB),NOSPLIT,$16-1
+ LA tmp-16(SP), R1
+ XOR R0, R0 // query function code is 0
+ WORD $0xB93E0006 // KIMD (R6 is ignored)
+ MOVBZ tmp-16(SP), R4 // get the first byte
+ AND $0x40, R4 // bit 1 (big endian) for SHA1
+ CMPBEQ R4, $0, nosha1
+ MOVB $1, ret+0(FP)
+ RET
+nosha1:
+ MOVB $0, ret+0(FP)
+ RET
+
+// func block(dig *digest, p []byte)
+TEXT ·block(SB),NOSPLIT,$0-32
+ MOVBZ ·useAsm(SB), R4
+ LMG dig+0(FP), R1, R3 // R2 = &p[0], R3 = len(p)
+ CMPBNE R4, $1, generic
+ MOVBZ $1, R0 // SHA1 function code
+loop:
+ WORD $0xB93E0002 // KIMD R2
+ BVS loop // continue if interrupted
+done:
+ XOR R0, R0 // restore R0
+ RET
+generic:
+ BR ·blockGeneric(SB)
diff --git a/src/crypto/sha256/fallback_test.go b/src/crypto/sha256/fallback_test.go
new file mode 100644
index 0000000000..5917a4862a
--- /dev/null
+++ b/src/crypto/sha256/fallback_test.go
@@ -0,0 +1,35 @@
+// Copyright 2016 The Go 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 s390x
+
+package sha256
+
+import (
+ "fmt"
+ "io"
+ "testing"
+)
+
+// Tests the fallback code path in case the optimized asm
+// implementation cannot be used.
+// See also TestBlockGeneric.
+func TestGenericPath(t *testing.T) {
+ if useAsm == false {
+ t.Skipf("assembly implementation unavailable")
+ }
+ useAsm = false
+ defer func() { useAsm = true }()
+ c := New()
+ in := "ΑΒΓΔΕϜΖΗΘΙΚΛΜΝΞΟΠϺϘΡΣΤΥΦΧΨΩ"
+ gold := "e93d84ec2b22383123be9f713697fb25" +
+ "338c86e2f7d8d1ddc2d89d332dd9d76c"
+ if _, err := io.WriteString(c, in); err != nil {
+ t.Fatalf("could not write to c: %v", err)
+ }
+ out := fmt.Sprintf("%x", c.Sum(nil))
+ if out != gold {
+ t.Fatalf("mismatch: got %s, wanted %s", out, gold)
+ }
+}
diff --git a/src/crypto/sha256/sha256_test.go b/src/crypto/sha256/sha256_test.go
index 9ac8a96dfc..279cf5ad40 100644
--- a/src/crypto/sha256/sha256_test.go
+++ b/src/crypto/sha256/sha256_test.go
@@ -7,6 +7,7 @@
package sha256
import (
+ "crypto/rand"
"fmt"
"io"
"testing"
@@ -150,6 +151,18 @@ func TestBlockSize(t *testing.T) {
}
}
+// Tests that blockGeneric (pure Go) and block (in assembly for some architectures) match.
+func TestBlockGeneric(t *testing.T) {
+ gen, asm := New().(*digest), New().(*digest)
+ buf := make([]byte, BlockSize*20) // arbitrary factor
+ rand.Read(buf)
+ blockGeneric(gen, buf)
+ block(asm, buf)
+ if *gen != *asm {
+ t.Error("block and blockGeneric resulted in different states")
+ }
+}
+
var bench = New()
var buf = make([]byte, 8192)
diff --git a/src/crypto/sha256/sha256block.go b/src/crypto/sha256/sha256block.go
index ca5efd156a..d43bbf0245 100644
--- a/src/crypto/sha256/sha256block.go
+++ b/src/crypto/sha256/sha256block.go
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build !386,!amd64
-
// SHA256 block step.
// In its own file so that a faster assembly or C version
// can be substituted easily.
@@ -77,7 +75,7 @@ var _K = []uint32{
0xc67178f2,
}
-func block(dig *digest, p []byte) {
+func blockGeneric(dig *digest, p []byte) {
var w [64]uint32
h0, h1, h2, h3, h4, h5, h6, h7 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7]
for len(p) >= chunk {
diff --git a/src/crypto/sha256/sha256block_decl.go b/src/crypto/sha256/sha256block_decl.go
index 35fe34b98a..e6caff9a74 100644
--- a/src/crypto/sha256/sha256block_decl.go
+++ b/src/crypto/sha256/sha256block_decl.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build 386 amd64
+// +build 386 amd64 s390x
package sha256
diff --git a/src/crypto/sha256/sha256block_generic.go b/src/crypto/sha256/sha256block_generic.go
new file mode 100644
index 0000000000..1a01969b0d
--- /dev/null
+++ b/src/crypto/sha256/sha256block_generic.go
@@ -0,0 +1,9 @@
+// Copyright 2016 The Go 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 !amd64,!386,!s390x
+
+package sha256
+
+var block = blockGeneric
diff --git a/src/crypto/sha256/sha256block_s390x.go b/src/crypto/sha256/sha256block_s390x.go
new file mode 100644
index 0000000000..b7beefef0c
--- /dev/null
+++ b/src/crypto/sha256/sha256block_s390x.go
@@ -0,0 +1,12 @@
+// Copyright 2016 The Go 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 sha256
+
+// featureCheck reports whether the CPU supports the
+// SHA256 compute intermediate message digest (KIMD)
+// function code.
+func featureCheck() bool
+
+var useAsm = featureCheck()
diff --git a/src/crypto/sha256/sha256block_s390x.s b/src/crypto/sha256/sha256block_s390x.s
new file mode 100644
index 0000000000..ee35991f50
--- /dev/null
+++ b/src/crypto/sha256/sha256block_s390x.s
@@ -0,0 +1,34 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+// func featureCheck() bool
+TEXT ·featureCheck(SB),NOSPLIT,$16-1
+ LA tmp-16(SP), R1
+ XOR R0, R0 // query function code is 0
+ WORD $0xB93E0006 // KIMD (R6 is ignored)
+ MOVBZ tmp-16(SP), R4 // get the first byte
+ AND $0x20, R4 // bit 2 (big endian) for SHA256
+ CMPBEQ R4, $0, nosha256
+ MOVB $1, ret+0(FP)
+ RET
+nosha256:
+ MOVB $0, ret+0(FP)
+ RET
+
+// func block(dig *digest, p []byte)
+TEXT ·block(SB),NOSPLIT,$0-32
+ MOVBZ ·useAsm(SB), R4
+ LMG dig+0(FP), R1, R3 // R2 = &p[0], R3 = len(p)
+ CMPBNE R4, $1, generic
+ MOVBZ $2, R0 // SHA256 function code
+loop:
+ WORD $0xB93E0002 // KIMD R2
+ BVS loop // continue if interrupted
+done:
+ XOR R0, R0 // restore R0
+ RET
+generic:
+ BR ·blockGeneric(SB)
diff --git a/src/crypto/sha512/fallback_test.go b/src/crypto/sha512/fallback_test.go
new file mode 100644
index 0000000000..9024ce668a
--- /dev/null
+++ b/src/crypto/sha512/fallback_test.go
@@ -0,0 +1,37 @@
+// Copyright 2016 The Go 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 s390x
+
+package sha512
+
+import (
+ "fmt"
+ "io"
+ "testing"
+)
+
+// Tests the fallback code path in case the optimized asm
+// implementation cannot be used.
+// See also TestBlockGeneric.
+func TestGenericPath(t *testing.T) {
+ if useAsm == false {
+ t.Skipf("assembly implementation unavailable")
+ }
+ useAsm = false
+ defer func() { useAsm = true }()
+ c := New()
+ in := "ΑΒΓΔΕϜΖΗΘΙΚΛΜΝΞΟΠϺϘΡΣΤΥΦΧΨΩ"
+ gold := "6922e319366d677f34c504af31bfcb29" +
+ "e531c125ecd08679362bffbd6b6ebfb9" +
+ "0dcc27dfc1f3d3b16a16c0763cf43b91" +
+ "40bbf9bbb7233724e9a0c6655b185d76"
+ if _, err := io.WriteString(c, in); err != nil {
+ t.Fatalf("could not write to c: %v", err)
+ }
+ out := fmt.Sprintf("%x", c.Sum(nil))
+ if out != gold {
+ t.Fatalf("mismatch: got %s, wanted %s", out, gold)
+ }
+}
diff --git a/src/crypto/sha512/sha512_test.go b/src/crypto/sha512/sha512_test.go
index 6992d125de..a3a136a19f 100644
--- a/src/crypto/sha512/sha512_test.go
+++ b/src/crypto/sha512/sha512_test.go
@@ -7,6 +7,7 @@
package sha512
import (
+ "crypto/rand"
"encoding/hex"
"hash"
"io"
@@ -304,6 +305,18 @@ func TestBlockSize(t *testing.T) {
}
}
+// Tests that blockGeneric (pure Go) and block (in assembly for some architectures) match.
+func TestBlockGeneric(t *testing.T) {
+ gen, asm := New().(*digest), New().(*digest)
+ buf := make([]byte, BlockSize*20) // arbitrary factor
+ rand.Read(buf)
+ blockGeneric(gen, buf)
+ block(asm, buf)
+ if *gen != *asm {
+ t.Error("block and blockGeneric resulted in different states")
+ }
+}
+
var bench = New()
var buf = make([]byte, 8192)
diff --git a/src/crypto/sha512/sha512block.go b/src/crypto/sha512/sha512block.go
index 648ae8f7e1..42e8d19fe8 100644
--- a/src/crypto/sha512/sha512block.go
+++ b/src/crypto/sha512/sha512block.go
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build !amd64
-
// SHA512 block step.
// In its own file so that a faster assembly or C version
// can be substituted easily.
@@ -93,7 +91,7 @@ var _K = []uint64{
0x6c44198c4a475817,
}
-func block(dig *digest, p []byte) {
+func blockGeneric(dig *digest, p []byte) {
var w [80]uint64
h0, h1, h2, h3, h4, h5, h6, h7 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7]
for len(p) >= chunk {
diff --git a/src/crypto/sha512/sha512block_decl.go b/src/crypto/sha512/sha512block_decl.go
index 8034153779..47d656a7e4 100644
--- a/src/crypto/sha512/sha512block_decl.go
+++ b/src/crypto/sha512/sha512block_decl.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build amd64
+// +build amd64 s390x
package sha512
diff --git a/src/crypto/sha512/sha512block_generic.go b/src/crypto/sha512/sha512block_generic.go
new file mode 100644
index 0000000000..2c691baa3d
--- /dev/null
+++ b/src/crypto/sha512/sha512block_generic.go
@@ -0,0 +1,9 @@
+// Copyright 2016 The Go 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 !amd64,!s390x
+
+package sha512
+
+var block = blockGeneric
diff --git a/src/crypto/sha512/sha512block_s390x.go b/src/crypto/sha512/sha512block_s390x.go
new file mode 100644
index 0000000000..f05dc18e12
--- /dev/null
+++ b/src/crypto/sha512/sha512block_s390x.go
@@ -0,0 +1,12 @@
+// Copyright 2016 The Go 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 sha512
+
+// featureCheck reports whether the CPU supports the
+// SHA512 compute intermediate message digest (KIMD)
+// function code.
+func featureCheck() bool
+
+var useAsm = featureCheck()
diff --git a/src/crypto/sha512/sha512block_s390x.s b/src/crypto/sha512/sha512block_s390x.s
new file mode 100644
index 0000000000..aab81e2bcf
--- /dev/null
+++ b/src/crypto/sha512/sha512block_s390x.s
@@ -0,0 +1,34 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+// func featureCheck() bool
+TEXT ·featureCheck(SB),NOSPLIT,$16-1
+ LA tmp-16(SP), R1
+ XOR R0, R0 // query function code is 0
+ WORD $0xB93E0006 // KIMD (R6 is ignored)
+ MOVBZ tmp-16(SP), R4 // get the first byte
+ AND $0x10, R4 // bit 3 (big endian) for SHA512
+ CMPBEQ R4, $0, nosha512
+ MOVB $1, ret+0(FP)
+ RET
+nosha512:
+ MOVB $0, ret+0(FP)
+ RET
+
+// func block(dig *digest, p []byte)
+TEXT ·block(SB),NOSPLIT,$0-32
+ MOVBZ ·useAsm(SB), R4
+ LMG dig+0(FP), R1, R3 // R2 = &p[0], R3 = len(p)
+ CMPBNE R4, $1, generic
+ MOVBZ $3, R0 // SHA512 function code
+loop:
+ WORD $0xB93E0002 // KIMD R2
+ BVS loop // continue if interrupted
+done:
+ XOR R0, R0 // restore R0
+ RET
+generic:
+ BR ·blockGeneric(SB)
diff --git a/src/crypto/tls/alert.go b/src/crypto/tls/alert.go
index 3de4834d3f..9cf99224f5 100644
--- a/src/crypto/tls/alert.go
+++ b/src/crypto/tls/alert.go
@@ -69,9 +69,9 @@ var alertText = map[alert]string{
func (e alert) String() string {
s, ok := alertText[e]
if ok {
- return s
+ return "tls: " + s
}
- return "alert(" + strconv.Itoa(int(e)) + ")"
+ return "tls: alert(" + strconv.Itoa(int(e)) + ")"
}
func (e alert) Error() string {
diff --git a/src/crypto/tls/common.go b/src/crypto/tls/common.go
index 572266bc8f..33d585c5b0 100644
--- a/src/crypto/tls/common.go
+++ b/src/crypto/tls/common.go
@@ -114,7 +114,7 @@ const (
certTypeRSAFixedDH = 3 // A certificate containing a static DH key
certTypeDSSFixedDH = 4 // A certificate containing a static DH key
- // See RFC4492 sections 3 and 5.5.
+ // See RFC 4492 sections 3 and 5.5.
certTypeECDSASign = 64 // A certificate containing an ECDSA-capable public key, signed with ECDSA.
certTypeRSAFixedECDH = 65 // A certificate containing an ECDH-capable public key, signed with RSA.
certTypeECDSAFixedECDH = 66 // A certificate containing an ECDH-capable public key, signed with ECDSA.
@@ -516,7 +516,7 @@ func (c *Config) getCertificate(clientHello *ClientHelloInfo) (*Certificate, err
}
if len(c.Certificates) == 0 {
- return nil, errors.New("crypto/tls: no certificates configured")
+ return nil, errors.New("tls: no certificates configured")
}
if len(c.Certificates) == 1 || c.NameToCertificate == nil {
diff --git a/src/crypto/tls/conn.go b/src/crypto/tls/conn.go
index 2b1875d903..8c05b15a06 100644
--- a/src/crypto/tls/conn.go
+++ b/src/crypto/tls/conn.go
@@ -928,7 +928,7 @@ func (c *Conn) readHandshake() (interface{}, error) {
return m, nil
}
-var errClosed = errors.New("crypto/tls: use of closed connection")
+var errClosed = errors.New("tls: use of closed connection")
// Write writes data to the connection.
func (c *Conn) Write(b []byte) (int, error) {
diff --git a/src/crypto/tls/handshake_client.go b/src/crypto/tls/handshake_client.go
index f71509b25a..75e84023fe 100644
--- a/src/crypto/tls/handshake_client.go
+++ b/src/crypto/tls/handshake_client.go
@@ -533,17 +533,17 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) {
if !clientDidNPN && serverHasNPN {
c.sendAlert(alertHandshakeFailure)
- return false, errors.New("server advertised unrequested NPN extension")
+ return false, errors.New("tls: server advertised unrequested NPN extension")
}
if !clientDidALPN && serverHasALPN {
c.sendAlert(alertHandshakeFailure)
- return false, errors.New("server advertised unrequested ALPN extension")
+ return false, errors.New("tls: server advertised unrequested ALPN extension")
}
if serverHasNPN && serverHasALPN {
c.sendAlert(alertHandshakeFailure)
- return false, errors.New("server advertised both NPN and ALPN extensions")
+ return false, errors.New("tls: server advertised both NPN and ALPN extensions")
}
if serverHasALPN {
@@ -556,6 +556,16 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) {
return false, nil
}
+ if hs.session.vers != c.vers {
+ c.sendAlert(alertHandshakeFailure)
+ return false, errors.New("tls: server resumed a session with a different version")
+ }
+
+ if hs.session.cipherSuite != hs.suite.id {
+ c.sendAlert(alertHandshakeFailure)
+ return false, errors.New("tls: server resumed a session with a different cipher suite")
+ }
+
// Restore masterSecret and peerCerts from previous state
hs.masterSecret = hs.session.masterSecret
c.peerCertificates = hs.session.serverCertificates
diff --git a/src/crypto/tls/handshake_messages.go b/src/crypto/tls/handshake_messages.go
index 13d013a594..3f9a63b110 100644
--- a/src/crypto/tls/handshake_messages.go
+++ b/src/crypto/tls/handshake_messages.go
@@ -214,7 +214,7 @@ func (m *clientHelloMsg) marshal() []byte {
z[4] = byte(l)
z = z[5:]
for _, pointFormat := range m.supportedPoints {
- z[0] = byte(pointFormat)
+ z[0] = pointFormat
z = z[1:]
}
}
@@ -589,7 +589,7 @@ func (m *serverHelloMsg) marshal() []byte {
z := x[39+len(m.sessionId):]
z[0] = uint8(m.cipherSuite >> 8)
z[1] = uint8(m.cipherSuite)
- z[2] = uint8(m.compressionMethod)
+ z[2] = m.compressionMethod
z = z[3:]
if numExtensions > 0 {
diff --git a/src/crypto/tls/handshake_server.go b/src/crypto/tls/handshake_server.go
index a6cafd3d24..70f4374d34 100644
--- a/src/crypto/tls/handshake_server.go
+++ b/src/crypto/tls/handshake_server.go
@@ -209,7 +209,7 @@ Curves:
hs.rsaSignOk = true
default:
c.sendAlert(alertInternalError)
- return false, fmt.Errorf("crypto/tls: unsupported signing key type (%T)", priv.Public())
+ return false, fmt.Errorf("tls: unsupported signing key type (%T)", priv.Public())
}
}
if priv, ok := hs.cert.PrivateKey.(crypto.Decrypter); ok {
@@ -218,7 +218,7 @@ Curves:
hs.rsaDecryptOk = true
default:
c.sendAlert(alertInternalError)
- return false, fmt.Errorf("crypto/tls: unsupported decryption key type (%T)", priv.Public())
+ return false, fmt.Errorf("tls: unsupported decryption key type (%T)", priv.Public())
}
}
@@ -514,7 +514,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
switch key := pub.(type) {
case *ecdsa.PublicKey:
if signatureAndHash.signature != signatureECDSA {
- err = errors.New("bad signature type for client's ECDSA certificate")
+ err = errors.New("tls: bad signature type for client's ECDSA certificate")
break
}
ecdsaSig := new(ecdsaSignature)
@@ -522,7 +522,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
break
}
if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
- err = errors.New("ECDSA signature contained zero or negative values")
+ err = errors.New("tls: ECDSA signature contained zero or negative values")
break
}
var digest []byte
@@ -530,11 +530,11 @@ func (hs *serverHandshakeState) doFullHandshake() error {
break
}
if !ecdsa.Verify(key, digest, ecdsaSig.R, ecdsaSig.S) {
- err = errors.New("ECDSA verification failure")
+ err = errors.New("tls: ECDSA verification failure")
}
case *rsa.PublicKey:
if signatureAndHash.signature != signatureRSA {
- err = errors.New("bad signature type for client's RSA certificate")
+ err = errors.New("tls: bad signature type for client's RSA certificate")
break
}
var digest []byte
diff --git a/src/crypto/tls/key_agreement.go b/src/crypto/tls/key_agreement.go
index 3326894a08..467efb2bf5 100644
--- a/src/crypto/tls/key_agreement.go
+++ b/src/crypto/tls/key_agreement.go
@@ -242,19 +242,19 @@ NextCandidate:
case signatureECDSA:
_, ok := priv.Public().(*ecdsa.PublicKey)
if !ok {
- return nil, errors.New("ECDHE ECDSA requires an ECDSA server key")
+ return nil, errors.New("tls: ECDHE ECDSA requires an ECDSA server key")
}
case signatureRSA:
_, ok := priv.Public().(*rsa.PublicKey)
if !ok {
- return nil, errors.New("ECDHE RSA requires a RSA server key")
+ return nil, errors.New("tls: ECDHE RSA requires a RSA server key")
}
default:
- return nil, errors.New("unknown ECDHE signature algorithm")
+ return nil, errors.New("tls: unknown ECDHE signature algorithm")
}
sig, err = priv.Sign(config.rand(), digest, hashFunc)
if err != nil {
- return nil, errors.New("failed to sign ECDHE parameters: " + err.Error())
+ return nil, errors.New("tls: failed to sign ECDHE parameters: " + err.Error())
}
skx := new(serverKeyExchangeMsg)
@@ -354,28 +354,28 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell
case signatureECDSA:
pubKey, ok := cert.PublicKey.(*ecdsa.PublicKey)
if !ok {
- return errors.New("ECDHE ECDSA requires a ECDSA server public key")
+ return errors.New("tls: ECDHE ECDSA requires a ECDSA server public key")
}
ecdsaSig := new(ecdsaSignature)
if _, err := asn1.Unmarshal(sig, ecdsaSig); err != nil {
return err
}
if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
- return errors.New("ECDSA signature contained zero or negative values")
+ return errors.New("tls: ECDSA signature contained zero or negative values")
}
if !ecdsa.Verify(pubKey, digest, ecdsaSig.R, ecdsaSig.S) {
- return errors.New("ECDSA verification failure")
+ return errors.New("tls: ECDSA verification failure")
}
case signatureRSA:
pubKey, ok := cert.PublicKey.(*rsa.PublicKey)
if !ok {
- return errors.New("ECDHE RSA requires a RSA server public key")
+ return errors.New("tls: ECDHE RSA requires a RSA server public key")
}
if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, digest, sig); err != nil {
return err
}
default:
- return errors.New("unknown ECDHE signature algorithm")
+ return errors.New("tls: unknown ECDHE signature algorithm")
}
return nil
@@ -383,7 +383,7 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell
func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
if ka.curve == nil {
- return nil, nil, errors.New("missing ServerKeyExchange message")
+ return nil, nil, errors.New("tls: missing ServerKeyExchange message")
}
priv, mx, my, err := elliptic.GenerateKey(ka.curve, config.rand())
if err != nil {
diff --git a/src/crypto/tls/prf.go b/src/crypto/tls/prf.go
index 747b817ba3..5833fc1963 100644
--- a/src/crypto/tls/prf.go
+++ b/src/crypto/tls/prf.go
@@ -85,7 +85,7 @@ func prf30(result, secret, label, seed []byte) {
done := 0
i := 0
- // RFC5246 section 6.3 says that the largest PRF output needed is 128
+ // RFC 5246 section 6.3 says that the largest PRF output needed is 128
// bytes. Since no more ciphersuites will be added to SSLv3, this will
// remain true. Each iteration gives us 16 bytes so 10 iterations will
// be sufficient.
diff --git a/src/crypto/tls/tls.go b/src/crypto/tls/tls.go
index bfe331dd42..0be0b42912 100644
--- a/src/crypto/tls/tls.go
+++ b/src/crypto/tls/tls.go
@@ -209,12 +209,12 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
if len(cert.Certificate) == 0 {
if len(skippedBlockTypes) == 0 {
- return fail(errors.New("crypto/tls: failed to find any PEM data in certificate input"))
+ return fail(errors.New("tls: failed to find any PEM data in certificate input"))
}
if len(skippedBlockTypes) == 1 && strings.HasSuffix(skippedBlockTypes[0], "PRIVATE KEY") {
- return fail(errors.New("crypto/tls: failed to find certificate PEM data in certificate input, but did find a private key; PEM inputs may have been switched"))
+ return fail(errors.New("tls: failed to find certificate PEM data in certificate input, but did find a private key; PEM inputs may have been switched"))
}
- return fail(fmt.Errorf("crypto/tls: failed to find \"CERTIFICATE\" PEM block in certificate input after skipping PEM blocks of the following types: %v", skippedBlockTypes))
+ return fail(fmt.Errorf("tls: failed to find \"CERTIFICATE\" PEM block in certificate input after skipping PEM blocks of the following types: %v", skippedBlockTypes))
}
skippedBlockTypes = skippedBlockTypes[:0]
@@ -223,12 +223,12 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock)
if keyDERBlock == nil {
if len(skippedBlockTypes) == 0 {
- return fail(errors.New("crypto/tls: failed to find any PEM data in key input"))
+ return fail(errors.New("tls: failed to find any PEM data in key input"))
}
if len(skippedBlockTypes) == 1 && skippedBlockTypes[0] == "CERTIFICATE" {
- return fail(errors.New("crypto/tls: found a certificate rather than a key in the PEM for the private key"))
+ return fail(errors.New("tls: found a certificate rather than a key in the PEM for the private key"))
}
- return fail(fmt.Errorf("crypto/tls: failed to find PEM block with type ending in \"PRIVATE KEY\" in key input after skipping PEM blocks of the following types: %v", skippedBlockTypes))
+ return fail(fmt.Errorf("tls: failed to find PEM block with type ending in \"PRIVATE KEY\" in key input after skipping PEM blocks of the following types: %v", skippedBlockTypes))
}
if keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") {
break
@@ -253,21 +253,21 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
case *rsa.PublicKey:
priv, ok := cert.PrivateKey.(*rsa.PrivateKey)
if !ok {
- return fail(errors.New("crypto/tls: private key type does not match public key type"))
+ return fail(errors.New("tls: private key type does not match public key type"))
}
if pub.N.Cmp(priv.N) != 0 {
- return fail(errors.New("crypto/tls: private key does not match public key"))
+ return fail(errors.New("tls: private key does not match public key"))
}
case *ecdsa.PublicKey:
priv, ok := cert.PrivateKey.(*ecdsa.PrivateKey)
if !ok {
- return fail(errors.New("crypto/tls: private key type does not match public key type"))
+ return fail(errors.New("tls: private key type does not match public key type"))
}
if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 {
- return fail(errors.New("crypto/tls: private key does not match public key"))
+ return fail(errors.New("tls: private key does not match public key"))
}
default:
- return fail(errors.New("crypto/tls: unknown public key algorithm"))
+ return fail(errors.New("tls: unknown public key algorithm"))
}
return cert, nil
@@ -285,12 +285,12 @@ func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
case *rsa.PrivateKey, *ecdsa.PrivateKey:
return key, nil
default:
- return nil, errors.New("crypto/tls: found unknown private key type in PKCS#8 wrapping")
+ return nil, errors.New("tls: found unknown private key type in PKCS#8 wrapping")
}
}
if key, err := x509.ParseECPrivateKey(der); err == nil {
return key, nil
}
- return nil, errors.New("crypto/tls: failed to parse private key")
+ return nil, errors.New("tls: failed to parse private key")
}
diff --git a/src/crypto/x509/pkcs8.go b/src/crypto/x509/pkcs8.go
index ba19989cba..b304a3f63c 100644
--- a/src/crypto/x509/pkcs8.go
+++ b/src/crypto/x509/pkcs8.go
@@ -13,7 +13,7 @@ import (
// pkcs8 reflects an ASN.1, PKCS#8 PrivateKey. See
// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-8/pkcs-8v1_2.asn
-// and RFC5208.
+// and RFC 5208.
type pkcs8 struct {
Version int
Algo pkix.AlgorithmIdentifier
@@ -21,8 +21,8 @@ type pkcs8 struct {
// optional attributes omitted.
}
-// ParsePKCS8PrivateKey parses an unencrypted, PKCS#8 private key. See
-// http://www.rsa.com/rsalabs/node.asp?id=2130 and RFC5208.
+// ParsePKCS8PrivateKey parses an unencrypted, PKCS#8 private key.
+// See RFC 5208.
func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) {
var privKey pkcs8
if _, err := asn1.Unmarshal(der, &privKey); err != nil {
diff --git a/src/crypto/x509/sec1.go b/src/crypto/x509/sec1.go
index 5f1b3ecc7c..33f376c072 100644
--- a/src/crypto/x509/sec1.go
+++ b/src/crypto/x509/sec1.go
@@ -17,9 +17,9 @@ const ecPrivKeyVersion = 1
// ecPrivateKey reflects an ASN.1 Elliptic Curve Private Key Structure.
// References:
-// RFC5915
+// RFC 5915
// SEC1 - http://www.secg.org/sec1-v2.pdf
-// Per RFC5915 the NamedCurveOID is marked as ASN.1 OPTIONAL, however in
+// Per RFC 5915 the NamedCurveOID is marked as ASN.1 OPTIONAL, however in
// most cases it is not.
type ecPrivateKey struct {
Version int
diff --git a/src/crypto/x509/x509.go b/src/crypto/x509/x509.go
index d35c29434c..6004d5cd23 100644
--- a/src/crypto/x509/x509.go
+++ b/src/crypto/x509/x509.go
@@ -1133,7 +1133,7 @@ func parseCertificate(in *certificate) (*Certificate, error) {
if rest, err := asn1.Unmarshal(e.Value, &keyid); err != nil {
return nil, err
} else if len(rest) != 0 {
- return nil, errors.New("x509: trailing data after X.509 authority key-id")
+ return nil, errors.New("x509: trailing data after X.509 key-id")
}
out.SubjectKeyId = keyid
@@ -1587,21 +1587,21 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub, priv
return nil, err
}
- if len(parent.SubjectKeyId) > 0 {
- template.AuthorityKeyId = parent.SubjectKeyId
- }
-
- extensions, err := buildExtensions(template)
+ asn1Issuer, err := subjectBytes(parent)
if err != nil {
return
}
- asn1Issuer, err := subjectBytes(parent)
+ asn1Subject, err := subjectBytes(template)
if err != nil {
return
}
- asn1Subject, err := subjectBytes(template)
+ if !bytes.Equal(asn1Issuer, asn1Subject) && len(parent.SubjectKeyId) > 0 {
+ template.AuthorityKeyId = parent.SubjectKeyId
+ }
+
+ extensions, err := buildExtensions(template)
if err != nil {
return
}
diff --git a/src/crypto/x509/x509_test.go b/src/crypto/x509/x509_test.go
index cd70a27da3..c6448d39ab 100644
--- a/src/crypto/x509/x509_test.go
+++ b/src/crypto/x509/x509_test.go
@@ -96,6 +96,17 @@ tAboUGBxTDq3ZroNism3DaMIbKPyYrAqhKov1h5V
-----END RSA PRIVATE KEY-----
`
+var testPrivateKey *rsa.PrivateKey
+
+func init() {
+ block, _ := pem.Decode([]byte(pemPrivateKey))
+
+ var err error
+ if testPrivateKey, err = ParsePKCS1PrivateKey(block.Bytes); err != nil {
+ panic("Failed to parse private key: " + err.Error())
+ }
+}
+
func bigFromString(s string) *big.Int {
ret := new(big.Int)
ret.SetString(s, 10)
@@ -314,12 +325,6 @@ var certBytes = "308203223082028ba00302010202106edf0d9499fd4533dd1297fc42a93be13
func TestCreateSelfSignedCertificate(t *testing.T) {
random := rand.Reader
- block, _ := pem.Decode([]byte(pemPrivateKey))
- rsaPriv, err := ParsePKCS1PrivateKey(block.Bytes)
- if err != nil {
- t.Fatalf("Failed to parse private key: %s", err)
- }
-
ecdsaPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatalf("Failed to generate ECDSA key: %s", err)
@@ -331,9 +336,9 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
checkSig bool
sigAlgo SignatureAlgorithm
}{
- {"RSA/RSA", &rsaPriv.PublicKey, rsaPriv, true, SHA1WithRSA},
- {"RSA/ECDSA", &rsaPriv.PublicKey, ecdsaPriv, false, ECDSAWithSHA384},
- {"ECDSA/RSA", &ecdsaPriv.PublicKey, rsaPriv, false, SHA256WithRSA},
+ {"RSA/RSA", &testPrivateKey.PublicKey, testPrivateKey, true, SHA1WithRSA},
+ {"RSA/ECDSA", &testPrivateKey.PublicKey, ecdsaPriv, false, ECDSAWithSHA384},
+ {"ECDSA/RSA", &ecdsaPriv.PublicKey, testPrivateKey, false, SHA256WithRSA},
{"ECDSA/ECDSA", &ecdsaPriv.PublicKey, ecdsaPriv, true, ECDSAWithSHA1},
}
@@ -874,12 +879,6 @@ const pemCRLBase64 = "LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0tDQpNSUlCOWpDQ0FWOENBUUV3RF
func TestCreateCertificateRequest(t *testing.T) {
random := rand.Reader
- block, _ := pem.Decode([]byte(pemPrivateKey))
- rsaPriv, err := ParsePKCS1PrivateKey(block.Bytes)
- if err != nil {
- t.Fatalf("Failed to parse private key: %s", err)
- }
-
ecdsa256Priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatalf("Failed to generate ECDSA key: %s", err)
@@ -900,7 +899,7 @@ func TestCreateCertificateRequest(t *testing.T) {
priv interface{}
sigAlgo SignatureAlgorithm
}{
- {"RSA", rsaPriv, SHA1WithRSA},
+ {"RSA", testPrivateKey, SHA1WithRSA},
{"ECDSA-256", ecdsa256Priv, ECDSAWithSHA1},
{"ECDSA-384", ecdsa384Priv, ECDSAWithSHA1},
{"ECDSA-521", ecdsa521Priv, ECDSAWithSHA1},
@@ -951,13 +950,7 @@ func TestCreateCertificateRequest(t *testing.T) {
}
func marshalAndParseCSR(t *testing.T, template *CertificateRequest) *CertificateRequest {
- block, _ := pem.Decode([]byte(pemPrivateKey))
- rsaPriv, err := ParsePKCS1PrivateKey(block.Bytes)
- if err != nil {
- t.Fatal(err)
- }
-
- derBytes, err := CreateCertificateRequest(rand.Reader, template, rsaPriv)
+ derBytes, err := CreateCertificateRequest(rand.Reader, template, testPrivateKey)
if err != nil {
t.Fatal(err)
}
@@ -1113,13 +1106,25 @@ func TestCriticalFlagInCSRRequestedExtensions(t *testing.T) {
}
}
-func TestMaxPathLen(t *testing.T) {
- block, _ := pem.Decode([]byte(pemPrivateKey))
- rsaPriv, err := ParsePKCS1PrivateKey(block.Bytes)
+// serialiseAndParse generates a self-signed certificate from template and
+// returns a parsed version of it.
+func serialiseAndParse(t *testing.T, template *Certificate) *Certificate {
+ derBytes, err := CreateCertificate(rand.Reader, template, template, &testPrivateKey.PublicKey, testPrivateKey)
if err != nil {
- t.Fatalf("Failed to parse private key: %s", err)
+ t.Fatalf("failed to create certificate: %s", err)
+ return nil
}
+ cert, err := ParseCertificate(derBytes)
+ if err != nil {
+ t.Fatalf("failed to parse certificate: %s", err)
+ return nil
+ }
+
+ return cert
+}
+
+func TestMaxPathLen(t *testing.T) {
template := &Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
@@ -1132,23 +1137,7 @@ func TestMaxPathLen(t *testing.T) {
IsCA: true,
}
- serialiseAndParse := func(template *Certificate) *Certificate {
- derBytes, err := CreateCertificate(rand.Reader, template, template, &rsaPriv.PublicKey, rsaPriv)
- if err != nil {
- t.Fatalf("failed to create certificate: %s", err)
- return nil
- }
-
- cert, err := ParseCertificate(derBytes)
- if err != nil {
- t.Fatalf("failed to parse certificate: %s", err)
- return nil
- }
-
- return cert
- }
-
- cert1 := serialiseAndParse(template)
+ cert1 := serialiseAndParse(t, template)
if m := cert1.MaxPathLen; m != -1 {
t.Errorf("Omitting MaxPathLen didn't turn into -1, got %d", m)
}
@@ -1157,7 +1146,7 @@ func TestMaxPathLen(t *testing.T) {
}
template.MaxPathLen = 1
- cert2 := serialiseAndParse(template)
+ cert2 := serialiseAndParse(t, template)
if m := cert2.MaxPathLen; m != 1 {
t.Errorf("Setting MaxPathLen didn't work. Got %d but set 1", m)
}
@@ -1167,7 +1156,7 @@ func TestMaxPathLen(t *testing.T) {
template.MaxPathLen = 0
template.MaxPathLenZero = true
- cert3 := serialiseAndParse(template)
+ cert3 := serialiseAndParse(t, template)
if m := cert3.MaxPathLen; m != 0 {
t.Errorf("Setting MaxPathLenZero didn't work, got %d", m)
}
@@ -1176,6 +1165,30 @@ func TestMaxPathLen(t *testing.T) {
}
}
+func TestNoAuthorityKeyIdInSelfSignedCert(t *testing.T) {
+ template := &Certificate{
+ SerialNumber: big.NewInt(1),
+ Subject: pkix.Name{
+ CommonName: "Σ Acme Co",
+ },
+ NotBefore: time.Unix(1000, 0),
+ NotAfter: time.Unix(100000, 0),
+
+ BasicConstraintsValid: true,
+ IsCA: true,
+ SubjectKeyId: []byte{1, 2, 3, 4},
+ }
+
+ if cert := serialiseAndParse(t, template); len(cert.AuthorityKeyId) != 0 {
+ t.Fatalf("self-signed certificate contained default authority key id")
+ }
+
+ template.AuthorityKeyId = []byte{1, 2, 3, 4}
+ if cert := serialiseAndParse(t, template); len(cert.AuthorityKeyId) == 0 {
+ t.Fatalf("self-signed certificate erased explicit authority key id")
+ }
+}
+
func TestASN1BitLength(t *testing.T) {
tests := []struct {
bytes []byte
diff --git a/src/debug/dwarf/buf.go b/src/debug/dwarf/buf.go
index 7443043c11..24d266db10 100644
--- a/src/debug/dwarf/buf.go
+++ b/src/debug/dwarf/buf.go
@@ -157,7 +157,7 @@ func (b *buf) addr() uint64 {
case 4:
return uint64(b.uint32())
case 8:
- return uint64(b.uint64())
+ return b.uint64()
}
b.error("unknown address size")
return 0
diff --git a/src/debug/dwarf/line.go b/src/debug/dwarf/line.go
index b3b91ade62..ed82feef92 100644
--- a/src/debug/dwarf/line.go
+++ b/src/debug/dwarf/line.go
@@ -361,7 +361,7 @@ func (r *LineReader) step(entry *LineEntry) bool {
// Special opcode [DWARF2 6.2.5.1, DWARF4 6.2.5.1]
adjustedOpcode := opcode - r.opcodeBase
r.advancePC(adjustedOpcode / r.lineRange)
- lineDelta := r.lineBase + int(adjustedOpcode)%r.lineRange
+ lineDelta := r.lineBase + adjustedOpcode%r.lineRange
r.state.Line += lineDelta
goto emit
}
diff --git a/src/debug/dwarf/typeunit.go b/src/debug/dwarf/typeunit.go
index ed42547386..652e02d917 100644
--- a/src/debug/dwarf/typeunit.go
+++ b/src/debug/dwarf/typeunit.go
@@ -76,7 +76,7 @@ func (d *Data) parseTypes(name string, types []byte) error {
data: b.bytes(int(n - (b.off - hdroff))),
atable: atable,
asize: int(asize),
- vers: int(vers),
+ vers: vers,
is64: dwarf64,
},
toff: Offset(toff),
@@ -101,7 +101,7 @@ func (d *Data) sigToType(sig uint64) (Type, error) {
b := makeBuf(d, tu, tu.name, tu.off, tu.data)
r := &typeUnitReader{d: d, tu: tu, b: b}
- t, err := d.readType(tu.name, r, Offset(tu.toff), make(map[Offset]Type), nil)
+ t, err := d.readType(tu.name, r, tu.toff, make(map[Offset]Type), nil)
if err != nil {
return nil, err
}
diff --git a/src/debug/elf/elf.go b/src/debug/elf/elf.go
index af881c2495..3f43d4d896 100644
--- a/src/debug/elf/elf.go
+++ b/src/debug/elf/elf.go
@@ -2060,8 +2060,8 @@ type Rela32 struct {
Addend int32 /* Addend. */
}
-func R_SYM32(info uint32) uint32 { return uint32(info >> 8) }
-func R_TYPE32(info uint32) uint32 { return uint32(info & 0xff) }
+func R_SYM32(info uint32) uint32 { return info >> 8 }
+func R_TYPE32(info uint32) uint32 { return info & 0xff }
func R_INFO32(sym, typ uint32) uint32 { return sym<<8 | typ }
// ELF32 Symbol.
diff --git a/src/debug/elf/file.go b/src/debug/elf/file.go
index c28a964b73..c173ea9331 100644
--- a/src/debug/elf/file.go
+++ b/src/debug/elf/file.go
@@ -269,7 +269,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
switch f.Class {
case ELFCLASS32:
hdr := new(Header32)
- sr.Seek(0, os.SEEK_SET)
+ sr.Seek(0, io.SeekStart)
if err := binary.Read(sr, f.ByteOrder, hdr); err != nil {
return nil, err
}
@@ -288,13 +288,13 @@ func NewFile(r io.ReaderAt) (*File, error) {
shstrndx = int(hdr.Shstrndx)
case ELFCLASS64:
hdr := new(Header64)
- sr.Seek(0, os.SEEK_SET)
+ sr.Seek(0, io.SeekStart)
if err := binary.Read(sr, f.ByteOrder, hdr); err != nil {
return nil, err
}
f.Type = Type(hdr.Type)
f.Machine = Machine(hdr.Machine)
- f.Entry = uint64(hdr.Entry)
+ f.Entry = hdr.Entry
if v := Version(hdr.Version); v != f.Version {
return nil, &FormatError{0, "mismatched ELF version", v}
}
@@ -315,7 +315,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
f.Progs = make([]*Prog, phnum)
for i := 0; i < phnum; i++ {
off := phoff + int64(i)*int64(phentsize)
- sr.Seek(off, os.SEEK_SET)
+ sr.Seek(off, io.SeekStart)
p := new(Prog)
switch f.Class {
case ELFCLASS32:
@@ -341,12 +341,12 @@ func NewFile(r io.ReaderAt) (*File, error) {
p.ProgHeader = ProgHeader{
Type: ProgType(ph.Type),
Flags: ProgFlag(ph.Flags),
- Off: uint64(ph.Off),
- Vaddr: uint64(ph.Vaddr),
- Paddr: uint64(ph.Paddr),
- Filesz: uint64(ph.Filesz),
- Memsz: uint64(ph.Memsz),
- Align: uint64(ph.Align),
+ Off: ph.Off,
+ Vaddr: ph.Vaddr,
+ Paddr: ph.Paddr,
+ Filesz: ph.Filesz,
+ Memsz: ph.Memsz,
+ Align: ph.Align,
}
}
p.sr = io.NewSectionReader(r, int64(p.Off), int64(p.Filesz))
@@ -359,7 +359,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
names := make([]uint32, shnum)
for i := 0; i < shnum; i++ {
off := shoff + int64(i)*int64(shentsize)
- sr.Seek(off, os.SEEK_SET)
+ sr.Seek(off, io.SeekStart)
s := new(Section)
switch f.Class {
case ELFCLASS32:
@@ -374,8 +374,8 @@ func NewFile(r io.ReaderAt) (*File, error) {
Addr: uint64(sh.Addr),
Offset: uint64(sh.Off),
FileSize: uint64(sh.Size),
- Link: uint32(sh.Link),
- Info: uint32(sh.Info),
+ Link: sh.Link,
+ Info: sh.Info,
Addralign: uint64(sh.Addralign),
Entsize: uint64(sh.Entsize),
}
@@ -388,13 +388,13 @@ func NewFile(r io.ReaderAt) (*File, error) {
s.SectionHeader = SectionHeader{
Type: SectionType(sh.Type),
Flags: SectionFlag(sh.Flags),
- Offset: uint64(sh.Off),
- FileSize: uint64(sh.Size),
- Addr: uint64(sh.Addr),
- Link: uint32(sh.Link),
- Info: uint32(sh.Info),
- Addralign: uint64(sh.Addralign),
- Entsize: uint64(sh.Entsize),
+ Offset: sh.Off,
+ FileSize: sh.Size,
+ Addr: sh.Addr,
+ Link: sh.Link,
+ Info: sh.Info,
+ Addralign: sh.Addralign,
+ Entsize: sh.Entsize,
}
}
s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.FileSize))
diff --git a/src/debug/gosym/pclntab.go b/src/debug/gosym/pclntab.go
index 01a9f11f05..e859d5aed5 100644
--- a/src/debug/gosym/pclntab.go
+++ b/src/debug/gosym/pclntab.go
@@ -167,7 +167,7 @@ func (t *LineTable) go12Init() {
// Check header: 4-byte magic, two zeros, pc quantum, pointer size.
t.go12 = -1 // not Go 1.2 until proven otherwise
if len(t.Data) < 16 || t.Data[4] != 0 || t.Data[5] != 0 ||
- (t.Data[6] != 1 && t.Data[6] != 4) || // pc quantum
+ (t.Data[6] != 1 && t.Data[6] != 2 && t.Data[6] != 4) || // pc quantum
(t.Data[7] != 4 && t.Data[7] != 8) { // pointer size
return
}
@@ -207,8 +207,8 @@ func (t *LineTable) go12Funcs() []Func {
funcs := make([]Func, n)
for i := range funcs {
f := &funcs[i]
- f.Entry = uint64(t.uintptr(t.functab[2*i*int(t.ptrsize):]))
- f.End = uint64(t.uintptr(t.functab[(2*i+2)*int(t.ptrsize):]))
+ f.Entry = t.uintptr(t.functab[2*i*int(t.ptrsize):])
+ f.End = t.uintptr(t.functab[(2*i+2)*int(t.ptrsize):])
info := t.Data[t.uintptr(t.functab[(2*i+1)*int(t.ptrsize):]):]
f.LineTable = t
f.FrameSize = int(t.binary.Uint32(info[t.ptrsize+2*4:]))
diff --git a/src/debug/gosym/symtab.go b/src/debug/gosym/symtab.go
index 49e154fd8e..c8fa9a0b38 100644
--- a/src/debug/gosym/symtab.go
+++ b/src/debug/gosym/symtab.go
@@ -294,8 +294,8 @@ func NewTable(symtab []byte, pcln *LineTable) (*Table, error) {
t.Syms = t.Syms[0 : n+1]
ts := &t.Syms[n]
ts.Type = s.typ
- ts.Value = uint64(s.value)
- ts.GoType = uint64(s.gotype)
+ ts.Value = s.value
+ ts.GoType = s.gotype
switch s.typ {
default:
// rewrite name to use . instead of · (c2 b7)
diff --git a/src/debug/pe/file.go b/src/debug/pe/file.go
index 1acc368e1b..1cd84d5727 100644
--- a/src/debug/pe/file.go
+++ b/src/debug/pe/file.go
@@ -8,11 +8,9 @@ package pe
import (
"debug/dwarf"
"encoding/binary"
- "errors"
"fmt"
"io"
"os"
- "strconv"
)
// A File represents an open PE file.
@@ -20,83 +18,13 @@ type File struct {
FileHeader
OptionalHeader interface{} // of type *OptionalHeader32 or *OptionalHeader64
Sections []*Section
- Symbols []*Symbol
+ Symbols []*Symbol // COFF symbols with auxiliary symbol records removed
+ COFFSymbols []COFFSymbol // all COFF symbols (including auxiliary symbol records)
+ StringTable StringTable
closer io.Closer
}
-type SectionHeader struct {
- Name string
- VirtualSize uint32
- VirtualAddress uint32
- Size uint32
- Offset uint32
- PointerToRelocations uint32
- PointerToLineNumbers uint32
- NumberOfRelocations uint16
- NumberOfLineNumbers uint16
- Characteristics uint32
-}
-
-type Section struct {
- SectionHeader
-
- // Embed ReaderAt for ReadAt method.
- // Do not embed SectionReader directly
- // to avoid having Read and Seek.
- // If a client wants Read and Seek it must use
- // Open() to avoid fighting over the seek offset
- // with other clients.
- io.ReaderAt
- sr *io.SectionReader
-}
-
-type Symbol struct {
- Name string
- Value uint32
- SectionNumber int16
- Type uint16
- StorageClass uint8
-}
-
-type ImportDirectory struct {
- OriginalFirstThunk uint32
- TimeDateStamp uint32
- ForwarderChain uint32
- Name uint32
- FirstThunk uint32
-
- dll string
-}
-
-// Data reads and returns the contents of the PE section.
-func (s *Section) Data() ([]byte, error) {
- dat := make([]byte, s.sr.Size())
- n, err := s.sr.ReadAt(dat, 0)
- if n == len(dat) {
- err = nil
- }
- return dat[0:n], err
-}
-
-// Open returns a new ReadSeeker reading the PE section.
-func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
-
-type FormatError struct {
- off int64
- msg string
- val interface{}
-}
-
-func (e *FormatError) Error() string {
- msg := e.msg
- if e.val != nil {
- msg += fmt.Sprintf(" '%v'", e.val)
- }
- msg += fmt.Sprintf(" in record at byte %#x", e.off)
- return msg
-}
-
// Open opens the named file using os.Open and prepares it for use as a PE binary.
func Open(name string) (*File, error) {
f, err := os.Open(name)
@@ -129,6 +57,8 @@ var (
sizeofOptionalHeader64 = uint16(binary.Size(OptionalHeader64{}))
)
+// TODO(brainman): add Load function, as a replacement for NewFile, that does not call removeAuxSymbols (for performance)
+
// NewFile creates a new File for accessing a PE binary in an underlying reader.
func NewFile(r io.ReaderAt) (*File, error) {
f := new(File)
@@ -144,66 +74,42 @@ func NewFile(r io.ReaderAt) (*File, error) {
var sign [4]byte
r.ReadAt(sign[:], signoff)
if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) {
- return nil, errors.New("Invalid PE File Format.")
+ return nil, fmt.Errorf("Invalid PE COFF file signature of %v.", sign)
}
base = signoff + 4
} else {
base = int64(0)
}
- sr.Seek(base, os.SEEK_SET)
+ sr.Seek(base, io.SeekStart)
if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
return nil, err
}
- if f.FileHeader.Machine != IMAGE_FILE_MACHINE_UNKNOWN && f.FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 && f.FileHeader.Machine != IMAGE_FILE_MACHINE_I386 {
- return nil, errors.New("Invalid PE File Format.")
+ switch f.FileHeader.Machine {
+ case IMAGE_FILE_MACHINE_UNKNOWN, IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_I386:
+ default:
+ return nil, fmt.Errorf("Unrecognised COFF file header machine value of 0x%x.", f.FileHeader.Machine)
}
- var ss []byte
- if f.FileHeader.NumberOfSymbols > 0 {
- // Get COFF string table, which is located at the end of the COFF symbol table.
- sr.Seek(int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols), os.SEEK_SET)
- var l uint32
- if err := binary.Read(sr, binary.LittleEndian, &l); err != nil {
- return nil, err
- }
- ss = make([]byte, l)
- if _, err := r.ReadAt(ss, int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols)); err != nil {
- return nil, err
- }
+ var err error
- // Process COFF symbol table.
- sr.Seek(int64(f.FileHeader.PointerToSymbolTable), os.SEEK_SET)
- aux := uint8(0)
- for i := 0; i < int(f.FileHeader.NumberOfSymbols); i++ {
- cs := new(COFFSymbol)
- if err := binary.Read(sr, binary.LittleEndian, cs); err != nil {
- return nil, err
- }
- if aux > 0 {
- aux--
- continue
- }
- var name string
- if cs.Name[0] == 0 && cs.Name[1] == 0 && cs.Name[2] == 0 && cs.Name[3] == 0 {
- si := int(binary.LittleEndian.Uint32(cs.Name[4:]))
- name, _ = getString(ss, si)
- } else {
- name = cstring(cs.Name[:])
- }
- aux = cs.NumberOfAuxSymbols
- s := &Symbol{
- Name: name,
- Value: cs.Value,
- SectionNumber: cs.SectionNumber,
- Type: cs.Type,
- StorageClass: cs.StorageClass,
- }
- f.Symbols = append(f.Symbols, s)
- }
+ // Read string table.
+ f.StringTable, err = readStringTable(&f.FileHeader, sr)
+ if err != nil {
+ return nil, err
+ }
+
+ // Read symbol table.
+ f.COFFSymbols, err = readCOFFSymbols(&f.FileHeader, sr)
+ if err != nil {
+ return nil, err
+ }
+ f.Symbols, err = removeAuxSymbols(f.COFFSymbols, f.StringTable)
+ if err != nil {
+ return nil, err
}
// Read optional header.
- sr.Seek(base, os.SEEK_SET)
+ sr.Seek(base, io.SeekStart)
if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
return nil, err
}
@@ -235,12 +141,9 @@ func NewFile(r io.ReaderAt) (*File, error) {
if err := binary.Read(sr, binary.LittleEndian, sh); err != nil {
return nil, err
}
- var name string
- if sh.Name[0] == '\x2F' {
- si, _ := strconv.Atoi(cstring(sh.Name[1:]))
- name, _ = getString(ss, si)
- } else {
- name = cstring(sh.Name[0:])
+ name, err := sh.fullName(f.StringTable)
+ if err != nil {
+ return nil, err
}
s := new(Section)
s.SectionHeader = SectionHeader{
@@ -259,14 +162,15 @@ func NewFile(r io.ReaderAt) (*File, error) {
s.ReaderAt = s.sr
f.Sections[i] = s
}
- return f, nil
-}
-
-func cstring(b []byte) string {
- var i int
- for i = 0; i < len(b) && b[i] != 0; i++ {
+ for i := range f.Sections {
+ var err error
+ f.Sections[i].Relocs, err = readRelocs(&f.Sections[i].SectionHeader, sr)
+ if err != nil {
+ return nil, err
+ }
}
- return string(b[0:i])
+
+ return f, nil
}
// getString extracts a string from symbol string table.
@@ -320,6 +224,18 @@ func (f *File) DWARF() (*dwarf.Data, error) {
return dwarf.New(abbrev, nil, nil, info, line, nil, ranges, str)
}
+// TODO(brainman): document ImportDirectory once we decide what to do with it.
+
+type ImportDirectory struct {
+ OriginalFirstThunk uint32
+ TimeDateStamp uint32
+ ForwarderChain uint32
+ Name uint32
+ FirstThunk uint32
+
+ dll string
+}
+
// ImportedSymbols returns the names of all symbols
// referred to by the binary f that are expected to be
// satisfied by other libraries at dynamic load time.
@@ -347,6 +263,12 @@ func (f *File) ImportedSymbols() ([]string, error) {
}
ida = append(ida, dt)
}
+ // TODO(brainman): this needs to be rewritten
+ // ds.Data() return contets of .idata section. Why store in variable called "names"?
+ // Why we are retrieving it second time? We already have it in "d", and it is not modified anywhere.
+ // getString does not extracts a string from symbol string table (as getString doco says).
+ // Why ds.Data() called again and again in the loop?
+ // Needs test before rewrite.
names, _ := ds.Data()
var all []string
for _, dt := range ida {
@@ -395,3 +317,12 @@ func (f *File) ImportedLibraries() ([]string, error) {
// cgo -dynimport don't use this for windows PE, so just return.
return nil, nil
}
+
+// FormatError is unused.
+// The type is retained for compatibility.
+type FormatError struct {
+}
+
+func (e *FormatError) Error() string {
+ return "unknown error"
+}
diff --git a/src/debug/pe/pe.go b/src/debug/pe/pe.go
index 6af40e2c78..8050d59c70 100644
--- a/src/debug/pe/pe.go
+++ b/src/debug/pe/pe.go
@@ -86,30 +86,6 @@ type OptionalHeader64 struct {
DataDirectory [16]DataDirectory
}
-type SectionHeader32 struct {
- Name [8]uint8
- VirtualSize uint32
- VirtualAddress uint32
- SizeOfRawData uint32
- PointerToRawData uint32
- PointerToRelocations uint32
- PointerToLineNumbers uint32
- NumberOfRelocations uint16
- NumberOfLineNumbers uint16
- Characteristics uint32
-}
-
-const COFFSymbolSize = 18
-
-type COFFSymbol struct {
- Name [8]uint8
- Value uint32
- SectionNumber int16
- Type uint16
- StorageClass uint8
- NumberOfAuxSymbols uint8
-}
-
const (
IMAGE_FILE_MACHINE_UNKNOWN = 0x0
IMAGE_FILE_MACHINE_AM33 = 0x1d3
diff --git a/src/debug/pe/section.go b/src/debug/pe/section.go
new file mode 100644
index 0000000000..5d881577d3
--- /dev/null
+++ b/src/debug/pe/section.go
@@ -0,0 +1,111 @@
+// Copyright 2016 The Go 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 pe
+
+import (
+ "encoding/binary"
+ "fmt"
+ "io"
+ "strconv"
+)
+
+// SectionHeader32 represents real PE COFF section header.
+type SectionHeader32 struct {
+ Name [8]uint8
+ VirtualSize uint32
+ VirtualAddress uint32
+ SizeOfRawData uint32
+ PointerToRawData uint32
+ PointerToRelocations uint32
+ PointerToLineNumbers uint32
+ NumberOfRelocations uint16
+ NumberOfLineNumbers uint16
+ Characteristics uint32
+}
+
+// fullName finds real name of section sh. Normally name is stored
+// in sh.Name, but if it is longer then 8 characters, it is stored
+// in COFF string table st instead.
+func (sh *SectionHeader32) fullName(st StringTable) (string, error) {
+ if sh.Name[0] != '/' {
+ return cstring(sh.Name[:]), nil
+ }
+ i, err := strconv.Atoi(cstring(sh.Name[1:]))
+ if err != nil {
+ return "", err
+ }
+ return st.String(uint32(i))
+}
+
+// TODO(brainman): copy all IMAGE_REL_* consts from ldpe.go here
+
+// Reloc represents a PE COFF relocation.
+// Each section contains its own relocation list.
+type Reloc struct {
+ VirtualAddress uint32
+ SymbolTableIndex uint32
+ Type uint16
+}
+
+func readRelocs(sh *SectionHeader, r io.ReadSeeker) ([]Reloc, error) {
+ if sh.NumberOfRelocations <= 0 {
+ return nil, nil
+ }
+ _, err := r.Seek(int64(sh.PointerToRelocations), io.SeekStart)
+ if err != nil {
+ return nil, fmt.Errorf("fail to seek to %q section relocations: %v", sh.Name, err)
+ }
+ relocs := make([]Reloc, sh.NumberOfRelocations)
+ err = binary.Read(r, binary.LittleEndian, relocs)
+ if err != nil {
+ return nil, fmt.Errorf("fail to read section relocations: %v", err)
+ }
+ return relocs, nil
+}
+
+// SectionHeader is similar to SectionHeader32 with Name
+// field replaced by Go string.
+type SectionHeader struct {
+ Name string
+ VirtualSize uint32
+ VirtualAddress uint32
+ Size uint32
+ Offset uint32
+ PointerToRelocations uint32
+ PointerToLineNumbers uint32
+ NumberOfRelocations uint16
+ NumberOfLineNumbers uint16
+ Characteristics uint32
+}
+
+// Section provides access to PE COFF section.
+type Section struct {
+ SectionHeader
+ Relocs []Reloc
+
+ // Embed ReaderAt for ReadAt method.
+ // Do not embed SectionReader directly
+ // to avoid having Read and Seek.
+ // If a client wants Read and Seek it must use
+ // Open() to avoid fighting over the seek offset
+ // with other clients.
+ io.ReaderAt
+ sr *io.SectionReader
+}
+
+// Data reads and returns the contents of the PE section s.
+func (s *Section) Data() ([]byte, error) {
+ dat := make([]byte, s.sr.Size())
+ n, err := s.sr.ReadAt(dat, 0)
+ if n == len(dat) {
+ err = nil
+ }
+ return dat[0:n], err
+}
+
+// Open returns a new ReadSeeker reading the PE section s.
+func (s *Section) Open() io.ReadSeeker {
+ return io.NewSectionReader(s.sr, 0, 1<<63-1)
+}
diff --git a/src/debug/pe/string.go b/src/debug/pe/string.go
new file mode 100644
index 0000000000..e00bd97dd4
--- /dev/null
+++ b/src/debug/pe/string.go
@@ -0,0 +1,63 @@
+// Copyright 2016 The Go 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 pe
+
+import (
+ "encoding/binary"
+ "fmt"
+ "io"
+)
+
+// cstring converts ASCII byte sequence b to string.
+// It stops once it finds 0 or reaches end of b.
+func cstring(b []byte) string {
+ var i int
+ for i = 0; i < len(b) && b[i] != 0; i++ {
+ }
+ return string(b[:i])
+}
+
+// StringTable is a COFF string table.
+type StringTable []byte
+
+func readStringTable(fh *FileHeader, r io.ReadSeeker) (StringTable, error) {
+ // COFF string table is located right after COFF symbol table.
+ offset := fh.PointerToSymbolTable + COFFSymbolSize*fh.NumberOfSymbols
+ _, err := r.Seek(int64(offset), io.SeekStart)
+ if err != nil {
+ return nil, fmt.Errorf("fail to seek to string table: %v", err)
+ }
+ var l uint32
+ err = binary.Read(r, binary.LittleEndian, &l)
+ if err != nil {
+ return nil, fmt.Errorf("fail to read string table length: %v", err)
+ }
+ // string table length includes itself
+ if l <= 4 {
+ return nil, nil
+ }
+ l -= 4
+ buf := make([]byte, l)
+ _, err = io.ReadFull(r, buf)
+ if err != nil {
+ return nil, fmt.Errorf("fail to read string table: %v", err)
+ }
+ return StringTable(buf), nil
+}
+
+// TODO(brainman): decide if start parameter should be int instead of uint32
+
+// String extracts string from COFF string table st at offset start.
+func (st StringTable) String(start uint32) (string, error) {
+ // start includes 4 bytes of string table length
+ if start < 4 {
+ return "", fmt.Errorf("offset %d is before the start of string table", start)
+ }
+ start -= 4
+ if int(start) > len(st) {
+ return "", fmt.Errorf("offset %d is beyond the end of string table", start)
+ }
+ return cstring(st[start:]), nil
+}
diff --git a/src/debug/pe/symbol.go b/src/debug/pe/symbol.go
new file mode 100644
index 0000000000..3cf5a101e7
--- /dev/null
+++ b/src/debug/pe/symbol.go
@@ -0,0 +1,95 @@
+// Copyright 2016 The Go 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 pe
+
+import (
+ "encoding/binary"
+ "fmt"
+ "io"
+)
+
+const COFFSymbolSize = 18
+
+// COFFSymbol represents single COFF symbol table record.
+type COFFSymbol struct {
+ Name [8]uint8
+ Value uint32
+ SectionNumber int16
+ Type uint16
+ StorageClass uint8
+ NumberOfAuxSymbols uint8
+}
+
+func readCOFFSymbols(fh *FileHeader, r io.ReadSeeker) ([]COFFSymbol, error) {
+ if fh.NumberOfSymbols <= 0 {
+ return nil, nil
+ }
+ _, err := r.Seek(int64(fh.PointerToSymbolTable), io.SeekStart)
+ if err != nil {
+ return nil, fmt.Errorf("fail to seek to symbol table: %v", err)
+ }
+ syms := make([]COFFSymbol, fh.NumberOfSymbols)
+ err = binary.Read(r, binary.LittleEndian, syms)
+ if err != nil {
+ return nil, fmt.Errorf("fail to read symbol table: %v", err)
+ }
+ return syms, nil
+}
+
+// isSymNameOffset checks symbol name if it is encoded as offset into string table.
+func isSymNameOffset(name [8]byte) (bool, uint32) {
+ if name[0] == 0 && name[1] == 0 && name[2] == 0 && name[3] == 0 {
+ return true, binary.LittleEndian.Uint32(name[4:])
+ }
+ return false, 0
+}
+
+// FullName finds real name of symbol sym. Normally name is stored
+// in sym.Name, but if it is longer then 8 characters, it is stored
+// in COFF string table st instead.
+func (sym *COFFSymbol) FullName(st StringTable) (string, error) {
+ if ok, offset := isSymNameOffset(sym.Name); ok {
+ return st.String(offset)
+ }
+ return cstring(sym.Name[:]), nil
+}
+
+func removeAuxSymbols(allsyms []COFFSymbol, st StringTable) ([]*Symbol, error) {
+ if len(allsyms) == 0 {
+ return nil, nil
+ }
+ syms := make([]*Symbol, 0)
+ aux := uint8(0)
+ for _, sym := range allsyms {
+ if aux > 0 {
+ aux--
+ continue
+ }
+ name, err := sym.FullName(st)
+ if err != nil {
+ return nil, err
+ }
+ aux = sym.NumberOfAuxSymbols
+ s := &Symbol{
+ Name: name,
+ Value: sym.Value,
+ SectionNumber: sym.SectionNumber,
+ Type: sym.Type,
+ StorageClass: sym.StorageClass,
+ }
+ syms = append(syms, s)
+ }
+ return syms, nil
+}
+
+// Symbol is similar to COFFSymbol with Name field replaced
+// by Go string. Symbol also does not have NumberOfAuxSymbols.
+type Symbol struct {
+ Name string
+ Value uint32
+ SectionNumber int16
+ Type uint16
+ StorageClass uint8
+}
diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go
index bd2c96d887..2b5ad08551 100644
--- a/src/encoding/asn1/asn1.go
+++ b/src/encoding/asn1/asn1.go
@@ -393,7 +393,7 @@ func isPrintable(b byte) bool {
// byte slice and returns it.
func parseIA5String(bytes []byte) (ret string, err error) {
for _, b := range bytes {
- if b >= 0x80 {
+ if b >= utf8.RuneSelf {
err = SyntaxError{"IA5String contains invalid character"}
return
}
diff --git a/src/encoding/asn1/marshal.go b/src/encoding/asn1/marshal.go
index 2b796c4e75..30797ef099 100644
--- a/src/encoding/asn1/marshal.go
+++ b/src/encoding/asn1/marshal.go
@@ -315,9 +315,9 @@ func marshalUTCTime(out *forkableWriter, t time.Time) (err error) {
switch {
case 1950 <= year && year < 2000:
- err = marshalTwoDigits(out, int(year-1900))
+ err = marshalTwoDigits(out, year-1900)
case 2000 <= year && year < 2050:
- err = marshalTwoDigits(out, int(year-2000))
+ err = marshalTwoDigits(out, year-2000)
default:
return StructuralError{"cannot represent time as UTCTime"}
}
@@ -435,7 +435,7 @@ func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameter
return out.WriteByte(0)
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return marshalInt64(out, int64(v.Int()))
+ return marshalInt64(out, v.Int())
case reflect.Struct:
t := v.Type()
diff --git a/src/encoding/binary/binary.go b/src/encoding/binary/binary.go
index ada5768695..46c6add062 100644
--- a/src/encoding/binary/binary.go
+++ b/src/encoding/binary/binary.go
@@ -269,7 +269,7 @@ func Write(w io.Writer, order ByteOrder, data interface{}) error {
case *uint8:
b[0] = *v
case uint8:
- b[0] = byte(v)
+ b[0] = v
case []uint8:
bs = v
case *int16:
diff --git a/src/encoding/gob/doc.go b/src/encoding/gob/doc.go
index cf878f4502..6f86d84891 100644
--- a/src/encoding/gob/doc.go
+++ b/src/encoding/gob/doc.go
@@ -254,6 +254,12 @@ In summary, a gob stream looks like
where * signifies zero or more repetitions and the type id of a value must
be predefined or be defined before the value in the stream.
+Compatibility: Any future changes to the package will endeavor to maintain
+compatibility with streams encoded using previous versions. That is, any released
+version of this package should be able to decode data written with any previously
+released version, subject to issues such as security fixes. See the Go compatibility
+document for background: https://golang.org/doc/go1compat
+
See "Gobs of data" for a design discussion of the gob wire format:
https://blog.golang.org/gobs-of-data
*/
diff --git a/src/encoding/gob/encode.go b/src/encoding/gob/encode.go
index 2b3a556eac..50cd6adb46 100644
--- a/src/encoding/gob/encode.go
+++ b/src/encoding/gob/encode.go
@@ -127,7 +127,7 @@ func (state *encoderState) encodeInt(i int64) {
} else {
x = uint64(i << 1)
}
- state.encodeUint(uint64(x))
+ state.encodeUint(x)
}
// encOp is the signature of an encoding operator for a given type.
diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go
index a7ff8cf3dc..434edf8ea4 100644
--- a/src/encoding/json/decode.go
+++ b/src/encoding/json/decode.go
@@ -97,7 +97,7 @@ func Unmarshal(data []byte, v interface{}) error {
return d.unmarshal(v)
}
-// Unmarshaler is the interface implemented by objects
+// Unmarshaler is the interface implemented by types
// that can unmarshal a JSON description of themselves.
// The input can be assumed to be a valid encoding of
// a JSON value. UnmarshalJSON must copy the JSON data
diff --git a/src/encoding/json/encode.go b/src/encoding/json/encode.go
index bcae6838cc..d8c779869b 100644
--- a/src/encoding/json/encode.go
+++ b/src/encoding/json/encode.go
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package json implements encoding and decoding of JSON objects as defined in
-// RFC 4627. The mapping between JSON objects and Go values is described
+// Package json implements encoding and decoding of JSON as defined in
+// RFC 4627. The mapping between JSON and Go values is described
// in the documentation for the Marshal and Unmarshal functions.
//
// See "JSON and Go" for an introduction to this package:
@@ -49,10 +49,11 @@ import (
// The angle brackets "<" and ">" are escaped to "\u003c" and "\u003e"
// to keep some browsers from misinterpreting JSON output as HTML.
// Ampersand "&" is also escaped to "\u0026" for the same reason.
+// This escaping can be disabled using an Encoder with DisableHTMLEscaping.
//
// Array and slice values encode as JSON arrays, except that
// []byte encodes as a base64-encoded string, and a nil slice
-// encodes as the null JSON object.
+// encodes as the null JSON value.
//
// Struct values encode as JSON objects. Each exported struct field
// becomes a member of the object unless
@@ -121,10 +122,10 @@ import (
// keys, subject to the UTF-8 coercion described for string values above.
//
// Pointer values encode as the value pointed to.
-// A nil pointer encodes as the null JSON object.
+// A nil pointer encodes as the null JSON value.
//
// Interface values encode as the value contained in the interface.
-// A nil interface value encodes as the null JSON object.
+// A nil interface value encodes as the null JSON value.
//
// Channel, complex, and function values cannot be encoded in JSON.
// Attempting to encode such a value causes Marshal to return
@@ -136,7 +137,7 @@ import (
//
func Marshal(v interface{}) ([]byte, error) {
e := &encodeState{}
- err := e.marshal(v)
+ err := e.marshal(v, encOpts{escapeHTML: true})
if err != nil {
return nil, err
}
@@ -192,7 +193,7 @@ func HTMLEscape(dst *bytes.Buffer, src []byte) {
}
}
-// Marshaler is the interface implemented by objects that
+// Marshaler is the interface implemented by types that
// can marshal themselves into valid JSON.
type Marshaler interface {
MarshalJSON() ([]byte, error)
@@ -259,7 +260,7 @@ func newEncodeState() *encodeState {
return new(encodeState)
}
-func (e *encodeState) marshal(v interface{}) (err error) {
+func (e *encodeState) marshal(v interface{}, opts encOpts) (err error) {
defer func() {
if r := recover(); r != nil {
if _, ok := r.(runtime.Error); ok {
@@ -271,7 +272,7 @@ func (e *encodeState) marshal(v interface{}) (err error) {
err = r.(error)
}
}()
- e.reflectValue(reflect.ValueOf(v))
+ e.reflectValue(reflect.ValueOf(v), opts)
return nil
}
@@ -297,11 +298,18 @@ func isEmptyValue(v reflect.Value) bool {
return false
}
-func (e *encodeState) reflectValue(v reflect.Value) {
- valueEncoder(v)(e, v, false)
+func (e *encodeState) reflectValue(v reflect.Value, opts encOpts) {
+ valueEncoder(v)(e, v, opts)
}
-type encoderFunc func(e *encodeState, v reflect.Value, quoted bool)
+type encOpts struct {
+ // quoted causes primitive fields to be encoded inside JSON strings.
+ quoted bool
+ // escapeHTML causes '<', '>', and '&' to be escaped in JSON strings.
+ escapeHTML bool
+}
+
+type encoderFunc func(e *encodeState, v reflect.Value, opts encOpts)
var encoderCache struct {
sync.RWMutex
@@ -333,9 +341,9 @@ func typeEncoder(t reflect.Type) encoderFunc {
}
var wg sync.WaitGroup
wg.Add(1)
- encoderCache.m[t] = func(e *encodeState, v reflect.Value, quoted bool) {
+ encoderCache.m[t] = func(e *encodeState, v reflect.Value, opts encOpts) {
wg.Wait()
- f(e, v, quoted)
+ f(e, v, opts)
}
encoderCache.Unlock()
@@ -405,11 +413,11 @@ func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc {
}
}
-func invalidValueEncoder(e *encodeState, v reflect.Value, quoted bool) {
+func invalidValueEncoder(e *encodeState, v reflect.Value, _ encOpts) {
e.WriteString("null")
}
-func marshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
+func marshalerEncoder(e *encodeState, v reflect.Value, opts encOpts) {
if v.Kind() == reflect.Ptr && v.IsNil() {
e.WriteString("null")
return
@@ -418,14 +426,14 @@ func marshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
b, err := m.MarshalJSON()
if err == nil {
// copy JSON into buffer, checking validity.
- err = compact(&e.Buffer, b, true)
+ err = compact(&e.Buffer, b, opts.escapeHTML)
}
if err != nil {
e.error(&MarshalerError{v.Type(), err})
}
}
-func addrMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
+func addrMarshalerEncoder(e *encodeState, v reflect.Value, _ encOpts) {
va := v.Addr()
if va.IsNil() {
e.WriteString("null")
@@ -442,7 +450,7 @@ func addrMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
}
}
-func textMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
+func textMarshalerEncoder(e *encodeState, v reflect.Value, opts encOpts) {
if v.Kind() == reflect.Ptr && v.IsNil() {
e.WriteString("null")
return
@@ -452,10 +460,10 @@ func textMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
if err != nil {
e.error(&MarshalerError{v.Type(), err})
}
- e.stringBytes(b)
+ e.stringBytes(b, opts.escapeHTML)
}
-func addrTextMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
+func addrTextMarshalerEncoder(e *encodeState, v reflect.Value, opts encOpts) {
va := v.Addr()
if va.IsNil() {
e.WriteString("null")
@@ -466,11 +474,11 @@ func addrTextMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
if err != nil {
e.error(&MarshalerError{v.Type(), err})
}
- e.stringBytes(b)
+ e.stringBytes(b, opts.escapeHTML)
}
-func boolEncoder(e *encodeState, v reflect.Value, quoted bool) {
- if quoted {
+func boolEncoder(e *encodeState, v reflect.Value, opts encOpts) {
+ if opts.quoted {
e.WriteByte('"')
}
if v.Bool() {
@@ -478,46 +486,46 @@ func boolEncoder(e *encodeState, v reflect.Value, quoted bool) {
} else {
e.WriteString("false")
}
- if quoted {
+ if opts.quoted {
e.WriteByte('"')
}
}
-func intEncoder(e *encodeState, v reflect.Value, quoted bool) {
+func intEncoder(e *encodeState, v reflect.Value, opts encOpts) {
b := strconv.AppendInt(e.scratch[:0], v.Int(), 10)
- if quoted {
+ if opts.quoted {
e.WriteByte('"')
}
e.Write(b)
- if quoted {
+ if opts.quoted {
e.WriteByte('"')
}
}
-func uintEncoder(e *encodeState, v reflect.Value, quoted bool) {
+func uintEncoder(e *encodeState, v reflect.Value, opts encOpts) {
b := strconv.AppendUint(e.scratch[:0], v.Uint(), 10)
- if quoted {
+ if opts.quoted {
e.WriteByte('"')
}
e.Write(b)
- if quoted {
+ if opts.quoted {
e.WriteByte('"')
}
}
type floatEncoder int // number of bits
-func (bits floatEncoder) encode(e *encodeState, v reflect.Value, quoted bool) {
+func (bits floatEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
f := v.Float()
if math.IsInf(f, 0) || math.IsNaN(f) {
e.error(&UnsupportedValueError{v, strconv.FormatFloat(f, 'g', -1, int(bits))})
}
b := strconv.AppendFloat(e.scratch[:0], f, 'g', -1, int(bits))
- if quoted {
+ if opts.quoted {
e.WriteByte('"')
}
e.Write(b)
- if quoted {
+ if opts.quoted {
e.WriteByte('"')
}
}
@@ -527,7 +535,7 @@ var (
float64Encoder = (floatEncoder(64)).encode
)
-func stringEncoder(e *encodeState, v reflect.Value, quoted bool) {
+func stringEncoder(e *encodeState, v reflect.Value, opts encOpts) {
if v.Type() == numberType {
numStr := v.String()
// In Go1.5 the empty string encodes to "0", while this is not a valid number literal
@@ -541,26 +549,26 @@ func stringEncoder(e *encodeState, v reflect.Value, quoted bool) {
e.WriteString(numStr)
return
}
- if quoted {
+ if opts.quoted {
sb, err := Marshal(v.String())
if err != nil {
e.error(err)
}
- e.string(string(sb))
+ e.string(string(sb), opts.escapeHTML)
} else {
- e.string(v.String())
+ e.string(v.String(), opts.escapeHTML)
}
}
-func interfaceEncoder(e *encodeState, v reflect.Value, quoted bool) {
+func interfaceEncoder(e *encodeState, v reflect.Value, opts encOpts) {
if v.IsNil() {
e.WriteString("null")
return
}
- e.reflectValue(v.Elem())
+ e.reflectValue(v.Elem(), opts)
}
-func unsupportedTypeEncoder(e *encodeState, v reflect.Value, quoted bool) {
+func unsupportedTypeEncoder(e *encodeState, v reflect.Value, _ encOpts) {
e.error(&UnsupportedTypeError{v.Type()})
}
@@ -569,7 +577,7 @@ type structEncoder struct {
fieldEncs []encoderFunc
}
-func (se *structEncoder) encode(e *encodeState, v reflect.Value, quoted bool) {
+func (se *structEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
e.WriteByte('{')
first := true
for i, f := range se.fields {
@@ -582,9 +590,10 @@ func (se *structEncoder) encode(e *encodeState, v reflect.Value, quoted bool) {
} else {
e.WriteByte(',')
}
- e.string(f.name)
+ e.string(f.name, opts.escapeHTML)
e.WriteByte(':')
- se.fieldEncs[i](e, fv, f.quoted)
+ opts.quoted = f.quoted
+ se.fieldEncs[i](e, fv, opts)
}
e.WriteByte('}')
}
@@ -605,7 +614,7 @@ type mapEncoder struct {
elemEnc encoderFunc
}
-func (me *mapEncoder) encode(e *encodeState, v reflect.Value, _ bool) {
+func (me *mapEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
if v.IsNil() {
e.WriteString("null")
return
@@ -627,9 +636,9 @@ func (me *mapEncoder) encode(e *encodeState, v reflect.Value, _ bool) {
if i > 0 {
e.WriteByte(',')
}
- e.string(kv.s)
+ e.string(kv.s, opts.escapeHTML)
e.WriteByte(':')
- me.elemEnc(e, v.MapIndex(kv.v), false)
+ me.elemEnc(e, v.MapIndex(kv.v), opts)
}
e.WriteByte('}')
}
@@ -642,7 +651,7 @@ func newMapEncoder(t reflect.Type) encoderFunc {
return me.encode
}
-func encodeByteSlice(e *encodeState, v reflect.Value, _ bool) {
+func encodeByteSlice(e *encodeState, v reflect.Value, _ encOpts) {
if v.IsNil() {
e.WriteString("null")
return
@@ -669,17 +678,19 @@ type sliceEncoder struct {
arrayEnc encoderFunc
}
-func (se *sliceEncoder) encode(e *encodeState, v reflect.Value, _ bool) {
+func (se *sliceEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
if v.IsNil() {
e.WriteString("null")
return
}
- se.arrayEnc(e, v, false)
+ se.arrayEnc(e, v, opts)
}
func newSliceEncoder(t reflect.Type) encoderFunc {
// Byte slices get special treatment; arrays don't.
- if t.Elem().Kind() == reflect.Uint8 {
+ if t.Elem().Kind() == reflect.Uint8 &&
+ !t.Elem().Implements(marshalerType) &&
+ !t.Elem().Implements(textMarshalerType) {
return encodeByteSlice
}
enc := &sliceEncoder{newArrayEncoder(t)}
@@ -690,14 +701,14 @@ type arrayEncoder struct {
elemEnc encoderFunc
}
-func (ae *arrayEncoder) encode(e *encodeState, v reflect.Value, _ bool) {
+func (ae *arrayEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
e.WriteByte('[')
n := v.Len()
for i := 0; i < n; i++ {
if i > 0 {
e.WriteByte(',')
}
- ae.elemEnc(e, v.Index(i), false)
+ ae.elemEnc(e, v.Index(i), opts)
}
e.WriteByte(']')
}
@@ -711,12 +722,12 @@ type ptrEncoder struct {
elemEnc encoderFunc
}
-func (pe *ptrEncoder) encode(e *encodeState, v reflect.Value, quoted bool) {
+func (pe *ptrEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
if v.IsNil() {
e.WriteString("null")
return
}
- pe.elemEnc(e, v.Elem(), quoted)
+ pe.elemEnc(e, v.Elem(), opts)
}
func newPtrEncoder(t reflect.Type) encoderFunc {
@@ -728,11 +739,11 @@ type condAddrEncoder struct {
canAddrEnc, elseEnc encoderFunc
}
-func (ce *condAddrEncoder) encode(e *encodeState, v reflect.Value, quoted bool) {
+func (ce *condAddrEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
if v.CanAddr() {
- ce.canAddrEnc(e, v, quoted)
+ ce.canAddrEnc(e, v, opts)
} else {
- ce.elseEnc(e, v, quoted)
+ ce.elseEnc(e, v, opts)
}
}
@@ -810,13 +821,14 @@ func (sv byString) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] }
func (sv byString) Less(i, j int) bool { return sv[i].s < sv[j].s }
// NOTE: keep in sync with stringBytes below.
-func (e *encodeState) string(s string) int {
+func (e *encodeState) string(s string, escapeHTML bool) int {
len0 := e.Len()
e.WriteByte('"')
start := 0
for i := 0; i < len(s); {
if b := s[i]; b < utf8.RuneSelf {
- if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' {
+ if 0x20 <= b && b != '\\' && b != '"' &&
+ (!escapeHTML || b != '<' && b != '>' && b != '&') {
i++
continue
}
@@ -837,10 +849,11 @@ func (e *encodeState) string(s string) int {
e.WriteByte('\\')
e.WriteByte('t')
default:
- // This encodes bytes < 0x20 except for \n and \r,
- // as well as <, > and &. The latter are escaped because they
- // can lead to security holes when user-controlled strings
- // are rendered into JSON and served to some browsers.
+ // This encodes bytes < 0x20 except for \t, \n and \r.
+ // If escapeHTML is set, it also escapes <, >, and &
+ // because they can lead to security holes when
+ // user-controlled strings are rendered into JSON
+ // and served to some browsers.
e.WriteString(`\u00`)
e.WriteByte(hex[b>>4])
e.WriteByte(hex[b&0xF])
@@ -886,13 +899,14 @@ func (e *encodeState) string(s string) int {
}
// NOTE: keep in sync with string above.
-func (e *encodeState) stringBytes(s []byte) int {
+func (e *encodeState) stringBytes(s []byte, escapeHTML bool) int {
len0 := e.Len()
e.WriteByte('"')
start := 0
for i := 0; i < len(s); {
if b := s[i]; b < utf8.RuneSelf {
- if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' {
+ if 0x20 <= b && b != '\\' && b != '"' &&
+ (!escapeHTML || b != '<' && b != '>' && b != '&') {
i++
continue
}
@@ -913,10 +927,11 @@ func (e *encodeState) stringBytes(s []byte) int {
e.WriteByte('\\')
e.WriteByte('t')
default:
- // This encodes bytes < 0x20 except for \n and \r,
- // as well as <, >, and &. The latter are escaped because they
- // can lead to security holes when user-controlled strings
- // are rendered into JSON and served to some browsers.
+ // This encodes bytes < 0x20 except for \t, \n and \r.
+ // If escapeHTML is set, it also escapes <, >, and &
+ // because they can lead to security holes when
+ // user-controlled strings are rendered into JSON
+ // and served to some browsers.
e.WriteString(`\u00`)
e.WriteByte(hex[b>>4])
e.WriteByte(hex[b&0xF])
diff --git a/src/encoding/json/encode_test.go b/src/encoding/json/encode_test.go
index eed40a4272..b484022a70 100644
--- a/src/encoding/json/encode_test.go
+++ b/src/encoding/json/encode_test.go
@@ -6,6 +6,7 @@ package json
import (
"bytes"
+ "fmt"
"math"
"reflect"
"testing"
@@ -375,41 +376,45 @@ func TestDuplicatedFieldDisappears(t *testing.T) {
func TestStringBytes(t *testing.T) {
// Test that encodeState.stringBytes and encodeState.string use the same encoding.
- es := &encodeState{}
var r []rune
for i := '\u0000'; i <= unicode.MaxRune; i++ {
r = append(r, i)
}
s := string(r) + "\xff\xff\xffhello" // some invalid UTF-8 too
- es.string(s)
- esBytes := &encodeState{}
- esBytes.stringBytes([]byte(s))
+ for _, escapeHTML := range []bool{true, false} {
+ es := &encodeState{}
+ es.string(s, escapeHTML)
- enc := es.Buffer.String()
- encBytes := esBytes.Buffer.String()
- if enc != encBytes {
- i := 0
- for i < len(enc) && i < len(encBytes) && enc[i] == encBytes[i] {
- i++
- }
- enc = enc[i:]
- encBytes = encBytes[i:]
- i = 0
- for i < len(enc) && i < len(encBytes) && enc[len(enc)-i-1] == encBytes[len(encBytes)-i-1] {
- i++
- }
- enc = enc[:len(enc)-i]
- encBytes = encBytes[:len(encBytes)-i]
+ esBytes := &encodeState{}
+ esBytes.stringBytes([]byte(s), escapeHTML)
- if len(enc) > 20 {
- enc = enc[:20] + "..."
- }
- if len(encBytes) > 20 {
- encBytes = encBytes[:20] + "..."
- }
+ enc := es.Buffer.String()
+ encBytes := esBytes.Buffer.String()
+ if enc != encBytes {
+ i := 0
+ for i < len(enc) && i < len(encBytes) && enc[i] == encBytes[i] {
+ i++
+ }
+ enc = enc[i:]
+ encBytes = encBytes[i:]
+ i = 0
+ for i < len(enc) && i < len(encBytes) && enc[len(enc)-i-1] == encBytes[len(encBytes)-i-1] {
+ i++
+ }
+ enc = enc[:len(enc)-i]
+ encBytes = encBytes[:len(encBytes)-i]
- t.Errorf("encodings differ at %#q vs %#q", enc, encBytes)
+ if len(enc) > 20 {
+ enc = enc[:20] + "..."
+ }
+ if len(encBytes) > 20 {
+ encBytes = encBytes[:20] + "..."
+ }
+
+ t.Errorf("with escapeHTML=%t, encodings differ at %#q vs %#q",
+ escapeHTML, enc, encBytes)
+ }
}
}
@@ -537,6 +542,60 @@ func TestEncodeString(t *testing.T) {
}
}
+type jsonbyte byte
+
+func (b jsonbyte) MarshalJSON() ([]byte, error) { return tenc(`{"JB":%d}`, b) }
+
+type textbyte byte
+
+func (b textbyte) MarshalText() ([]byte, error) { return tenc(`TB:%d`, b) }
+
+type jsonint int
+
+func (i jsonint) MarshalJSON() ([]byte, error) { return tenc(`{"JI":%d}`, i) }
+
+type textint int
+
+func (i textint) MarshalText() ([]byte, error) { return tenc(`TI:%d`, i) }
+
+func tenc(format string, a ...interface{}) ([]byte, error) {
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, format, a...)
+ return buf.Bytes(), nil
+}
+
+// Issue 13783
+func TestEncodeBytekind(t *testing.T) {
+ testdata := []struct {
+ data interface{}
+ want string
+ }{
+ {byte(7), "7"},
+ {jsonbyte(7), `{"JB":7}`},
+ {textbyte(4), `"TB:4"`},
+ {jsonint(5), `{"JI":5}`},
+ {textint(1), `"TI:1"`},
+ {[]byte{0, 1}, `"AAE="`},
+ {[]jsonbyte{0, 1}, `[{"JB":0},{"JB":1}]`},
+ {[][]jsonbyte{{0, 1}, {3}}, `[[{"JB":0},{"JB":1}],[{"JB":3}]]`},
+ {[]textbyte{2, 3}, `["TB:2","TB:3"]`},
+ {[]jsonint{5, 4}, `[{"JI":5},{"JI":4}]`},
+ {[]textint{9, 3}, `["TI:9","TI:3"]`},
+ {[]int{9, 3}, `[9,3]`},
+ }
+ for _, d := range testdata {
+ js, err := Marshal(d.data)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ got, want := string(js), d.want
+ if got != want {
+ t.Errorf("got %s, want %s", got, want)
+ }
+ }
+}
+
func TestTextMarshalerMapKeysAreSorted(t *testing.T) {
b, err := Marshal(map[unmarshalerText]int{
{"x", "y"}: 1,
diff --git a/src/encoding/json/stream.go b/src/encoding/json/stream.go
index b740d32a7d..d6b2992e9b 100644
--- a/src/encoding/json/stream.go
+++ b/src/encoding/json/stream.go
@@ -10,7 +10,7 @@ import (
"io"
)
-// A Decoder reads and decodes JSON objects from an input stream.
+// A Decoder reads and decodes JSON values from an input stream.
type Decoder struct {
r io.Reader
buf []byte
@@ -164,10 +164,11 @@ func nonSpace(b []byte) bool {
return false
}
-// An Encoder writes JSON objects to an output stream.
+// An Encoder writes JSON values to an output stream.
type Encoder struct {
- w io.Writer
- err error
+ w io.Writer
+ err error
+ escapeHTML bool
indentBuf *bytes.Buffer
indentPrefix string
@@ -176,7 +177,7 @@ type Encoder struct {
// NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
- return &Encoder{w: w}
+ return &Encoder{w: w, escapeHTML: true}
}
// Encode writes the JSON encoding of v to the stream,
@@ -189,7 +190,7 @@ func (enc *Encoder) Encode(v interface{}) error {
return enc.err
}
e := newEncodeState()
- err := e.marshal(v)
+ err := e.marshal(v, encOpts{escapeHTML: enc.escapeHTML})
if err != nil {
return err
}
@@ -218,14 +219,20 @@ func (enc *Encoder) Encode(v interface{}) error {
return err
}
-// Indent sets the encoder to format each encoded object with Indent.
+// Indent sets the encoder to format each encoded value with Indent.
func (enc *Encoder) Indent(prefix, indent string) {
enc.indentBuf = new(bytes.Buffer)
enc.indentPrefix = prefix
enc.indentValue = indent
}
-// RawMessage is a raw encoded JSON object.
+// DisableHTMLEscaping causes the encoder not to escape angle brackets
+// ("<" and ">") or ampersands ("&") in JSON strings.
+func (enc *Encoder) DisableHTMLEscaping() {
+ enc.escapeHTML = false
+}
+
+// RawMessage is a raw encoded JSON value.
// It implements Marshaler and Unmarshaler and can
// be used to delay JSON decoding or precompute a JSON encoding.
type RawMessage []byte
diff --git a/src/encoding/json/stream_test.go b/src/encoding/json/stream_test.go
index db25708f4c..3516ac3b83 100644
--- a/src/encoding/json/stream_test.go
+++ b/src/encoding/json/stream_test.go
@@ -87,6 +87,39 @@ func TestEncoderIndent(t *testing.T) {
}
}
+func TestEncoderDisableHTMLEscaping(t *testing.T) {
+ var c C
+ var ct CText
+ for _, tt := range []struct {
+ name string
+ v interface{}
+ wantEscape string
+ want string
+ }{
+ {"c", c, `"\u003c\u0026\u003e"`, `"<&>"`},
+ {"ct", ct, `"\"\u003c\u0026\u003e\""`, `"\"<&>\""`},
+ {`"<&>"`, "<&>", `"\u003c\u0026\u003e"`, `"<&>"`},
+ } {
+ var buf bytes.Buffer
+ enc := NewEncoder(&buf)
+ if err := enc.Encode(tt.v); err != nil {
+ t.Fatalf("Encode(%s): %s", tt.name, err)
+ }
+ if got := strings.TrimSpace(buf.String()); got != tt.wantEscape {
+ t.Errorf("Encode(%s) = %#q, want %#q", tt.name, got, tt.wantEscape)
+ }
+ buf.Reset()
+ enc.DisableHTMLEscaping()
+ if err := enc.Encode(tt.v); err != nil {
+ t.Fatalf("DisableHTMLEscaping Encode(%s): %s", tt.name, err)
+ }
+ if got := strings.TrimSpace(buf.String()); got != tt.want {
+ t.Errorf("DisableHTMLEscaping Encode(%s) = %#q, want %#q",
+ tt.name, got, tt.want)
+ }
+ }
+}
+
func TestDecoder(t *testing.T) {
for i := 0; i <= len(streamTest); i++ {
// Use stream without newlines as input,
diff --git a/src/expvar/expvar.go b/src/expvar/expvar.go
index 1ec85006b4..b7ea433014 100644
--- a/src/expvar/expvar.go
+++ b/src/expvar/expvar.go
@@ -219,8 +219,10 @@ type String struct {
func (v *String) String() string {
v.mu.RLock()
- defer v.mu.RUnlock()
- return strconv.Quote(v.s)
+ s := v.s
+ v.mu.RUnlock()
+ b, _ := json.Marshal(s)
+ return string(b)
}
func (v *String) Set(value string) {
diff --git a/src/expvar/expvar_test.go b/src/expvar/expvar_test.go
index 385fea81ad..7b1c9dfc4f 100644
--- a/src/expvar/expvar_test.go
+++ b/src/expvar/expvar_test.go
@@ -142,8 +142,14 @@ func TestString(t *testing.T) {
t.Errorf("name.s = %q, want \"Mike\"", name.s)
}
- if s := name.String(); s != "\"Mike\"" {
- t.Errorf("reqs.String() = %q, want \"\"Mike\"\"", s)
+ if s, want := name.String(), `"Mike"`; s != want {
+ t.Errorf("from %q, name.String() = %q, want %q", name.s, s, want)
+ }
+
+ // Make sure we produce safe JSON output.
+ name.Set(`<`)
+ if s, want := name.String(), "\"\\u003c\""; s != want {
+ t.Errorf("from %q, name.String() = %q, want %q", name.s, s, want)
}
}
diff --git a/src/flag/flag_test.go b/src/flag/flag_test.go
index e2319ec94c..1a8bdc106a 100644
--- a/src/flag/flag_test.go
+++ b/src/flag/flag_test.go
@@ -393,7 +393,7 @@ const defaultOutput = ` -A for bootstrapping, allow 'any' type
-Z int
an int that defaults to zero
-maxT timeout
- set timeout for dial
+ set timeout for dial (default 0s)
`
func TestPrintDefaults(t *testing.T) {
diff --git a/src/fmt/doc.go b/src/fmt/doc.go
index 4eea48eb6b..fefc10c19d 100644
--- a/src/fmt/doc.go
+++ b/src/fmt/doc.go
@@ -62,7 +62,7 @@
For compound objects, the elements are printed using these rules, recursively,
laid out like this:
struct: {field0 field1 ...}
- array, slice: [elem0 elem1 ...]
+ array, slice: [elem0 elem1 ...]
maps: map[key1:value1 key2:value2]
pointer to above: &{}, &[], &map[]
@@ -95,10 +95,10 @@
For floating-point values, width sets the minimum width of the field and
precision sets the number of places after the decimal, if appropriate,
- except that for %g/%G it sets the total number of digits. For example,
- given 123.45 the format %6.2f prints 123.45 while %.4g prints 123.5.
- The default precision for %e and %f is 6; for %g it is the smallest
- number of digits necessary to identify the value uniquely.
+ except that for %g/%G precision sets the total number of significant
+ digits. For example, given 12.345 the format %6.3f prints 12.345 while
+ %.3g prints 12.3. The default precision for %e and %f is 6; for %g it
+ is the smallest number of digits necessary to identify the value uniquely.
For complex numbers, the width and precision apply to the two
components independently and the result is parenthesized, so %f applied
diff --git a/src/fmt/fmt_test.go b/src/fmt/fmt_test.go
index 3c5142c0f8..5fb2a632b2 100644
--- a/src/fmt/fmt_test.go
+++ b/src/fmt/fmt_test.go
@@ -328,6 +328,8 @@ var fmtTests = []struct {
{"%d", int64(-1 << 63), "-9223372036854775808"},
{"%.d", 0, ""},
{"%.0d", 0, ""},
+ {"%6.0d", 0, " "},
+ {"%06.0d", 0, " "},
{"% d", 12345, " 12345"},
{"%+d", 12345, "+12345"},
{"%+d", -12345, "-12345"},
@@ -335,6 +337,7 @@ var fmtTests = []struct {
{"%b", -6, "-110"},
{"%b", ^uint32(0), "11111111111111111111111111111111"},
{"%b", ^uint64(0), "1111111111111111111111111111111111111111111111111111111111111111"},
+ {"%b", int64(-1 << 63), zeroFill("-1", 63, "")},
{"%o", 01234, "1234"},
{"%#o", 01234, "01234"},
{"%o", ^uint32(0), "37777777777"},
@@ -345,7 +348,6 @@ var fmtTests = []struct {
{"%x", ^uint32(0), "ffffffff"},
{"%X", ^uint64(0), "FFFFFFFFFFFFFFFF"},
{"%.20b", 7, "00000000000000000111"},
- {"%6.0d", 0, " "},
{"%10d", 12345, " 12345"},
{"%10d", -12345, " -12345"},
{"%+10d", 12345, " +12345"},
@@ -353,12 +355,23 @@ var fmtTests = []struct {
{"%010d", -12345, "-000012345"},
{"%20.8d", 1234, " 00001234"},
{"%20.8d", -1234, " -00001234"},
+ {"%020.8d", 1234, " 00001234"},
+ {"%020.8d", -1234, " -00001234"},
{"%-20.8d", 1234, "00001234 "},
{"%-20.8d", -1234, "-00001234 "},
{"%-#20.8x", 0x1234abc, "0x01234abc "},
{"%-#20.8X", 0x1234abc, "0X01234ABC "},
{"%-#20.8o", 01234, "00001234 "},
+ // Test correct f.intbuf overflow checks.
+ {"%068d", 1, zeroFill("", 68, "1")},
+ {"%068d", -1, zeroFill("-", 67, "1")},
+ {"%#.68x", 42, zeroFill("0x", 68, "2a")},
+ {"%.68d", -42, zeroFill("-", 68, "42")},
+ {"%+.68d", 42, zeroFill("+", 68, "42")},
+ {"% .68d", 42, zeroFill(" ", 68, "42")},
+ {"% +.68d", 42, zeroFill("+", 68, "42")},
+
// unicode format
{"%U", 0, "U+0000"},
{"%U", -1, "U+FFFFFFFFFFFFFFFF"},
@@ -375,8 +388,8 @@ var fmtTests = []struct {
{"%#-14.6U", '⌘', "U+002318 '⌘' "},
{"%#014.6U", '⌘', " U+002318 '⌘'"},
{"%#-014.6U", '⌘', "U+002318 '⌘' "},
- {"%.80U", uint(42), zeroFill("U+", 80, "2A")},
- {"%#.80U", '日', zeroFill("U+", 80, "65E5") + " '日'"},
+ {"%.68U", uint(42), zeroFill("U+", 68, "2A")},
+ {"%#.68U", '日', zeroFill("U+", 68, "65E5") + " '日'"},
// floats
{"%+.3e", 0.0, "+0.000e+00"},
@@ -406,6 +419,9 @@ var fmtTests = []struct {
// Precision has no effect for binary float format.
{"%.4b", float32(1.0), "8388608p-23"},
{"%.4b", -1.0, "-4503599627370496p-52"},
+ // Test correct f.intbuf boundary checks.
+ {"%.68f", 1.0, zeroFill("1.", 68, "")},
+ {"%.68f", -1.0, zeroFill("-1.", 68, "")},
// float infinites and NaNs
{"%f", posInf, "+Inf"},
{"%.1f", negInf, "-Inf"},
@@ -795,22 +811,6 @@ var fmtTests = []struct {
// This test is just to check that it shows the two NaNs at all.
{"%v", map[float64]int{NaN: 1, NaN: 2}, "map[NaN:<nil> NaN:<nil>]"},
- // Used to crash because nByte didn't allow for a sign.
- {"%b", int64(-1 << 63), zeroFill("-1", 63, "")},
-
- // Used to panic.
- {"%0100d", 1, zeroFill("", 100, "1")},
- {"%0100d", -1, zeroFill("-", 99, "1")},
- {"%0.100f", 1.0, zeroFill("1.", 100, "")},
- {"%0.100f", -1.0, zeroFill("-1.", 100, "")},
-
- // Used to panic: integer function didn't look at f.prec, f.unicode, f.width or sign.
- {"%#.65x", 42, zeroFill("0x", 65, "2a")},
- {"%.65d", -42, zeroFill("-", 65, "42")},
- {"%+.65d", 42, zeroFill("+", 65, "42")},
- {"% .65d", 42, zeroFill(" ", 65, "42")},
- {"% +.65d", 42, zeroFill("+", 65, "42")},
-
// Comparison of padding rules with C printf.
/*
C program:
@@ -882,10 +882,6 @@ var fmtTests = []struct {
{"%7.2f", 1 + 2i, "( 1.00 +2.00i)"},
{"%+07.2f", -1 - 2i, "(-001.00-002.00i)"},
- {"%20f", -1.0, " -1.000000"},
- // Make sure we can handle very large widths.
- {"%0100f", -1.0, zeroFill("-", 99, "1.000000")},
-
// Use spaces instead of zero if padding to the right.
{"%0-5s", "abc", "abc "},
{"%-05.1f", 1.0, "1.0 "},
@@ -899,6 +895,7 @@ var fmtTests = []struct {
// integer formatting should not alter padding for other elements.
{"%03.6v", []interface{}{1, 2.0, "x"}, "[000001 002 00x]"},
+ {"%03.0v", []interface{}{0, 2.0, "x"}, "[ 002 000]"},
// Complex fmt used to leave the plus flag set for future entries in the array
// causing +2+0i and +3+0i instead of 2+0i and 3+0i.
@@ -908,27 +905,6 @@ var fmtTests = []struct {
// Incomplete format specification caused crash.
{"%.", 3, "%!.(int=3)"},
- // Used to panic with out-of-bounds for very large numeric representations.
- // nByte is set to handle one bit per uint64 in %b format, with a negative number.
- // See issue 6777.
- {"%#064x", 1, zeroFill("0x", 64, "1")},
- {"%#064x", -1, zeroFill("-0x", 63, "1")},
- {"%#064b", 1, zeroFill("", 64, "1")},
- {"%#064b", -1, zeroFill("-", 63, "1")},
- {"%#064o", 1, zeroFill("", 64, "1")},
- {"%#064o", -1, zeroFill("-", 63, "1")},
- {"%#064d", 1, zeroFill("", 64, "1")},
- {"%#064d", -1, zeroFill("-", 63, "1")},
- // Test that we handle the crossover above the size of uint64
- {"%#072x", 1, zeroFill("0x", 72, "1")},
- {"%#072x", -1, zeroFill("-0x", 71, "1")},
- {"%#072b", 1, zeroFill("", 72, "1")},
- {"%#072b", -1, zeroFill("-", 71, "1")},
- {"%#072o", 1, zeroFill("", 72, "1")},
- {"%#072o", -1, zeroFill("-", 71, "1")},
- {"%#072d", 1, zeroFill("", 72, "1")},
- {"%#072d", -1, zeroFill("-", 71, "1")},
-
// Padding for complex numbers. Has been bad, then fixed, then bad again.
{"%+10.2f", +104.66 + 440.51i, "( +104.66 +440.51i)"},
{"%+10.2f", -104.66 + 440.51i, "( -104.66 +440.51i)"},
diff --git a/src/fmt/format.go b/src/fmt/format.go
index b7e4f51639..023647501a 100644
--- a/src/fmt/format.go
+++ b/src/fmt/format.go
@@ -10,10 +10,6 @@ import (
)
const (
- // %b of an int64, plus a sign.
- // Hex can add 0x and we handle it specially.
- nByte = 65
-
ldigits = "0123456789abcdefx"
udigits = "0123456789ABCDEFX"
)
@@ -43,12 +39,16 @@ type fmtFlags struct {
// A fmt is the raw formatter used by Printf etc.
// It prints into a buffer that must be set up separately.
type fmt struct {
- intbuf [nByte]byte
- buf *buffer
- // width, precision
- wid int
- prec int
+ buf *buffer
+
fmtFlags
+
+ wid int // width
+ prec int // precision
+
+ // intbuf is large enought to store %b of an int64 with a sign and
+ // avoids padding at the end of the struct on 32 bit architectures.
+ intbuf [68]byte
}
func (f *fmt) clearflags() {
@@ -136,14 +136,14 @@ func (f *fmt) fmt_unicode(u uint64) {
buf := f.intbuf[0:]
// With default precision set the maximum needed buf length is 18
- // for formatting -1 with %#U ("U+FFFFFFFFFFFFFFFF")
- // which fits into the already allocated intbuf with a capacity of 65 bytes.
+ // for formatting -1 with %#U ("U+FFFFFFFFFFFFFFFF") which fits
+ // into the already allocated intbuf with a capacity of 68 bytes.
prec := 4
if f.precPresent && f.prec > 4 {
prec = f.prec
// Compute space needed for "U+" , number, " '", character, "'".
width := 2 + prec + 2 + utf8.UTFMax + 1
- if width > cap(buf) {
+ if width > len(buf) {
buf = make([]byte, width)
}
}
@@ -192,40 +192,37 @@ func (f *fmt) fmt_unicode(u uint64) {
// fmt_integer formats signed and unsigned integers.
func (f *fmt) fmt_integer(u uint64, base int, isSigned bool, digits string) {
- // Precision of 0 and value of 0 means "print nothing" but padding.
- if f.precPresent && f.prec == 0 && u == 0 {
- if f.widPresent {
- f.writePadding(f.wid)
- }
- return
- }
-
negative := isSigned && int64(u) < 0
if negative {
u = -u
}
- var buf []byte = f.intbuf[0:]
- if f.widPresent || f.precPresent || f.plus || f.space {
- width := f.wid + f.prec // Only one will be set, both are positive; this provides the maximum.
- if base == 16 && f.sharp {
- // Also adds "0x".
- width += 2
- }
- if negative || f.plus || f.space {
- width++
- }
- if width > nByte {
+ buf := f.intbuf[0:]
+ // The already allocated f.intbuf with a capacity of 68 bytes
+ // is large enough for integer formatting when no precision or width is set.
+ if f.widPresent || f.precPresent {
+ // Account 3 extra bytes for possible addition of a sign and "0x".
+ width := 3 + f.wid + f.prec // wid and prec are always positive.
+ if width > len(buf) {
// We're going to need a bigger boat.
buf = make([]byte, width)
}
}
- // two ways to ask for extra leading zero digits: %.3d or %03d.
- // apparently the first cancels the second.
+ // Two ways to ask for extra leading zero digits: %.3d or %03d.
+ // If both are specified the f.zero flag is ignored and
+ // padding with spaces is used instead.
prec := 0
if f.precPresent {
prec = f.prec
+ // Precision of 0 and value of 0 means "print nothing" but padding.
+ if prec == 0 && u == 0 {
+ oldZero := f.zero
+ f.zero = false
+ f.writePadding(f.wid)
+ f.zero = oldZero
+ return
+ }
} else if f.zero && f.widPresent {
prec = f.wid
if negative || f.plus || f.space {
@@ -304,13 +301,11 @@ func (f *fmt) fmt_integer(u uint64, base int, isSigned bool, digits string) {
}
// Left padding with zeros has already been handled like precision earlier
- // or was overruled by an explicitly set precision.
- if f.zero {
- f.buf.Write(buf[i:])
- return
- }
-
+ // or the f.zero flag is ignored due to an explicitly set precision.
+ oldZero := f.zero
+ f.zero = false
f.pad(buf[i:])
+ f.zero = oldZero
}
// truncate truncates the string to the specified precision, if present.
diff --git a/src/fmt/print.go b/src/fmt/print.go
index d071dcfb31..f8c731656e 100644
--- a/src/fmt/print.go
+++ b/src/fmt/print.go
@@ -101,20 +101,27 @@ func (bp *buffer) WriteRune(r rune) {
*bp = b[:n+w]
}
+// pp is used to store a printer's state and is reused with sync.Pool to avoid allocations.
type pp struct {
- panicking bool
- erroring bool // printing an error condition
- buf buffer
+ buf buffer
+
// arg holds the current item, as an interface{}.
arg interface{}
- // value holds the current item, as a reflect.Value, and will be
- // the zero Value if the item has not been reflected.
+
+ // value is used instead of arg for reflect values.
value reflect.Value
+
+ // fmt is used to format basic items such as integers or strings.
+ fmt fmt
+
// reordered records whether the format string used argument reordering.
reordered bool
// goodArgNum records whether the most recent reordering directive was valid.
goodArgNum bool
- fmt fmt
+ // panicking is set by catchPanic to avoid infinite panic, recover, panic, ... recursion.
+ panicking bool
+ // erroring is set when printing an error string to guard against calling handleMethods.
+ erroring bool
}
var ppFree = sync.Pool{
diff --git a/src/go/ast/ast.go b/src/go/ast/ast.go
index 5ab4283826..cca2d48bbd 100644
--- a/src/go/ast/ast.go
+++ b/src/go/ast/ast.go
@@ -99,7 +99,7 @@ func (g *CommentGroup) Text() string {
}
comments := make([]string, len(g.List))
for i, c := range g.List {
- comments[i] = string(c.Text)
+ comments[i] = c.Text
}
lines := make([]string, 0, 10) // most comments are less than 10 lines
diff --git a/src/go/build/build.go b/src/go/build/build.go
index e61d564fa3..04a41a6c2e 100644
--- a/src/go/build/build.go
+++ b/src/go/build/build.go
@@ -1266,7 +1266,7 @@ func safeCgoName(s string, spaces bool) bool {
safe = safe[len(safeSpaces):]
}
for i := 0; i < len(s); i++ {
- if c := s[i]; c < 0x80 && bytes.IndexByte(safe, c) < 0 {
+ if c := s[i]; c < utf8.RuneSelf && bytes.IndexByte(safe, c) < 0 {
return false
}
}
diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go
index c066048630..2db5ba67d1 100644
--- a/src/go/build/deps_test.go
+++ b/src/go/build/deps_test.go
@@ -168,7 +168,7 @@ var pkgDeps = map[string][]string{
"testing": {"L2", "flag", "fmt", "os", "runtime/debug", "runtime/pprof", "runtime/trace", "time"},
"testing/iotest": {"L2", "log"},
"testing/quick": {"L2", "flag", "fmt", "reflect"},
- "internal/testenv": {"L2", "OS", "testing"},
+ "internal/testenv": {"L2", "OS", "flag", "testing"},
// L4 is defined as L3+fmt+log+time, because in general once
// you're using L3 packages, use of fmt, log, or time is not a big deal.
@@ -215,7 +215,7 @@ var pkgDeps = map[string][]string{
"compress/gzip": {"L4", "compress/flate"},
"compress/lzw": {"L4"},
"compress/zlib": {"L4", "compress/flate"},
- "context": {"errors", "fmt", "sync", "time"},
+ "context": {"errors", "fmt", "reflect", "sync", "time"},
"database/sql": {"L4", "container/list", "database/sql/driver"},
"database/sql/driver": {"L4", "time"},
"debug/dwarf": {"L4"},
@@ -280,7 +280,9 @@ var pkgDeps = map[string][]string{
// Basic networking.
// Because net must be used by any package that wants to
// do networking portably, it must have a small dependency set: just L0+basic os.
- "net": {"L0", "CGO", "math/rand", "os", "sort", "syscall", "time", "internal/syscall/windows", "internal/singleflight", "internal/race"},
+ "net": {"L0", "CGO",
+ "context", "math/rand", "os", "sort", "syscall", "time",
+ "internal/syscall/windows", "internal/singleflight", "internal/race"},
// NET enables use of basic network-related packages.
"NET": {
diff --git a/src/go/build/read.go b/src/go/build/read.go
index d411c1980e..29b8cdc786 100644
--- a/src/go/build/read.go
+++ b/src/go/build/read.go
@@ -8,6 +8,7 @@ import (
"bufio"
"errors"
"io"
+ "unicode/utf8"
)
type importReader struct {
@@ -20,7 +21,7 @@ type importReader struct {
}
func isIdent(c byte) bool {
- return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= 0x80
+ return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= utf8.RuneSelf
}
var (
diff --git a/src/go/importer/importer.go b/src/go/importer/importer.go
index 560b853c39..f655bc1e92 100644
--- a/src/go/importer/importer.go
+++ b/src/go/importer/importer.go
@@ -31,7 +31,7 @@ func For(compiler string, lookup Lookup) types.Importer {
return make(gcimports)
case "gccgo":
- if lookup == nil {
+ if lookup != nil {
panic("gccgo importer for custom import path lookup not yet implemented")
}
diff --git a/src/go/internal/gcimporter/bimport.go b/src/go/internal/gcimporter/bimport.go
index 12efb2aaf3..5ba9af1b02 100644
--- a/src/go/internal/gcimporter/bimport.go
+++ b/src/go/internal/gcimporter/bimport.go
@@ -11,18 +11,28 @@ import (
"go/token"
"go/types"
"sort"
+ "strings"
"unicode"
"unicode/utf8"
)
type importer struct {
- imports map[string]*types.Package
- data []byte
- buf []byte // for reading strings
- bufarray [64]byte // initial underlying array for buf, large enough to avoid allocation when compiling std lib
- pkgList []*types.Package
- typList []types.Type
+ imports map[string]*types.Package
+ data []byte
+ path string
+ buf []byte // for reading strings
+ // object lists
+ strList []string // in order of appearance
+ pkgList []*types.Package // in order of appearance
+ typList []types.Type // in order of appearance
+
+ // position encoding
+ posInfoFormat bool
+ prevFile string
+ prevLine int
+
+ // debugging support
debugFormat bool
read int // bytes read
}
@@ -35,11 +45,12 @@ func BImportData(imports map[string]*types.Package, data []byte, path string) (i
p := importer{
imports: imports,
data: data,
+ path: path,
+ strList: []string{""}, // empty string is mapped to 0
}
- p.buf = p.bufarray[:]
// read low-level encoding format
- switch format := p.byte(); format {
+ switch format := p.rawByte(); format {
case 'c':
// compact format - nothing to do
case 'd':
@@ -48,6 +59,8 @@ func BImportData(imports map[string]*types.Package, data []byte, path string) (i
return p.read, nil, fmt.Errorf("invalid encoding format in export data: got %q; want 'c' or 'd'", format)
}
+ p.posInfoFormat = p.int() != 0
+
// --- generic export data ---
if v := p.string(); v != "v0" {
@@ -58,28 +71,7 @@ func BImportData(imports map[string]*types.Package, data []byte, path string) (i
p.typList = append(p.typList, predeclared...)
// read package data
- // TODO(gri) clean this up
- i := p.tagOrIndex()
- if i != packageTag {
- panic(fmt.Sprintf("package tag expected, got %d", i))
- }
- name := p.string()
- if s := p.string(); s != "" {
- panic(fmt.Sprintf("empty path expected, got %s", s))
- }
- pkg := p.imports[path]
- if pkg == nil {
- pkg = types.NewPackage(path, name)
- p.imports[path] = pkg
- }
- p.pkgList = append(p.pkgList, pkg)
-
- if debug && p.pkgList[0] != pkg {
- panic("imported packaged not found in pkgList[0]")
- }
-
- // read compiler-specific flags
- p.string() // discard
+ pkg := p.pkg()
// read objects of phase 1 only (see cmd/compiler/internal/gc/bexport.go)
objcount := 0
@@ -94,7 +86,7 @@ func BImportData(imports map[string]*types.Package, data []byte, path string) (i
// self-verification
if count := p.int(); count != objcount {
- panic(fmt.Sprintf("importer: got %d objects; want %d", objcount, count))
+ panic(fmt.Sprintf("got %d objects; want %d", objcount, count))
}
// ignore compiler-specific import data
@@ -138,16 +130,22 @@ func (p *importer) pkg() *types.Package {
panic("empty package name in import")
}
- // we should never see an empty import path
- if path == "" {
- panic("empty import path")
+ // an empty path denotes the package we are currently importing;
+ // it must be the first package we see
+ if (path == "") != (len(p.pkgList) == 0) {
+ panic(fmt.Sprintf("package path %q for pkg index %d", path, len(p.pkgList)))
}
// if the package was imported before, use that one; otherwise create a new one
+ if path == "" {
+ path = p.path
+ }
pkg := p.imports[path]
if pkg == nil {
pkg = types.NewPackage(path, name)
p.imports[path] = pkg
+ } else if pkg.Name() != name {
+ panic(fmt.Sprintf("conflicting names %s and %s for package %q", pkg.Name(), name, path))
}
p.pkgList = append(p.pkgList, pkg)
@@ -171,6 +169,7 @@ func (p *importer) declare(obj types.Object) {
func (p *importer) obj(tag int) {
switch tag {
case constTag:
+ p.pos()
pkg, name := p.qualifiedName()
typ := p.typ(nil)
val := p.value()
@@ -180,23 +179,44 @@ func (p *importer) obj(tag int) {
_ = p.typ(nil)
case varTag:
+ p.pos()
pkg, name := p.qualifiedName()
typ := p.typ(nil)
p.declare(types.NewVar(token.NoPos, pkg, name, typ))
case funcTag:
+ p.pos()
pkg, name := p.qualifiedName()
params, isddd := p.paramList()
result, _ := p.paramList()
sig := types.NewSignature(nil, params, result, isddd)
- p.int() // read and discard index of inlined function body
p.declare(types.NewFunc(token.NoPos, pkg, name, sig))
default:
- panic("unexpected object tag")
+ panic(fmt.Sprintf("unexpected object tag %d", tag))
}
}
+func (p *importer) pos() {
+ if !p.posInfoFormat {
+ return
+ }
+
+ file := p.prevFile
+ line := p.prevLine
+
+ if delta := p.int(); delta != 0 {
+ line += delta
+ } else {
+ file = p.string()
+ line = p.int()
+ p.prevFile = file
+ }
+ p.prevLine = line
+
+ // TODO(gri) register new position
+}
+
func (p *importer) qualifiedName() (pkg *types.Package, name string) {
name = p.string()
pkg = p.pkg()
@@ -232,8 +252,8 @@ func (p *importer) typ(parent *types.Package) types.Type {
switch i {
case namedTag:
// read type object
- name := p.string()
- parent = p.pkg()
+ p.pos()
+ parent, name := p.qualifiedName()
scope := parent.Scope()
obj := scope.Lookup(name)
@@ -258,13 +278,14 @@ func (p *importer) typ(parent *types.Package) types.Type {
t0.SetUnderlying(p.typ(parent))
// interfaces don't have associated methods
- if _, ok := t0.Underlying().(*types.Interface); ok {
+ if types.IsInterface(t0) {
return t
}
// read associated methods
for i := p.int(); i > 0; i-- {
// TODO(gri) replace this with something closer to fieldName
+ p.pos()
name := p.string()
if !exported(name) {
p.pkg()
@@ -273,7 +294,6 @@ func (p *importer) typ(parent *types.Package) types.Type {
recv, _ := p.paramList() // TODO(gri) do we need a full param list for the receiver?
params, isddd := p.paramList()
result, _ := p.paramList()
- p.int() // read and discard index of inlined function body
sig := types.NewSignature(recv.At(0), params, result, isddd)
t0.AddMethod(types.NewFunc(token.NoPos, parent, name, sig))
@@ -307,14 +327,7 @@ func (p *importer) typ(parent *types.Package) types.Type {
t := new(types.Struct)
p.record(t)
- n := p.int()
- fields := make([]*types.Var, n)
- tags := make([]string, n)
- for i := range fields {
- fields[i] = p.field(parent)
- tags[i] = p.string()
- }
- *t = *types.NewStruct(fields, tags)
+ *t = *types.NewStruct(p.fieldList(parent))
return t
case pointerTag:
@@ -346,17 +359,7 @@ func (p *importer) typ(parent *types.Package) types.Type {
panic("unexpected embedded interface")
}
- // read methods
- methods := make([]*types.Func, p.int())
- for i := range methods {
- pkg, name := p.fieldName(parent)
- params, isddd := p.paramList()
- result, _ := p.paramList()
- sig := types.NewSignature(nil, params, result, isddd)
- methods[i] = types.NewFunc(token.NoPos, pkg, name, sig)
- }
-
- t := types.NewInterface(methods, nil)
+ t := types.NewInterface(p.methodList(parent), nil)
p.typList[n] = t
return t
@@ -394,7 +397,20 @@ func (p *importer) typ(parent *types.Package) types.Type {
}
}
+func (p *importer) fieldList(parent *types.Package) (fields []*types.Var, tags []string) {
+ if n := p.int(); n > 0 {
+ fields = make([]*types.Var, n)
+ tags = make([]string, n)
+ for i := range fields {
+ fields[i] = p.field(parent)
+ tags[i] = p.string()
+ }
+ }
+ return
+}
+
func (p *importer) field(parent *types.Package) *types.Var {
+ p.pos()
pkg, name := p.fieldName(parent)
typ := p.typ(parent)
@@ -416,6 +432,25 @@ func (p *importer) field(parent *types.Package) *types.Var {
return types.NewField(token.NoPos, pkg, name, typ, anonymous)
}
+func (p *importer) methodList(parent *types.Package) (methods []*types.Func) {
+ if n := p.int(); n > 0 {
+ methods = make([]*types.Func, n)
+ for i := range methods {
+ methods[i] = p.method(parent)
+ }
+ }
+ return
+}
+
+func (p *importer) method(parent *types.Package) *types.Func {
+ p.pos()
+ pkg, name := p.fieldName(parent)
+ params, isddd := p.paramList()
+ result, _ := p.paramList()
+ sig := types.NewSignature(nil, params, result, isddd)
+ return types.NewFunc(token.NoPos, pkg, name, sig)
+}
+
func (p *importer) fieldName(parent *types.Package) (*types.Package, string) {
pkg := parent
if pkg == nil {
@@ -470,6 +505,9 @@ func (p *importer) param(named bool) (*types.Var, bool) {
if name == "" {
panic("expected named parameter")
}
+ if i := strings.Index(name, "·"); i > 0 {
+ name = name[:i] // cut off gc-specific parameter numbering
+ }
pkg = p.pkg()
}
@@ -581,24 +619,28 @@ func (p *importer) string() string {
if p.debugFormat {
p.marker('s')
}
-
- if n := int(p.rawInt64()); n > 0 {
- if cap(p.buf) < n {
- p.buf = make([]byte, n)
- } else {
- p.buf = p.buf[:n]
- }
- for i := 0; i < n; i++ {
- p.buf[i] = p.byte()
- }
- return string(p.buf)
+ // if the string was seen before, i is its index (>= 0)
+ // (the empty string is at index 0)
+ i := p.rawInt64()
+ if i >= 0 {
+ return p.strList[i]
}
-
- return ""
+ // otherwise, i is the negative string length (< 0)
+ if n := int(-i); n <= cap(p.buf) {
+ p.buf = p.buf[:n]
+ } else {
+ p.buf = make([]byte, n)
+ }
+ for i := range p.buf {
+ p.buf[i] = p.rawByte()
+ }
+ s := string(p.buf)
+ p.strList = append(p.strList, s)
+ return s
}
func (p *importer) marker(want byte) {
- if got := p.byte(); got != want {
+ if got := p.rawByte(); got != want {
panic(fmt.Sprintf("incorrect marker: got %c; want %c (pos = %d)", got, want, p.read))
}
@@ -619,12 +661,13 @@ func (p *importer) rawInt64() int64 {
// needed for binary.ReadVarint in rawInt64
func (p *importer) ReadByte() (byte, error) {
- return p.byte(), nil
+ return p.rawByte(), nil
}
// byte is the bottleneck interface for reading p.data.
// It unescapes '|' 'S' to '$' and '|' '|' to '|'.
-func (p *importer) byte() byte {
+// rawByte should only be used by low-level decoders.
+func (p *importer) rawByte() byte {
b := p.data[0]
r := 1
if b == '|' {
diff --git a/src/go/scanner/scanner.go b/src/go/scanner/scanner.go
index 4041d9aa47..ce660c71d5 100644
--- a/src/go/scanner/scanner.go
+++ b/src/go/scanner/scanner.go
@@ -64,7 +64,7 @@ func (s *Scanner) next() {
switch {
case r == 0:
s.error(s.offset, "illegal character NUL")
- case r >= 0x80:
+ case r >= utf8.RuneSelf:
// not ASCII
r, w = utf8.DecodeRune(s.src[s.rdOffset:])
if r == utf8.RuneError && w == 1 {
@@ -255,11 +255,11 @@ func (s *Scanner) findLineEnd() bool {
}
func isLetter(ch rune) bool {
- return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch)
+ return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= utf8.RuneSelf && unicode.IsLetter(ch)
}
func isDigit(ch rune) bool {
- return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch)
+ return '0' <= ch && ch <= '9' || ch >= utf8.RuneSelf && unicode.IsDigit(ch)
}
func (s *Scanner) scanIdentifier() string {
diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go
index c2feed3813..8b8ae1bb5d 100644
--- a/src/go/types/api_test.go
+++ b/src/go/types/api_test.go
@@ -1042,3 +1042,45 @@ func f() {
}
}
}
+
+func TestIdentical_issue15173(t *testing.T) {
+ // Identical should allow nil arguments and be symmetric.
+ for _, test := range []struct {
+ x, y Type
+ want bool
+ }{
+ {Typ[Int], Typ[Int], true},
+ {Typ[Int], nil, false},
+ {nil, Typ[Int], false},
+ {nil, nil, true},
+ } {
+ if got := Identical(test.x, test.y); got != test.want {
+ t.Errorf("Identical(%v, %v) = %t", test.x, test.y, got)
+ }
+ }
+}
+
+func TestIssue15305(t *testing.T) {
+ const src = "package p; func f() int16; var _ = f(undef)"
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "issue15305.go", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ conf := Config{
+ Error: func(err error) {}, // allow errors
+ }
+ info := &Info{
+ Types: make(map[ast.Expr]TypeAndValue),
+ }
+ conf.Check("p", fset, []*ast.File{f}, info) // ignore result
+ for e, tv := range info.Types {
+ if _, ok := e.(*ast.CallExpr); ok {
+ if tv.Type != Typ[Int16] {
+ t.Errorf("CallExpr has type %v, want int16", tv.Type)
+ }
+ return
+ }
+ }
+ t.Errorf("CallExpr has no type")
+}
diff --git a/src/go/types/call.go b/src/go/types/call.go
index 8aeb862993..45f3e9a605 100644
--- a/src/go/types/call.go
+++ b/src/go/types/call.go
@@ -62,14 +62,12 @@ func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind {
}
arg, n, _ := unpack(func(x *operand, i int) { check.multiExpr(x, e.Args[i]) }, len(e.Args), false)
- if arg == nil {
+ if arg != nil {
+ check.arguments(x, e, sig, arg, n)
+ } else {
x.mode = invalid
- x.expr = e
- return statement
}
- check.arguments(x, e, sig, arg, n)
-
// determine result
switch sig.results.Len() {
case 0:
@@ -81,6 +79,7 @@ func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind {
x.mode = value
x.typ = sig.results
}
+
x.expr = e
check.hasCallOrRecv = true
diff --git a/src/go/types/predicates.go b/src/go/types/predicates.go
index 993c6d290b..5509069fb6 100644
--- a/src/go/types/predicates.go
+++ b/src/go/types/predicates.go
@@ -277,6 +277,8 @@ func identical(x, y Type, p *ifacePair) bool {
return x.obj == y.obj
}
+ case nil:
+
default:
unreachable()
}
diff --git a/src/go/types/return.go b/src/go/types/return.go
index 6628985214..0c1447f89b 100644
--- a/src/go/types/return.go
+++ b/src/go/types/return.go
@@ -83,8 +83,13 @@ func (check *Checker) isTerminating(s ast.Stmt, label string) bool {
}
func (check *Checker) isTerminatingList(list []ast.Stmt, label string) bool {
- n := len(list)
- return n > 0 && check.isTerminating(list[n-1], label)
+ // trailing empty statements are permitted - skip them
+ for i := len(list) - 1; i >= 0; i-- {
+ if _, ok := list[i].(*ast.EmptyStmt); !ok {
+ return check.isTerminating(list[i], label)
+ }
+ }
+ return false // all statements are empty
}
func (check *Checker) isTerminatingSwitch(body *ast.BlockStmt, label string) bool {
diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go
index c6691851fb..e301f71159 100644
--- a/src/go/types/stmt.go
+++ b/src/go/types/stmt.go
@@ -83,9 +83,19 @@ func (check *Checker) simpleStmt(s ast.Stmt) {
}
}
+func trimTrailingEmptyStmts(list []ast.Stmt) []ast.Stmt {
+ for i := len(list); i > 0; i-- {
+ if _, ok := list[i-1].(*ast.EmptyStmt); !ok {
+ return list[:i]
+ }
+ }
+ return nil
+}
+
func (check *Checker) stmtList(ctxt stmtContext, list []ast.Stmt) {
ok := ctxt&fallthroughOk != 0
inner := ctxt &^ fallthroughOk
+ list = trimTrailingEmptyStmts(list) // trailing empty statements are "invisible" to fallthrough analysis
for i, s := range list {
inner := inner
if ok && i+1 == len(list) {
diff --git a/src/go/types/testdata/stmt0.src b/src/go/types/testdata/stmt0.src
index e0d714dfb6..ac32ed7ba9 100644
--- a/src/go/types/testdata/stmt0.src
+++ b/src/go/types/testdata/stmt0.src
@@ -531,16 +531,18 @@ func switches1() {
case 1:
fallthrough
case 2:
- default:
- fallthrough
+ fallthrough; ; ; // trailing empty statements are ok
case 3:
+ default:
+ fallthrough; ;
+ case 4:
fallthrough /* ERROR "fallthrough statement out of place" */
}
var y interface{}
switch y.(type) {
case int:
- fallthrough /* ERROR "fallthrough statement out of place" */
+ fallthrough /* ERROR "fallthrough statement out of place" */ ; ; ;
default:
}
@@ -554,7 +556,7 @@ func switches1() {
switch x {
case 0:
goto L1
- L1: fallthrough
+ L1: fallthrough; ;
case 1:
goto L2
goto L3
@@ -576,9 +578,16 @@ func switches1() {
switch x {
case 0:
+ fallthrough; ;
+ case 1:
{
fallthrough /* ERROR "fallthrough statement out of place" */
}
+ case 2:
+ fallthrough
+ case 3:
+ fallthrough /* ERROR "fallthrough statement out of place" */
+ { /* empty block is not an empty statement */ }; ;
default:
}
}
diff --git a/src/go/types/testdata/stmt1.src b/src/go/types/testdata/stmt1.src
index a2955e6fd0..24ad6ebdf1 100644
--- a/src/go/types/testdata/stmt1.src
+++ b/src/go/types/testdata/stmt1.src
@@ -22,9 +22,23 @@ func _(x, y int) (z int) {
func _(x, y int) (z int) {
{
+ return; ; ; // trailing empty statements are ok
+ }
+ ; ; ;
+}
+
+func _(x, y int) (z int) {
+ {
}
} /* ERROR "missing return" */
+func _(x, y int) (z int) {
+ {
+ ; ; ;
+ }
+ ; ; ;
+} /* ERROR "missing return" */
+
// if statements
func _(x, y int) (z int) {
if x < y { return }
@@ -32,6 +46,16 @@ func _(x, y int) (z int) {
}
func _(x, y int) (z int) {
+ if x < y { return; ; ; ; }
+ return 1
+}
+
+func _(x, y int) (z int) {
+ if x < y { return }
+ return 1; ;
+}
+
+func _(x, y int) (z int) {
if x < y { return }
} /* ERROR "missing return" */
@@ -62,9 +86,16 @@ func _(x, y int) (z int) {
func _(x, y int) (z int) {
for {
+ return; ; ; ;
+ }
+}
+
+func _(x, y int) (z int) {
+ for {
return
break
}
+ ; ; ;
} /* ERROR "missing return" */
func _(x, y int) (z int) {
@@ -75,6 +106,14 @@ func _(x, y int) (z int) {
}
func _(x, y int) (z int) {
+ for {
+ for { break }
+ return ; ;
+ }
+ ;
+}
+
+func _(x, y int) (z int) {
L: for {
for { break L }
return
@@ -91,6 +130,13 @@ func _(x, y int) (z int) {
func _(x, y int) (z int) {
switch x {
+ case 0: return;
+ default: return; ; ;
+ }
+}
+
+func _(x, y int) (z int) {
+ switch x {
case 0: return
}
} /* ERROR "missing return" */
@@ -114,6 +160,18 @@ func _(x, y int) (z int) {
}
func _(x, y int) (z int) {
+ switch x {
+ case 0: return
+ default:
+ switch y {
+ case 0: break
+ }
+ panic(0); ; ;
+ }
+ ;
+}
+
+func _(x, y int) (z int) {
L: switch x {
case 0: return
default:
@@ -130,6 +188,11 @@ func _(ch chan int) (z int) {
} // nice!
func _(ch chan int) (z int) {
+ select {}
+ ; ;
+}
+
+func _(ch chan int) (z int) {
select {
default: break
}
@@ -154,6 +217,18 @@ func _(ch chan int) (z int) {
}
func _(ch chan int) (z int) {
+ select {
+ case <-ch: return; ; ;
+ default:
+ for i := 0; i < 10; i++ {
+ break
+ }
+ return; ; ;
+ }
+ ; ; ;
+}
+
+func _(ch chan int) (z int) {
L: select {
case <-ch: return
default:
@@ -162,4 +237,5 @@ L: select {
}
return
}
+ ; ; ;
} /* ERROR "missing return" */
diff --git a/src/hash/adler32/adler32.go b/src/hash/adler32/adler32.go
index 0c733f751a..21d6a2e1dc 100644
--- a/src/hash/adler32/adler32.go
+++ b/src/hash/adler32/adler32.go
@@ -42,7 +42,7 @@ func New() hash.Hash32 {
func (d *digest) Size() int { return Size }
-func (d *digest) BlockSize() int { return 1 }
+func (d *digest) BlockSize() int { return 4 }
// Add p to the running checksum d.
func update(d digest, p []byte) digest {
@@ -52,6 +52,17 @@ func update(d digest, p []byte) digest {
if len(p) > nmax {
p, q = p[:nmax], p[nmax:]
}
+ for len(p) >= 4 {
+ s1 += uint32(p[0])
+ s2 += s1
+ s1 += uint32(p[1])
+ s2 += s1
+ s1 += uint32(p[2])
+ s2 += s1
+ s1 += uint32(p[3])
+ s2 += s1
+ p = p[4:]
+ }
for _, x := range p {
s1 += uint32(x)
s2 += s1
diff --git a/src/hash/crc32/crc32_generic.go b/src/hash/crc32/crc32_generic.go
index 08988f4b38..10a6367bc9 100644
--- a/src/hash/crc32/crc32_generic.go
+++ b/src/hash/crc32/crc32_generic.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build 386 arm arm64 mips64 mips64le ppc64 ppc64le
+// +build !amd64,!amd64p32,!s390x
package crc32
diff --git a/src/hash/crc32/crc32_s390x.go b/src/hash/crc32/crc32_s390x.go
new file mode 100644
index 0000000000..2f20690389
--- /dev/null
+++ b/src/hash/crc32/crc32_s390x.go
@@ -0,0 +1,101 @@
+// Copyright 2016 The Go 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 crc32
+
+import (
+ "unsafe"
+)
+
+const (
+ vxMinLen = 64
+ vxAlignment = 16
+ vxAlignMask = vxAlignment - 1
+)
+
+// hasVectorFacility reports whether the machine has the z/Architecture
+// vector facility installed and enabled.
+func hasVectorFacility() bool
+
+var hasVX = hasVectorFacility()
+
+// vectorizedCastagnoli implements CRC32 using vector instructions.
+// It is defined in crc32_s390x.s.
+//go:noescape
+func vectorizedCastagnoli(crc uint32, p []byte) uint32
+
+// vectorizedIEEE implements CRC32 using vector instructions.
+// It is defined in crc32_s390x.s.
+//go:noescape
+func vectorizedIEEE(crc uint32, p []byte) uint32
+
+func genericCastagnoli(crc uint32, p []byte) uint32 {
+ // Use slicing-by-8 on larger inputs.
+ if len(p) >= sliceBy8Cutoff {
+ return updateSlicingBy8(crc, castagnoliTable8, p)
+ }
+ return update(crc, castagnoliTable, p)
+}
+
+func genericIEEE(crc uint32, p []byte) uint32 {
+ // Use slicing-by-8 on larger inputs.
+ if len(p) >= sliceBy8Cutoff {
+ ieeeTable8Once.Do(func() {
+ ieeeTable8 = makeTable8(IEEE)
+ })
+ return updateSlicingBy8(crc, ieeeTable8, p)
+ }
+ return update(crc, IEEETable, p)
+}
+
+// updateCastagnoli calculates the checksum of p using genericCastagnoli to
+// align the data appropriately for vectorCastagnoli. It avoids using
+// vectorCastagnoli entirely if the length of p is less than or equal to
+// vxMinLen.
+func updateCastagnoli(crc uint32, p []byte) uint32 {
+ // Use vectorized function if vector facility is available and
+ // data length is above threshold.
+ if hasVX && len(p) > vxMinLen {
+ pAddr := uintptr(unsafe.Pointer(&p[0]))
+ if pAddr&vxAlignMask != 0 {
+ prealign := vxAlignment - int(pAddr&vxAlignMask)
+ crc = genericCastagnoli(crc, p[:prealign])
+ p = p[prealign:]
+ }
+ aligned := len(p) & ^vxAlignMask
+ crc = vectorizedCastagnoli(crc, p[:aligned])
+ p = p[aligned:]
+ // process remaining data
+ if len(p) > 0 {
+ crc = genericCastagnoli(crc, p)
+ }
+ return crc
+ }
+ return genericCastagnoli(crc, p)
+}
+
+// updateIEEE calculates the checksum of p using genericIEEE to align the data
+// appropriately for vectorIEEE. It avoids using vectorIEEE entirely if the length
+// of p is less than or equal to vxMinLen.
+func updateIEEE(crc uint32, p []byte) uint32 {
+ // Use vectorized function if vector facility is available and
+ // data length is above threshold.
+ if hasVX && len(p) > vxMinLen {
+ pAddr := uintptr(unsafe.Pointer(&p[0]))
+ if pAddr&vxAlignMask != 0 {
+ prealign := vxAlignment - int(pAddr&vxAlignMask)
+ crc = genericIEEE(crc, p[:prealign])
+ p = p[prealign:]
+ }
+ aligned := len(p) & ^vxAlignMask
+ crc = vectorizedIEEE(crc, p[:aligned])
+ p = p[aligned:]
+ // process remaining data
+ if len(p) > 0 {
+ crc = genericIEEE(crc, p)
+ }
+ return crc
+ }
+ return genericIEEE(crc, p)
+}
diff --git a/src/hash/crc32/crc32_s390x.s b/src/hash/crc32/crc32_s390x.s
new file mode 100644
index 0000000000..f8d39f3df9
--- /dev/null
+++ b/src/hash/crc32/crc32_s390x.s
@@ -0,0 +1,245 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+// Vector register range containing CRC-32 constants
+
+#define CONST_PERM_LE2BE V9
+#define CONST_R2R1 V10
+#define CONST_R4R3 V11
+#define CONST_R5 V12
+#define CONST_RU_POLY V13
+#define CONST_CRC_POLY V14
+
+
+// The CRC-32 constant block contains reduction constants to fold and
+// process particular chunks of the input data stream in parallel.
+//
+// Note that the constant definitions below are extended in order to compute
+// intermediate results with a single VECTOR GALOIS FIELD MULTIPLY instruction.
+// The rightmost doubleword can be 0 to prevent contribution to the result or
+// can be multiplied by 1 to perform an XOR without the need for a separate
+// VECTOR EXCLUSIVE OR instruction.
+//
+// The polynomials used are bit-reflected:
+//
+// IEEE: P'(x) = 0x0edb88320
+// Castagnoli: P'(x) = 0x082f63b78
+
+
+// IEEE polynomial constants
+DATA ·crclecons+0(SB)/8, $0x0F0E0D0C0B0A0908 // LE-to-BE mask
+DATA ·crclecons+8(SB)/8, $0x0706050403020100
+DATA ·crclecons+16(SB)/8, $0x00000001c6e41596 // R2
+DATA ·crclecons+24(SB)/8, $0x0000000154442bd4 // R1
+DATA ·crclecons+32(SB)/8, $0x00000000ccaa009e // R4
+DATA ·crclecons+40(SB)/8, $0x00000001751997d0 // R3
+DATA ·crclecons+48(SB)/8, $0x0000000000000000
+DATA ·crclecons+56(SB)/8, $0x0000000163cd6124 // R5
+DATA ·crclecons+64(SB)/8, $0x0000000000000000
+DATA ·crclecons+72(SB)/8, $0x00000001F7011641 // u'
+DATA ·crclecons+80(SB)/8, $0x0000000000000000
+DATA ·crclecons+88(SB)/8, $0x00000001DB710641 // P'(x) << 1
+
+GLOBL ·crclecons(SB),RODATA, $144
+
+// Castagonli Polynomial constants
+DATA ·crcclecons+0(SB)/8, $0x0F0E0D0C0B0A0908 // LE-to-BE mask
+DATA ·crcclecons+8(SB)/8, $0x0706050403020100
+DATA ·crcclecons+16(SB)/8, $0x000000009e4addf8 // R2
+DATA ·crcclecons+24(SB)/8, $0x00000000740eef02 // R1
+DATA ·crcclecons+32(SB)/8, $0x000000014cd00bd6 // R4
+DATA ·crcclecons+40(SB)/8, $0x00000000f20c0dfe // R3
+DATA ·crcclecons+48(SB)/8, $0x0000000000000000
+DATA ·crcclecons+56(SB)/8, $0x00000000dd45aab8 // R5
+DATA ·crcclecons+64(SB)/8, $0x0000000000000000
+DATA ·crcclecons+72(SB)/8, $0x00000000dea713f1 // u'
+DATA ·crcclecons+80(SB)/8, $0x0000000000000000
+DATA ·crcclecons+88(SB)/8, $0x0000000105ec76f0 // P'(x) << 1
+
+GLOBL ·crcclecons(SB),RODATA, $144
+
+// func hasVectorFacility() bool
+TEXT ·hasVectorFacility(SB),NOSPLIT,$24-1
+ MOVD $x-24(SP), R1
+ XC $24, 0(R1), 0(R1) // clear the storage
+ MOVD $2, R0 // R0 is the number of double words stored -1
+ WORD $0xB2B01000 // STFLE 0(R1)
+ XOR R0, R0 // reset the value of R0
+ MOVBZ z-8(SP), R1
+ AND $0x40, R1
+ BEQ novector
+vectorinstalled:
+ // check if the vector instruction has been enabled
+ VLEIB $0, $0xF, V16
+ VLGVB $0, V16, R1
+ CMPBNE R1, $0xF, novector
+ MOVB $1, ret+0(FP) // have vx
+ RET
+novector:
+ MOVB $0, ret+0(FP) // no vx
+ RET
+
+
+// The CRC-32 function(s) use these calling conventions:
+//
+// Parameters:
+//
+// R2: Initial CRC value, typically ~0; and final CRC (return) value.
+// R3: Input buffer pointer, performance might be improved if the
+// buffer is on a doubleword boundary.
+// R4: Length of the buffer, must be 64 bytes or greater.
+//
+// Register usage:
+//
+// R5: CRC-32 constant pool base pointer.
+// V0: Initial CRC value and intermediate constants and results.
+// V1..V4: Data for CRC computation.
+// V5..V8: Next data chunks that are fetched from the input buffer.
+//
+// V9..V14: CRC-32 constants.
+
+// func vectorizedIEEE(crc uint32, p []byte) uint32
+TEXT ·vectorizedIEEE(SB),NOSPLIT,$0
+ MOVWZ crc+0(FP), R2 // R2 stores the CRC value
+ MOVD p+8(FP), R3 // data pointer
+ MOVD p_len+16(FP), R4 // len(p)
+
+ MOVD $·crclecons(SB), R5
+ BR vectorizedBody<>(SB)
+
+// func vectorizedCastagnoli(crc uint32, p []byte) uint32
+TEXT ·vectorizedCastagnoli(SB),NOSPLIT,$0
+ MOVWZ crc+0(FP), R2 // R2 stores the CRC value
+ MOVD p+8(FP), R3 // data pointer
+ MOVD p_len+16(FP), R4 // len(p)
+
+ // R5: crc-32 constant pool base pointer, constant is used to reduce crc
+ MOVD $·crcclecons(SB), R5
+ BR vectorizedBody<>(SB)
+
+TEXT vectorizedBody<>(SB),NOSPLIT,$0
+ XOR $0xffffffff, R2 // NOTW R2
+ VLM 0(R5), CONST_PERM_LE2BE, CONST_CRC_POLY
+
+ // Load the initial CRC value into the rightmost word of V0
+ VZERO V0
+ VLVGF $3, R2, V0
+
+ // Load a 64-byte data chunk and XOR with CRC
+ VLM 0(R3), V1, V4 // 64-bytes into V1..V4
+
+ // Reflect the data if the CRC operation is in the bit-reflected domain
+ VPERM V1, V1, CONST_PERM_LE2BE, V1
+ VPERM V2, V2, CONST_PERM_LE2BE, V2
+ VPERM V3, V3, CONST_PERM_LE2BE, V3
+ VPERM V4, V4, CONST_PERM_LE2BE, V4
+
+ VX V0, V1, V1 // V1 ^= CRC
+ ADD $64, R3 // BUF = BUF + 64
+ ADD $(-64), R4
+
+ // Check remaining buffer size and jump to proper folding method
+ CMP R4, $64
+ BLT less_than_64bytes
+
+fold_64bytes_loop:
+ // Load the next 64-byte data chunk into V5 to V8
+ VLM 0(R3), V5, V8
+ VPERM V5, V5, CONST_PERM_LE2BE, V5
+ VPERM V6, V6, CONST_PERM_LE2BE, V6
+ VPERM V7, V7, CONST_PERM_LE2BE, V7
+ VPERM V8, V8, CONST_PERM_LE2BE, V8
+
+
+ // Perform a GF(2) multiplication of the doublewords in V1 with
+ // the reduction constants in V0. The intermediate result is
+ // then folded (accumulated) with the next data chunk in V5 and
+ // stored in V1. Repeat this step for the register contents
+ // in V2, V3, and V4 respectively.
+
+ VGFMAG CONST_R2R1, V1, V5, V1
+ VGFMAG CONST_R2R1, V2, V6, V2
+ VGFMAG CONST_R2R1, V3, V7, V3
+ VGFMAG CONST_R2R1, V4, V8 ,V4
+
+ // Adjust buffer pointer and length for next loop
+ ADD $64, R3 // BUF = BUF + 64
+ ADD $(-64), R4 // LEN = LEN - 64
+
+ CMP R4, $64
+ BGE fold_64bytes_loop
+
+less_than_64bytes:
+ // Fold V1 to V4 into a single 128-bit value in V1
+ VGFMAG CONST_R4R3, V1, V2, V1
+ VGFMAG CONST_R4R3, V1, V3, V1
+ VGFMAG CONST_R4R3, V1, V4, V1
+
+ // Check whether to continue with 64-bit folding
+ CMP R4, $16
+ BLT final_fold
+
+fold_16bytes_loop:
+ VL 0(R3), V2 // Load next data chunk
+ VPERM V2, V2, CONST_PERM_LE2BE, V2
+
+ VGFMAG CONST_R4R3, V1, V2, V1 // Fold next data chunk
+
+ // Adjust buffer pointer and size for folding next data chunk
+ ADD $16, R3
+ ADD $-16, R4
+
+ // Process remaining data chunks
+ CMP R4 ,$16
+ BGE fold_16bytes_loop
+
+final_fold:
+ VLEIB $7, $0x40, V9
+ VSRLB V9, CONST_R4R3, V0
+ VLEIG $0, $1, V0
+
+ VGFMG V0, V1, V1
+
+ VLEIB $7, $0x20, V9 // Shift by words
+ VSRLB V9, V1, V2 // Store remaining bits in V2
+ VUPLLF V1, V1 // Split rightmost doubleword
+ VGFMAG CONST_R5, V1, V2, V1 // V1 = (V1 * R5) XOR V2
+
+
+ // The input values to the Barret reduction are the degree-63 polynomial
+ // in V1 (R(x)), degree-32 generator polynomial, and the reduction
+ // constant u. The Barret reduction result is the CRC value of R(x) mod
+ // P(x).
+ //
+ // The Barret reduction algorithm is defined as:
+ //
+ // 1. T1(x) = floor( R(x) / x^32 ) GF2MUL u
+ // 2. T2(x) = floor( T1(x) / x^32 ) GF2MUL P(x)
+ // 3. C(x) = R(x) XOR T2(x) mod x^32
+ //
+ // Note: To compensate the division by x^32, use the vector unpack
+ // instruction to move the leftmost word into the leftmost doubleword
+ // of the vector register. The rightmost doubleword is multiplied
+ // with zero to not contribute to the intermedate results.
+
+
+ // T1(x) = floor( R(x) / x^32 ) GF2MUL u
+ VUPLLF V1, V2
+ VGFMG CONST_RU_POLY, V2, V2
+
+
+ // Compute the GF(2) product of the CRC polynomial in VO with T1(x) in
+ // V2 and XOR the intermediate result, T2(x), with the value in V1.
+ // The final result is in the rightmost word of V2.
+
+ VUPLLF V2 , V2
+ VGFMAG CONST_CRC_POLY, V2, V1, V2
+
+done:
+ VLGVF $2, V2, R2
+ XOR $0xffffffff, R2 // NOTW R2
+ MOVWZ R2, ret + 32(FP)
+ RET
diff --git a/src/html/escape.go b/src/html/escape.go
index 71906ac586..8dd1f4ad2f 100644
--- a/src/html/escape.go
+++ b/src/html/escape.go
@@ -181,7 +181,7 @@ func EscapeString(s string) string {
// UnescapeString unescapes entities like "&lt;" to become "<". It unescapes a
// larger range of entities than EscapeString escapes. For example, "&aacute;"
-// unescapes to "á", as does "&#225;" and "&xE1;".
+// unescapes to "á", as does "&#225;" and "&#xE1;".
// UnescapeString(EscapeString(s)) == s always holds, but the converse isn't
// always true.
func UnescapeString(s string) string {
diff --git a/src/html/template/css.go b/src/html/template/css.go
index 4c27cce85a..9154d8636d 100644
--- a/src/html/template/css.go
+++ b/src/html/template/css.go
@@ -243,7 +243,7 @@ func cssValueFilter(args ...interface{}) string {
return filterFailsafe
}
default:
- if c < 0x80 && isCSSNmchar(rune(c)) {
+ if c < utf8.RuneSelf && isCSSNmchar(rune(c)) {
id = append(id, c)
}
}
diff --git a/src/html/template/examplefiles_test.go b/src/html/template/examplefiles_test.go
new file mode 100644
index 0000000000..60518aee9e
--- /dev/null
+++ b/src/html/template/examplefiles_test.go
@@ -0,0 +1,226 @@
+// Copyright 2016 The Go 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 template_test
+
+import (
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+ "path/filepath"
+ "text/template"
+)
+
+// templateFile defines the contents of a template to be stored in a file, for testing.
+type templateFile struct {
+ name string
+ contents string
+}
+
+func createTestDir(files []templateFile) string {
+ dir, err := ioutil.TempDir("", "template")
+ if err != nil {
+ log.Fatal(err)
+ }
+ for _, file := range files {
+ f, err := os.Create(filepath.Join(dir, file.name))
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer f.Close()
+ _, err = io.WriteString(f, file.contents)
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+ return dir
+}
+
+// The following example is duplicated in text/template; keep them in sync.
+
+// Here we demonstrate loading a set of templates from a directory.
+func ExampleTemplate_glob() {
+ // Here we create a temporary directory and populate it with our sample
+ // template definition files; usually the template files would already
+ // exist in some location known to the program.
+ dir := createTestDir([]templateFile{
+ // T0.tmpl is a plain template file that just invokes T1.
+ {"T0.tmpl", `T0 invokes T1: ({{template "T1"}})`},
+ // T1.tmpl defines a template, T1 that invokes T2.
+ {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
+ // T2.tmpl defines a template T2.
+ {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
+ })
+ // Clean up after the test; another quirk of running as an example.
+ defer os.RemoveAll(dir)
+
+ // pattern is the glob pattern used to find all the template files.
+ pattern := filepath.Join(dir, "*.tmpl")
+
+ // Here starts the example proper.
+ // T0.tmpl is the first name matched, so it becomes the starting template,
+ // the value returned by ParseGlob.
+ tmpl := template.Must(template.ParseGlob(pattern))
+
+ err := tmpl.Execute(os.Stdout, nil)
+ if err != nil {
+ log.Fatalf("template execution: %s", err)
+ }
+ // Output:
+ // T0 invokes T1: (T1 invokes T2: (This is T2))
+}
+
+// Here we demonstrate loading a set of templates from files in different directories
+func ExampleTemplate_parsefiles() {
+ // Here we create different temporary directories and populate them with our sample
+ // template definition files; usually the template files would already
+ // exist in some location known to the program.
+ dir1 := createTestDir([]templateFile{
+ // T1.tmpl is a plain template file that just invokes T2.
+ {"T1.tmpl", `T1 invokes T2: ({{template "T2"}})`},
+ })
+
+ dir2 := createTestDir([]templateFile{
+ // T2.tmpl defines a template T2.
+ {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
+ })
+
+ // Clean up after the test; another quirk of running as an example.
+ defer func(dirs ...string) {
+ for _, dir := range dirs {
+ os.RemoveAll(dir)
+ }
+ }(dir1, dir2)
+
+ // Here starts the example proper.
+ // Let's just parse only dir1/T0 and dir2/T2
+ paths := []string{
+ filepath.Join(dir1, "T1.tmpl"),
+ filepath.Join(dir2, "T2.tmpl"),
+ }
+ tmpl := template.Must(template.ParseFiles(paths...))
+
+ err := tmpl.Execute(os.Stdout, nil)
+ if err != nil {
+ log.Fatalf("template execution: %s", err)
+ }
+ // Output:
+ // T1 invokes T2: (This is T2)
+}
+
+// The following example is duplicated in text/template; keep them in sync.
+
+// This example demonstrates one way to share some templates
+// and use them in different contexts. In this variant we add multiple driver
+// templates by hand to an existing bundle of templates.
+func ExampleTemplate_helpers() {
+ // Here we create a temporary directory and populate it with our sample
+ // template definition files; usually the template files would already
+ // exist in some location known to the program.
+ dir := createTestDir([]templateFile{
+ // T1.tmpl defines a template, T1 that invokes T2.
+ {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
+ // T2.tmpl defines a template T2.
+ {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
+ })
+ // Clean up after the test; another quirk of running as an example.
+ defer os.RemoveAll(dir)
+
+ // pattern is the glob pattern used to find all the template files.
+ pattern := filepath.Join(dir, "*.tmpl")
+
+ // Here starts the example proper.
+ // Load the helpers.
+ templates := template.Must(template.ParseGlob(pattern))
+ // Add one driver template to the bunch; we do this with an explicit template definition.
+ _, err := templates.Parse("{{define `driver1`}}Driver 1 calls T1: ({{template `T1`}})\n{{end}}")
+ if err != nil {
+ log.Fatal("parsing driver1: ", err)
+ }
+ // Add another driver template.
+ _, err = templates.Parse("{{define `driver2`}}Driver 2 calls T2: ({{template `T2`}})\n{{end}}")
+ if err != nil {
+ log.Fatal("parsing driver2: ", err)
+ }
+ // We load all the templates before execution. This package does not require
+ // that behavior but html/template's escaping does, so it's a good habit.
+ err = templates.ExecuteTemplate(os.Stdout, "driver1", nil)
+ if err != nil {
+ log.Fatalf("driver1 execution: %s", err)
+ }
+ err = templates.ExecuteTemplate(os.Stdout, "driver2", nil)
+ if err != nil {
+ log.Fatalf("driver2 execution: %s", err)
+ }
+ // Output:
+ // Driver 1 calls T1: (T1 invokes T2: (This is T2))
+ // Driver 2 calls T2: (This is T2)
+}
+
+// The following example is duplicated in text/template; keep them in sync.
+
+// This example demonstrates how to use one group of driver
+// templates with distinct sets of helper templates.
+func ExampleTemplate_share() {
+ // Here we create a temporary directory and populate it with our sample
+ // template definition files; usually the template files would already
+ // exist in some location known to the program.
+ dir := createTestDir([]templateFile{
+ // T0.tmpl is a plain template file that just invokes T1.
+ {"T0.tmpl", "T0 ({{.}} version) invokes T1: ({{template `T1`}})\n"},
+ // T1.tmpl defines a template, T1 that invokes T2. Note T2 is not defined
+ {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
+ })
+ // Clean up after the test; another quirk of running as an example.
+ defer os.RemoveAll(dir)
+
+ // pattern is the glob pattern used to find all the template files.
+ pattern := filepath.Join(dir, "*.tmpl")
+
+ // Here starts the example proper.
+ // Load the drivers.
+ drivers := template.Must(template.ParseGlob(pattern))
+
+ // We must define an implementation of the T2 template. First we clone
+ // the drivers, then add a definition of T2 to the template name space.
+
+ // 1. Clone the helper set to create a new name space from which to run them.
+ first, err := drivers.Clone()
+ if err != nil {
+ log.Fatal("cloning helpers: ", err)
+ }
+ // 2. Define T2, version A, and parse it.
+ _, err = first.Parse("{{define `T2`}}T2, version A{{end}}")
+ if err != nil {
+ log.Fatal("parsing T2: ", err)
+ }
+
+ // Now repeat the whole thing, using a different version of T2.
+ // 1. Clone the drivers.
+ second, err := drivers.Clone()
+ if err != nil {
+ log.Fatal("cloning drivers: ", err)
+ }
+ // 2. Define T2, version B, and parse it.
+ _, err = second.Parse("{{define `T2`}}T2, version B{{end}}")
+ if err != nil {
+ log.Fatal("parsing T2: ", err)
+ }
+
+ // Execute the templates in the reverse order to verify the
+ // first is unaffected by the second.
+ err = second.ExecuteTemplate(os.Stdout, "T0.tmpl", "second")
+ if err != nil {
+ log.Fatalf("second execution: %s", err)
+ }
+ err = first.ExecuteTemplate(os.Stdout, "T0.tmpl", "first")
+ if err != nil {
+ log.Fatalf("first: execution: %s", err)
+ }
+
+ // Output:
+ // T0 (second version) invokes T1: (T1 invokes T2: (T2, version B))
+ // T0 (first version) invokes T1: (T1 invokes T2: (T2, version A))
+}
diff --git a/src/html/template/template.go b/src/html/template/template.go
index 96ab268a7f..063e46d6bf 100644
--- a/src/html/template/template.go
+++ b/src/html/template/template.go
@@ -346,6 +346,11 @@ func Must(t *Template, err error) *Template {
// the named files. The returned template's name will have the (base) name and
// (parsed) contents of the first file. There must be at least one file.
// If an error occurs, parsing stops and the returned *Template is nil.
+//
+// When parsing multiple files with the same name in different directories,
+// the last one mentioned will be the one that results.
+// For instance, ParseFiles("a/foo", "b/foo") stores "b/foo" as the template
+// named "foo", while "a/foo" is unavailable.
func ParseFiles(filenames ...string) (*Template, error) {
return parseFiles(nil, filenames...)
}
@@ -353,6 +358,9 @@ func ParseFiles(filenames ...string) (*Template, error) {
// ParseFiles parses the named files and associates the resulting templates with
// t. If an error occurs, parsing stops and the returned template is nil;
// otherwise it is t. There must be at least one file.
+//
+// When parsing multiple files with the same name in different directories,
+// the last one mentioned will be the one that results.
func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
return parseFiles(t, filenames...)
}
@@ -399,6 +407,9 @@ func parseFiles(t *Template, filenames ...string) (*Template, error) {
// returned template will have the (base) name and (parsed) contents of the
// first file matched by the pattern. ParseGlob is equivalent to calling
// ParseFiles with the list of files matched by the pattern.
+//
+// When parsing multiple files with the same name in different directories,
+// the last one mentioned will be the one that results.
func ParseGlob(pattern string) (*Template, error) {
return parseGlob(nil, pattern)
}
@@ -408,6 +419,9 @@ func ParseGlob(pattern string) (*Template, error) {
// processed by filepath.Glob and must match at least one file. ParseGlob is
// equivalent to calling t.ParseFiles with the list of files matched by the
// pattern.
+//
+// When parsing multiple files with the same name in different directories,
+// the last one mentioned will be the one that results.
func (t *Template) ParseGlob(pattern string) (*Template, error) {
return parseGlob(t, pattern)
}
diff --git a/src/html/template/url.go b/src/html/template/url.go
index 2ca76bf389..246bfd32cd 100644
--- a/src/html/template/url.go
+++ b/src/html/template/url.go
@@ -17,7 +17,7 @@ func urlFilter(args ...interface{}) string {
if t == contentTypeURL {
return s
}
- if i := strings.IndexRune(s, ':'); i >= 0 && strings.IndexRune(s[:i], '/') < 0 {
+ if i := strings.IndexRune(s, ':'); i >= 0 && !strings.ContainsRune(s[:i], '/') {
protocol := strings.ToLower(s[:i])
if protocol != "http" && protocol != "https" && protocol != "mailto" {
return "#" + filterFailsafe
diff --git a/src/image/color/ycbcr.go b/src/image/color/ycbcr.go
index 904434f6a3..3df5d3675d 100644
--- a/src/image/color/ycbcr.go
+++ b/src/image/color/ycbcr.go
@@ -15,24 +15,37 @@ func RGBToYCbCr(r, g, b uint8) (uint8, uint8, uint8) {
r1 := int32(r)
g1 := int32(g)
b1 := int32(b)
+
+ // yy is in range [0,0xff].
yy := (19595*r1 + 38470*g1 + 7471*b1 + 1<<15) >> 16
- cb := (-11056*r1 - 21712*g1 + 32768*b1 + 257<<15) >> 16
- cr := (32768*r1 - 27440*g1 - 5328*b1 + 257<<15) >> 16
- if yy < 0 {
- yy = 0
- } else if yy > 0xff {
- yy = 0xff
- }
- if cb < 0 {
- cb = 0
- } else if cb > 0xff {
- cb = 0xff
+
+ // The bit twiddling below is equivalent to
+ //
+ // cb := (-11056*r1 - 21712*g1 + 32768*b1 + 257<<15) >> 16
+ // if cb < 0 {
+ // cb = 0
+ // } else if cb > 0xff {
+ // cb = ^int32(0)
+ // }
+ //
+ // but uses fewer branches and is faster.
+ // Note that the uint8 type conversion in the return
+ // statement will convert ^int32(0) to 0xff.
+ // The code below to compute cr uses a similar pattern.
+ cb := -11056*r1 - 21712*g1 + 32768*b1 + 257<<15
+ if uint32(cb)&0xff000000 == 0 {
+ cb >>= 16
+ } else {
+ cb = ^(cb >> 31)
}
- if cr < 0 {
- cr = 0
- } else if cr > 0xff {
- cr = 0xff
+
+ cr := 32768*r1 - 27440*g1 - 5328*b1 + 257<<15
+ if uint32(cr)&0xff000000 == 0 {
+ cr >>= 16
+ } else {
+ cr = ^(cr >> 31)
}
+
return uint8(yy), uint8(cb), uint8(cr)
}
@@ -44,27 +57,44 @@ func YCbCrToRGB(y, cb, cr uint8) (uint8, uint8, uint8) {
// B = Y' + 1.77200*(Cb-128)
// http://www.w3.org/Graphics/JPEG/jfif3.pdf says Y but means Y'.
- yy1 := int32(y) * 0x10100 // Convert 0x12 to 0x121200.
+ yy1 := int32(y) * 0x010100 // Convert 0x12 to 0x121200.
cb1 := int32(cb) - 128
cr1 := int32(cr) - 128
- r := (yy1 + 91881*cr1) >> 16
- g := (yy1 - 22554*cb1 - 46802*cr1) >> 16
- b := (yy1 + 116130*cb1) >> 16
- if r < 0 {
- r = 0
- } else if r > 0xff {
- r = 0xff
+
+ // The bit twiddling below is equivalent to
+ //
+ // r := (yy1 + 91881*cr1) >> 16
+ // if r < 0 {
+ // r = 0
+ // } else if r > 0xff {
+ // r = ^int32(0)
+ // }
+ //
+ // but uses fewer branches and is faster.
+ // Note that the uint8 type conversion in the return
+ // statement will convert ^int32(0) to 0xff.
+ // The code below to compute g and b uses a similar pattern.
+ r := yy1 + 91881*cr1
+ if uint32(r)&0xff000000 == 0 {
+ r >>= 16
+ } else {
+ r = ^(r >> 31)
}
- if g < 0 {
- g = 0
- } else if g > 0xff {
- g = 0xff
+
+ g := yy1 - 22554*cb1 - 46802*cr1
+ if uint32(g)&0xff000000 == 0 {
+ g >>= 16
+ } else {
+ g = ^(g >> 31)
}
- if b < 0 {
- b = 0
- } else if b > 0xff {
- b = 0xff
+
+ b := yy1 + 116130*cb1
+ if uint32(b)&0xff000000 == 0 {
+ b >>= 16
+ } else {
+ b = ^(b >> 31)
}
+
return uint8(r), uint8(g), uint8(b)
}
@@ -220,10 +250,10 @@ func RGBToCMYK(r, g, b uint8) (uint8, uint8, uint8, uint8) {
// CMYKToRGB converts a CMYK quadruple to an RGB triple.
func CMYKToRGB(c, m, y, k uint8) (uint8, uint8, uint8) {
- w := uint32(0xffff - uint32(k)*0x101)
- r := uint32(0xffff-uint32(c)*0x101) * w / 0xffff
- g := uint32(0xffff-uint32(m)*0x101) * w / 0xffff
- b := uint32(0xffff-uint32(y)*0x101) * w / 0xffff
+ w := 0xffff - uint32(k)*0x101
+ r := (0xffff - uint32(c)*0x101) * w / 0xffff
+ g := (0xffff - uint32(m)*0x101) * w / 0xffff
+ b := (0xffff - uint32(y)*0x101) * w / 0xffff
return uint8(r >> 8), uint8(g >> 8), uint8(b >> 8)
}
@@ -239,11 +269,11 @@ func (c CMYK) RGBA() (uint32, uint32, uint32, uint32) {
// This code is a copy of the CMYKToRGB function above, except that it
// returns values in the range [0, 0xffff] instead of [0, 0xff].
- w := uint32(0xffff - uint32(c.K)*0x101)
- r := uint32(0xffff-uint32(c.C)*0x101) * w / 0xffff
- g := uint32(0xffff-uint32(c.M)*0x101) * w / 0xffff
- b := uint32(0xffff-uint32(c.Y)*0x101) * w / 0xffff
- return uint32(r), uint32(g), uint32(b), 0xffff
+ w := 0xffff - uint32(c.K)*0x101
+ r := (0xffff - uint32(c.C)*0x101) * w / 0xffff
+ g := (0xffff - uint32(c.M)*0x101) * w / 0xffff
+ b := (0xffff - uint32(c.Y)*0x101) * w / 0xffff
+ return r, g, b, 0xffff
}
// CMYKModel is the Model for CMYK colors.
diff --git a/src/image/color/ycbcr_test.go b/src/image/color/ycbcr_test.go
index f5e7cbf335..561699f4e0 100644
--- a/src/image/color/ycbcr_test.go
+++ b/src/image/color/ycbcr_test.go
@@ -171,3 +171,47 @@ func TestPalette(t *testing.T) {
t.Errorf("got %v, want %v", got, want)
}
}
+
+var sink uint8
+
+func BenchmarkYCbCrToRGB(b *testing.B) {
+ // YCbCrToRGB does saturating arithmetic.
+ // Low, middle, and high values can take
+ // different paths through the generated code.
+ b.Run("0", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ sink, sink, sink = YCbCrToRGB(0, 0, 0)
+ }
+ })
+ b.Run("128", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ sink, sink, sink = YCbCrToRGB(128, 128, 128)
+ }
+ })
+ b.Run("255", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ sink, sink, sink = YCbCrToRGB(255, 255, 255)
+ }
+ })
+}
+
+func BenchmarkRGBToYCbCr(b *testing.B) {
+ // RGBToYCbCr does saturating arithmetic.
+ // Different values can take different paths
+ // through the generated code.
+ b.Run("0", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ sink, sink, sink = RGBToYCbCr(0, 0, 0)
+ }
+ })
+ b.Run("Cb", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ sink, sink, sink = RGBToYCbCr(0, 0, 255)
+ }
+ })
+ b.Run("Cr", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ sink, sink, sink = RGBToYCbCr(255, 0, 0)
+ }
+ })
+}
diff --git a/src/image/draw/draw.go b/src/image/draw/draw.go
index 94e3575663..6a16cd39cf 100644
--- a/src/image/draw/draw.go
+++ b/src/image/draw/draw.go
@@ -634,10 +634,10 @@ func drawPaletted(dst Image, r image.Rectangle, src image.Image, sp image.Point,
if !floydSteinberg {
continue
}
- er -= int32(palette[bestIndex][0])
- eg -= int32(palette[bestIndex][1])
- eb -= int32(palette[bestIndex][2])
- ea -= int32(palette[bestIndex][3])
+ er -= palette[bestIndex][0]
+ eg -= palette[bestIndex][1]
+ eb -= palette[bestIndex][2]
+ ea -= palette[bestIndex][3]
} else {
out.R = uint16(er)
diff --git a/src/image/internal/imageutil/gen.go b/src/image/internal/imageutil/gen.go
index fc1e707f0f..6792b28a45 100644
--- a/src/image/internal/imageutil/gen.go
+++ b/src/image/internal/imageutil/gen.go
@@ -95,32 +95,51 @@ const sratioCase = `
%s
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
- yy1 := int32(src.Y[yi]) * 0x10100 // Convert 0x12 to 0x121200.
+ yy1 := int32(src.Y[yi]) * 0x010100 // Convert 0x12 to 0x121200.
cb1 := int32(src.Cb[ci]) - 128
cr1 := int32(src.Cr[ci]) - 128
- r := (yy1 + 91881*cr1) >> 16
- g := (yy1 - 22554*cb1 - 46802*cr1) >> 16
- b := (yy1 + 116130*cb1) >> 16
- if r < 0 {
- r = 0
- } else if r > 255 {
- r = 255
+
+ // The bit twiddling below is equivalent to
+ //
+ // r := (yy1 + 91881*cr1) >> 16
+ // if r < 0 {
+ // r = 0
+ // } else if r > 0xff {
+ // r = ^int32(0)
+ // }
+ //
+ // but uses fewer branches and is faster.
+ // Note that the uint8 type conversion in the return
+ // statement will convert ^int32(0) to 0xff.
+ // The code below to compute g and b uses a similar pattern.
+ r := yy1 + 91881*cr1
+ if uint32(r)&0xff000000 == 0 {
+ r >>= 16
+ } else {
+ r = ^(r >> 31)
}
- if g < 0 {
- g = 0
- } else if g > 255 {
- g = 255
+
+ g := yy1 - 22554*cb1 - 46802*cr1
+ if uint32(g)&0xff000000 == 0 {
+ g >>= 16
+ } else {
+ g = ^(g >> 31)
}
- if b < 0 {
- b = 0
- } else if b > 255 {
- b = 255
+
+ b := yy1 + 116130*cb1
+ if uint32(b)&0xff000000 == 0 {
+ b >>= 16
+ } else {
+ b = ^(b >> 31)
}
- dpix[x+0] = uint8(r)
- dpix[x+1] = uint8(g)
- dpix[x+2] = uint8(b)
- dpix[x+3] = 255
+
+ // use a temp slice to hint to the compiler that a single bounds check suffices
+ rgba := dpix[x : x+4 : len(dpix)]
+ rgba[0] = uint8(r)
+ rgba[1] = uint8(g)
+ rgba[2] = uint8(b)
+ rgba[3] = 255
}
}
`
diff --git a/src/image/internal/imageutil/impl.go b/src/image/internal/imageutil/impl.go
index fd7826d4a9..3696b08e41 100644
--- a/src/image/internal/imageutil/impl.go
+++ b/src/image/internal/imageutil/impl.go
@@ -44,32 +44,50 @@ func DrawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Po
for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 {
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
- yy1 := int32(src.Y[yi]) * 0x10100 // Convert 0x12 to 0x121200.
+ yy1 := int32(src.Y[yi]) * 0x010100 // Convert 0x12 to 0x121200.
cb1 := int32(src.Cb[ci]) - 128
cr1 := int32(src.Cr[ci]) - 128
- r := (yy1 + 91881*cr1) >> 16
- g := (yy1 - 22554*cb1 - 46802*cr1) >> 16
- b := (yy1 + 116130*cb1) >> 16
- if r < 0 {
- r = 0
- } else if r > 255 {
- r = 255
+
+ // The bit twiddling below is equivalent to
+ //
+ // r := (yy1 + 91881*cr1) >> 16
+ // if r < 0 {
+ // r = 0
+ // } else if r > 0xff {
+ // r = ^int32(0)
+ // }
+ //
+ // but uses fewer branches and is faster.
+ // Note that the uint8 type conversion in the return
+ // statement will convert ^int32(0) to 0xff.
+ // The code below to compute g and b uses a similar pattern.
+ r := yy1 + 91881*cr1
+ if uint32(r)&0xff000000 == 0 {
+ r >>= 16
+ } else {
+ r = ^(r >> 31)
}
- if g < 0 {
- g = 0
- } else if g > 255 {
- g = 255
+
+ g := yy1 - 22554*cb1 - 46802*cr1
+ if uint32(g)&0xff000000 == 0 {
+ g >>= 16
+ } else {
+ g = ^(g >> 31)
}
- if b < 0 {
- b = 0
- } else if b > 255 {
- b = 255
+
+ b := yy1 + 116130*cb1
+ if uint32(b)&0xff000000 == 0 {
+ b >>= 16
+ } else {
+ b = ^(b >> 31)
}
- dpix[x+0] = uint8(r)
- dpix[x+1] = uint8(g)
- dpix[x+2] = uint8(b)
- dpix[x+3] = 255
+ // use a temp slice to hint to the compiler that a single bounds check suffices
+ rgba := dpix[x : x+4 : len(dpix)]
+ rgba[0] = uint8(r)
+ rgba[1] = uint8(g)
+ rgba[2] = uint8(b)
+ rgba[3] = 255
}
}
@@ -83,32 +101,50 @@ func DrawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Po
ci := ciBase + sx/2
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
- yy1 := int32(src.Y[yi]) * 0x10100 // Convert 0x12 to 0x121200.
+ yy1 := int32(src.Y[yi]) * 0x010100 // Convert 0x12 to 0x121200.
cb1 := int32(src.Cb[ci]) - 128
cr1 := int32(src.Cr[ci]) - 128
- r := (yy1 + 91881*cr1) >> 16
- g := (yy1 - 22554*cb1 - 46802*cr1) >> 16
- b := (yy1 + 116130*cb1) >> 16
- if r < 0 {
- r = 0
- } else if r > 255 {
- r = 255
+
+ // The bit twiddling below is equivalent to
+ //
+ // r := (yy1 + 91881*cr1) >> 16
+ // if r < 0 {
+ // r = 0
+ // } else if r > 0xff {
+ // r = ^int32(0)
+ // }
+ //
+ // but uses fewer branches and is faster.
+ // Note that the uint8 type conversion in the return
+ // statement will convert ^int32(0) to 0xff.
+ // The code below to compute g and b uses a similar pattern.
+ r := yy1 + 91881*cr1
+ if uint32(r)&0xff000000 == 0 {
+ r >>= 16
+ } else {
+ r = ^(r >> 31)
}
- if g < 0 {
- g = 0
- } else if g > 255 {
- g = 255
+
+ g := yy1 - 22554*cb1 - 46802*cr1
+ if uint32(g)&0xff000000 == 0 {
+ g >>= 16
+ } else {
+ g = ^(g >> 31)
}
- if b < 0 {
- b = 0
- } else if b > 255 {
- b = 255
+
+ b := yy1 + 116130*cb1
+ if uint32(b)&0xff000000 == 0 {
+ b >>= 16
+ } else {
+ b = ^(b >> 31)
}
- dpix[x+0] = uint8(r)
- dpix[x+1] = uint8(g)
- dpix[x+2] = uint8(b)
- dpix[x+3] = 255
+ // use a temp slice to hint to the compiler that a single bounds check suffices
+ rgba := dpix[x : x+4 : len(dpix)]
+ rgba[0] = uint8(r)
+ rgba[1] = uint8(g)
+ rgba[2] = uint8(b)
+ rgba[3] = 255
}
}
@@ -122,32 +158,50 @@ func DrawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Po
ci := ciBase + sx/2
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
- yy1 := int32(src.Y[yi]) * 0x10100 // Convert 0x12 to 0x121200.
+ yy1 := int32(src.Y[yi]) * 0x010100 // Convert 0x12 to 0x121200.
cb1 := int32(src.Cb[ci]) - 128
cr1 := int32(src.Cr[ci]) - 128
- r := (yy1 + 91881*cr1) >> 16
- g := (yy1 - 22554*cb1 - 46802*cr1) >> 16
- b := (yy1 + 116130*cb1) >> 16
- if r < 0 {
- r = 0
- } else if r > 255 {
- r = 255
+
+ // The bit twiddling below is equivalent to
+ //
+ // r := (yy1 + 91881*cr1) >> 16
+ // if r < 0 {
+ // r = 0
+ // } else if r > 0xff {
+ // r = ^int32(0)
+ // }
+ //
+ // but uses fewer branches and is faster.
+ // Note that the uint8 type conversion in the return
+ // statement will convert ^int32(0) to 0xff.
+ // The code below to compute g and b uses a similar pattern.
+ r := yy1 + 91881*cr1
+ if uint32(r)&0xff000000 == 0 {
+ r >>= 16
+ } else {
+ r = ^(r >> 31)
}
- if g < 0 {
- g = 0
- } else if g > 255 {
- g = 255
+
+ g := yy1 - 22554*cb1 - 46802*cr1
+ if uint32(g)&0xff000000 == 0 {
+ g >>= 16
+ } else {
+ g = ^(g >> 31)
}
- if b < 0 {
- b = 0
- } else if b > 255 {
- b = 255
+
+ b := yy1 + 116130*cb1
+ if uint32(b)&0xff000000 == 0 {
+ b >>= 16
+ } else {
+ b = ^(b >> 31)
}
- dpix[x+0] = uint8(r)
- dpix[x+1] = uint8(g)
- dpix[x+2] = uint8(b)
- dpix[x+3] = 255
+ // use a temp slice to hint to the compiler that a single bounds check suffices
+ rgba := dpix[x : x+4 : len(dpix)]
+ rgba[0] = uint8(r)
+ rgba[1] = uint8(g)
+ rgba[2] = uint8(b)
+ rgba[3] = 255
}
}
@@ -160,32 +214,50 @@ func DrawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Po
for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 {
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
- yy1 := int32(src.Y[yi]) * 0x10100 // Convert 0x12 to 0x121200.
+ yy1 := int32(src.Y[yi]) * 0x010100 // Convert 0x12 to 0x121200.
cb1 := int32(src.Cb[ci]) - 128
cr1 := int32(src.Cr[ci]) - 128
- r := (yy1 + 91881*cr1) >> 16
- g := (yy1 - 22554*cb1 - 46802*cr1) >> 16
- b := (yy1 + 116130*cb1) >> 16
- if r < 0 {
- r = 0
- } else if r > 255 {
- r = 255
+
+ // The bit twiddling below is equivalent to
+ //
+ // r := (yy1 + 91881*cr1) >> 16
+ // if r < 0 {
+ // r = 0
+ // } else if r > 0xff {
+ // r = ^int32(0)
+ // }
+ //
+ // but uses fewer branches and is faster.
+ // Note that the uint8 type conversion in the return
+ // statement will convert ^int32(0) to 0xff.
+ // The code below to compute g and b uses a similar pattern.
+ r := yy1 + 91881*cr1
+ if uint32(r)&0xff000000 == 0 {
+ r >>= 16
+ } else {
+ r = ^(r >> 31)
}
- if g < 0 {
- g = 0
- } else if g > 255 {
- g = 255
+
+ g := yy1 - 22554*cb1 - 46802*cr1
+ if uint32(g)&0xff000000 == 0 {
+ g >>= 16
+ } else {
+ g = ^(g >> 31)
}
- if b < 0 {
- b = 0
- } else if b > 255 {
- b = 255
+
+ b := yy1 + 116130*cb1
+ if uint32(b)&0xff000000 == 0 {
+ b >>= 16
+ } else {
+ b = ^(b >> 31)
}
- dpix[x+0] = uint8(r)
- dpix[x+1] = uint8(g)
- dpix[x+2] = uint8(b)
- dpix[x+3] = 255
+ // use a temp slice to hint to the compiler that a single bounds check suffices
+ rgba := dpix[x : x+4 : len(dpix)]
+ rgba[0] = uint8(r)
+ rgba[1] = uint8(g)
+ rgba[2] = uint8(b)
+ rgba[3] = 255
}
}
diff --git a/src/internal/testenv/testenv.go b/src/internal/testenv/testenv.go
index e751e0cf11..9e684e3034 100644
--- a/src/internal/testenv/testenv.go
+++ b/src/internal/testenv/testenv.go
@@ -11,6 +11,7 @@
package testenv
import (
+ "flag"
"os"
"os/exec"
"path/filepath"
@@ -124,3 +125,11 @@ func MustHaveExternalNetwork(t *testing.T) {
t.Skipf("skipping test: no external network in -short mode")
}
}
+
+var flaky = flag.Bool("flaky", false, "run known-flaky tests too")
+
+func SkipFlaky(t *testing.T, issue int) {
+ if !*flaky {
+ t.Skipf("skipping known flaky test without the -flaky flag; see golang.org/issue/%d", issue)
+ }
+}
diff --git a/src/internal/trace/order.go b/src/internal/trace/order.go
new file mode 100644
index 0000000000..8ca2da52aa
--- /dev/null
+++ b/src/internal/trace/order.go
@@ -0,0 +1,278 @@
+// Copyright 2016 The Go 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 trace
+
+import (
+ "fmt"
+ "sort"
+)
+
+type eventBatch struct {
+ events []*Event
+ selected bool
+}
+
+type orderEvent struct {
+ ev *Event
+ batch int
+ g uint64
+ init gState
+ next gState
+}
+
+type gStatus int
+
+type gState struct {
+ seq uint64
+ status gStatus
+}
+
+const (
+ gDead gStatus = iota
+ gRunnable
+ gRunning
+ gWaiting
+
+ unordered = ^uint64(0)
+ garbage = ^uint64(0) - 1
+ noseq = ^uint64(0)
+ seqinc = ^uint64(0) - 1
+)
+
+// order1007 merges a set of per-P event batches into a single, consistent stream.
+// The high level idea is as follows. Events within an individual batch are in
+// correct order, because they are emitted by a single P. So we need to produce
+// a correct interleaving of the batches. To do this we take first unmerged event
+// from each batch (frontier). Then choose subset that is "ready" to be merged,
+// that is, events for which all dependencies are already merged. Then we choose
+// event with the lowest timestamp from the subset, merge it and repeat.
+// This approach ensures that we form a consistent stream even if timestamps are
+// incorrect (condition observed on some machines).
+func order1007(m map[int][]*Event) (events []*Event, err error) {
+ pending := 0
+ var batches []*eventBatch
+ for _, v := range m {
+ pending += len(v)
+ batches = append(batches, &eventBatch{v, false})
+ }
+ gs := make(map[uint64]gState)
+ var frontier []orderEvent
+ for ; pending != 0; pending-- {
+ for i, b := range batches {
+ if b.selected || len(b.events) == 0 {
+ continue
+ }
+ ev := b.events[0]
+ g, init, next := stateTransition(ev)
+ if !transitionReady(g, gs[g], init) {
+ continue
+ }
+ frontier = append(frontier, orderEvent{ev, i, g, init, next})
+ b.events = b.events[1:]
+ b.selected = true
+ // Get rid of "Local" events, they are intended merely for ordering.
+ switch ev.Type {
+ case EvGoStartLocal:
+ ev.Type = EvGoStart
+ case EvGoUnblockLocal:
+ ev.Type = EvGoUnblock
+ case EvGoSysExitLocal:
+ ev.Type = EvGoSysExit
+ }
+ }
+ if len(frontier) == 0 {
+ return nil, fmt.Errorf("no consistent ordering of events possible")
+ }
+ sort.Sort(orderEventList(frontier))
+ f := frontier[0]
+ frontier[0] = frontier[len(frontier)-1]
+ frontier = frontier[:len(frontier)-1]
+ events = append(events, f.ev)
+ transition(gs, f.g, f.init, f.next)
+ if !batches[f.batch].selected {
+ panic("frontier batch is not selected")
+ }
+ batches[f.batch].selected = false
+ }
+
+ // At this point we have a consistent stream of events.
+ // Make sure time stamps respect the ordering.
+ // The tests will skip (not fail) the test case if they see this error.
+ if !sort.IsSorted(eventList(events)) {
+ return nil, ErrTimeOrder
+ }
+
+ // The last part is giving correct timestamps to EvGoSysExit events.
+ // The problem with EvGoSysExit is that actual syscall exit timestamp (ev.Args[2])
+ // is potentially acquired long before event emission. So far we've used
+ // timestamp of event emission (ev.Ts).
+ // We could not set ev.Ts = ev.Args[2] earlier, because it would produce
+ // seemingly broken timestamps (misplaced event).
+ // We also can't simply update the timestamp and resort events, because
+ // if timestamps are broken we will misplace the event and later report
+ // logically broken trace (instead of reporting broken timestamps).
+ lastSysBlock := make(map[uint64]int64)
+ for _, ev := range events {
+ switch ev.Type {
+ case EvGoSysBlock, EvGoInSyscall:
+ lastSysBlock[ev.G] = ev.Ts
+ case EvGoSysExit:
+ ts := int64(ev.Args[2])
+ if ts == 0 {
+ continue
+ }
+ block := lastSysBlock[ev.G]
+ if block == 0 {
+ return nil, fmt.Errorf("stray syscall exit")
+ }
+ if ts < block {
+ return nil, ErrTimeOrder
+ }
+ ev.Ts = ts
+ }
+ }
+ sort.Stable(eventList(events))
+
+ return
+}
+
+// stateTransition returns goroutine state (sequence and status) when the event
+// becomes ready for merging (init) and the goroutine state after the event (next).
+func stateTransition(ev *Event) (g uint64, init, next gState) {
+ switch ev.Type {
+ case EvGoCreate:
+ g = ev.Args[0]
+ init = gState{0, gDead}
+ next = gState{1, gRunnable}
+ case EvGoWaiting, EvGoInSyscall:
+ g = ev.G
+ init = gState{1, gRunnable}
+ next = gState{2, gWaiting}
+ case EvGoStart:
+ g = ev.G
+ init = gState{ev.Args[1], gRunnable}
+ next = gState{ev.Args[1] + 1, gRunning}
+ case EvGoStartLocal:
+ // noseq means that this event is ready for merging as soon as
+ // frontier reaches it (EvGoStartLocal is emitted on the same P
+ // as the corresponding EvGoCreate/EvGoUnblock, and thus the latter
+ // is already merged).
+ // seqinc is a stub for cases when event increments g sequence,
+ // but since we don't know current seq we also don't know next seq.
+ g = ev.G
+ init = gState{noseq, gRunnable}
+ next = gState{seqinc, gRunning}
+ case EvGoBlock, EvGoBlockSend, EvGoBlockRecv, EvGoBlockSelect,
+ EvGoBlockSync, EvGoBlockCond, EvGoBlockNet, EvGoSleep, EvGoSysBlock:
+ g = ev.G
+ init = gState{noseq, gRunning}
+ next = gState{noseq, gWaiting}
+ case EvGoSched, EvGoPreempt:
+ g = ev.G
+ init = gState{noseq, gRunning}
+ next = gState{noseq, gRunnable}
+ case EvGoUnblock, EvGoSysExit:
+ g = ev.Args[0]
+ init = gState{ev.Args[1], gWaiting}
+ next = gState{ev.Args[1] + 1, gRunnable}
+ case EvGoUnblockLocal, EvGoSysExitLocal:
+ g = ev.Args[0]
+ init = gState{noseq, gWaiting}
+ next = gState{seqinc, gRunnable}
+ case EvGCStart:
+ g = garbage
+ init = gState{ev.Args[0], gDead}
+ next = gState{ev.Args[0] + 1, gDead}
+ default:
+ // no ordering requirements
+ g = unordered
+ }
+ return
+}
+
+func transitionReady(g uint64, curr, init gState) bool {
+ return g == unordered || (init.seq == noseq || init.seq == curr.seq) && init.status == curr.status
+}
+
+func transition(gs map[uint64]gState, g uint64, init, next gState) {
+ if g == unordered {
+ return
+ }
+ curr := gs[g]
+ if !transitionReady(g, curr, init) {
+ panic("event sequences are broken")
+ }
+ switch next.seq {
+ case noseq:
+ next.seq = curr.seq
+ case seqinc:
+ next.seq = curr.seq + 1
+ }
+ gs[g] = next
+}
+
+// order1005 merges a set of per-P event batches into a single, consistent stream.
+func order1005(m map[int][]*Event) (events []*Event, err error) {
+ for _, batch := range m {
+ events = append(events, batch...)
+ }
+ for _, ev := range events {
+ if ev.Type == EvGoSysExit {
+ // EvGoSysExit emission is delayed until the thread has a P.
+ // Give it the real sequence number and time stamp.
+ ev.seq = int64(ev.Args[1])
+ if ev.Args[2] != 0 {
+ ev.Ts = int64(ev.Args[2])
+ }
+ }
+ }
+ sort.Sort(eventSeqList(events))
+ if !sort.IsSorted(eventList(events)) {
+ return nil, ErrTimeOrder
+ }
+ return
+}
+
+type orderEventList []orderEvent
+
+func (l orderEventList) Len() int {
+ return len(l)
+}
+
+func (l orderEventList) Less(i, j int) bool {
+ return l[i].ev.Ts < l[j].ev.Ts
+}
+
+func (l orderEventList) Swap(i, j int) {
+ l[i], l[j] = l[j], l[i]
+}
+
+type eventList []*Event
+
+func (l eventList) Len() int {
+ return len(l)
+}
+
+func (l eventList) Less(i, j int) bool {
+ return l[i].Ts < l[j].Ts
+}
+
+func (l eventList) Swap(i, j int) {
+ l[i], l[j] = l[j], l[i]
+}
+
+type eventSeqList []*Event
+
+func (l eventSeqList) Len() int {
+ return len(l)
+}
+
+func (l eventSeqList) Less(i, j int) bool {
+ return l[i].seq < l[j].seq
+}
+
+func (l eventSeqList) Swap(i, j int) {
+ l[i], l[j] = l[j], l[i]
+}
diff --git a/src/internal/trace/parser.go b/src/internal/trace/parser.go
index e325678733..843d0eaf63 100644
--- a/src/internal/trace/parser.go
+++ b/src/internal/trace/parser.go
@@ -9,18 +9,19 @@ import (
"bytes"
"fmt"
"io"
+ "math/rand"
"os"
"os/exec"
- "sort"
"strconv"
"strings"
+ _ "unsafe"
)
// Event describes one event in the trace.
type Event struct {
Off int // offset in input file (for debugging and error reporting)
Type byte // one of Ev*
- Seq int64 // sequence number
+ seq int64 // sequence number
Ts int64 // timestamp in nanoseconds
P int // P on which the event happened (can be one of TimerP, NetpollP, SyscallP)
G uint64 // G on which the event happened
@@ -58,12 +59,12 @@ const (
)
// Parse parses, post-processes and verifies the trace.
-func Parse(r io.Reader) ([]*Event, error) {
- rawEvents, err := readTrace(r)
+func Parse(r io.Reader, bin string) ([]*Event, error) {
+ ver, rawEvents, strings, err := readTrace(r)
if err != nil {
return nil, err
}
- events, err := parseEvents(rawEvents)
+ events, stacks, err := parseEvents(ver, rawEvents, strings)
if err != nil {
return nil, err
}
@@ -71,10 +72,21 @@ func Parse(r io.Reader) ([]*Event, error) {
if err != nil {
return nil, err
}
- err = postProcessTrace(events)
+ err = postProcessTrace(ver, events)
if err != nil {
return nil, err
}
+ // Attach stack traces.
+ for _, ev := range events {
+ if ev.StkID != 0 {
+ ev.Stk = stacks[ev.StkID]
+ }
+ }
+ if ver < 1007 && bin != "" {
+ if err := symbolize(events, bin); err != nil {
+ return nil, err
+ }
+ }
return events, nil
}
@@ -87,107 +99,187 @@ type rawEvent struct {
// readTrace does wire-format parsing and verification.
// It does not care about specific event types and argument meaning.
-func readTrace(r io.Reader) ([]rawEvent, error) {
+func readTrace(r io.Reader) (ver int, events []rawEvent, strings map[uint64]string, err error) {
// Read and validate trace header.
var buf [16]byte
- off, err := r.Read(buf[:])
- if off != 16 || err != nil {
- return nil, fmt.Errorf("failed to read header: read %v, err %v", off, err)
+ off, err := io.ReadFull(r, buf[:])
+ if err != nil {
+ err = fmt.Errorf("failed to read header: read %v, err %v", off, err)
+ return
}
- if !bytes.Equal(buf[:], []byte("go 1.5 trace\x00\x00\x00\x00")) {
- return nil, fmt.Errorf("not a trace file")
+ ver, err = parseHeader(buf[:])
+ if err != nil {
+ return
+ }
+ switch ver {
+ case 1005, 1007:
+ break
+ default:
+ err = fmt.Errorf("unsupported trace file version %v.%v (update Go toolchain) %v", ver/1000, ver%1000, ver)
+ return
}
// Read events.
- var events []rawEvent
+ strings = make(map[uint64]string)
for {
// Read event type and number of arguments (1 byte).
off0 := off
- n, err := r.Read(buf[:1])
+ var n int
+ n, err = r.Read(buf[:1])
if err == io.EOF {
+ err = nil
break
}
if err != nil || n != 1 {
- return nil, fmt.Errorf("failed to read trace at offset 0x%x: n=%v err=%v", off0, n, err)
+ err = fmt.Errorf("failed to read trace at offset 0x%x: n=%v err=%v", off0, n, err)
+ return
}
off += n
typ := buf[0] << 2 >> 2
- narg := buf[0] >> 6
+ narg := buf[0]>>6 + 1
+ inlineArgs := byte(4)
+ if ver < 1007 {
+ narg++
+ inlineArgs++
+ }
+ if typ == EvNone || typ >= EvCount || EventDescriptions[typ].minVersion > ver {
+ err = fmt.Errorf("unknown event type %v at offset 0x%x", typ, off0)
+ return
+ }
+ if typ == EvString {
+ // String dictionary entry [ID, length, string].
+ var id uint64
+ id, off, err = readVal(r, off)
+ if err != nil {
+ return
+ }
+ if id == 0 {
+ err = fmt.Errorf("string at offset %d has invalid id 0", off)
+ return
+ }
+ if strings[id] != "" {
+ err = fmt.Errorf("string at offset %d has duplicate id %v", off, id)
+ return
+ }
+ var ln uint64
+ ln, off, err = readVal(r, off)
+ if err != nil {
+ return
+ }
+ if ln == 0 {
+ err = fmt.Errorf("string at offset %d has invalid length 0", off)
+ return
+ }
+ if ln > 1e6 {
+ err = fmt.Errorf("string at offset %d has too large length %v", off, ln)
+ return
+ }
+ buf := make([]byte, ln)
+ var n int
+ n, err = io.ReadFull(r, buf)
+ if err != nil {
+ err = fmt.Errorf("failed to read trace at offset %d: read %v, want %v, error %v", off, n, ln, err)
+ return
+ }
+ off += n
+ strings[id] = string(buf)
+ continue
+ }
ev := rawEvent{typ: typ, off: off0}
- if narg < 3 {
- for i := 0; i < int(narg)+2; i++ { // sequence number and time stamp are present but not counted in narg
+ if narg < inlineArgs {
+ for i := 0; i < int(narg); i++ {
var v uint64
v, off, err = readVal(r, off)
if err != nil {
- return nil, err
+ err = fmt.Errorf("failed to read event %v argument at offset %v (%v)", typ, off, err)
+ return
}
ev.args = append(ev.args, v)
}
} else {
- // If narg == 3, the first value is length of the event in bytes.
+ // More than inlineArgs args, the first value is length of the event in bytes.
var v uint64
v, off, err = readVal(r, off)
if err != nil {
- return nil, err
+ err = fmt.Errorf("failed to read event %v argument at offset %v (%v)", typ, off, err)
+ return
}
evLen := v
off1 := off
for evLen > uint64(off-off1) {
v, off, err = readVal(r, off)
if err != nil {
- return nil, err
+ err = fmt.Errorf("failed to read event %v argument at offset %v (%v)", typ, off, err)
+ return
}
ev.args = append(ev.args, v)
}
if evLen != uint64(off-off1) {
- return nil, fmt.Errorf("event has wrong length at offset 0x%x: want %v, got %v", off0, evLen, off-off1)
+ err = fmt.Errorf("event has wrong length at offset 0x%x: want %v, got %v", off0, evLen, off-off1)
+ return
}
}
events = append(events, ev)
}
- return events, nil
+ return
+}
+
+// parseHeader parses trace header of the form "go 1.7 trace\x00\x00\x00\x00"
+// and returns parsed version as 1007.
+func parseHeader(buf []byte) (int, error) {
+ if len(buf) != 16 {
+ return 0, fmt.Errorf("bad header length")
+ }
+ if buf[0] != 'g' || buf[1] != 'o' || buf[2] != ' ' ||
+ buf[3] < '1' || buf[3] > '9' ||
+ buf[4] != '.' ||
+ buf[5] < '1' || buf[5] > '9' {
+ return 0, fmt.Errorf("not a trace file")
+ }
+ ver := int(buf[5] - '0')
+ i := 0
+ for ; buf[6+i] >= '0' && buf[6+i] <= '9' && i < 2; i++ {
+ ver = ver*10 + int(buf[6+i]-'0')
+ }
+ ver += int(buf[3]-'0') * 1000
+ if !bytes.Equal(buf[6+i:], []byte(" trace\x00\x00\x00\x00")[:10-i]) {
+ return 0, fmt.Errorf("not a trace file")
+ }
+ return ver, nil
}
// Parse events transforms raw events into events.
// It does analyze and verify per-event-type arguments.
-func parseEvents(rawEvents []rawEvent) (events []*Event, err error) {
+func parseEvents(ver int, rawEvents []rawEvent, strings map[uint64]string) (events []*Event, stacks map[uint64][]*Frame, err error) {
var ticksPerSec, lastSeq, lastTs int64
var lastG, timerGoid uint64
var lastP int
lastGs := make(map[int]uint64) // last goroutine running on P
- stacks := make(map[uint64][]*Frame)
+ stacks = make(map[uint64][]*Frame)
+ batches := make(map[int][]*Event) // events by P
for _, raw := range rawEvents {
- if raw.typ == EvNone || raw.typ >= EvCount {
- err = fmt.Errorf("unknown event type %v at offset 0x%x", raw.typ, raw.off)
- return
- }
desc := EventDescriptions[raw.typ]
if desc.Name == "" {
err = fmt.Errorf("missing description for event type %v", raw.typ)
return
}
- if raw.typ != EvStack {
- narg := len(desc.Args)
- if desc.Stack {
- narg++
- }
- if raw.typ != EvBatch && raw.typ != EvFrequency && raw.typ != EvTimerGoroutine {
- narg++ // sequence number
- narg++ // timestamp
- }
- if len(raw.args) != narg {
- err = fmt.Errorf("%v has wrong number of arguments at offset 0x%x: want %v, got %v",
- desc.Name, raw.off, narg, len(raw.args))
- return
- }
+ narg := argNum(raw, ver)
+ if len(raw.args) != narg {
+ err = fmt.Errorf("%v has wrong number of arguments at offset 0x%x: want %v, got %v",
+ desc.Name, raw.off, narg, len(raw.args))
+ return
}
switch raw.typ {
case EvBatch:
lastGs[lastP] = lastG
lastP = int(raw.args[0])
lastG = lastGs[lastP]
- lastSeq = int64(raw.args[1])
- lastTs = int64(raw.args[2])
+ if ver < 1007 {
+ lastSeq = int64(raw.args[1])
+ lastTs = int64(raw.args[2])
+ } else {
+ lastTs = int64(raw.args[1])
+ }
case EvFrequency:
ticksPerSec = int64(raw.args[0])
if ticksPerSec <= 0 {
@@ -211,33 +303,53 @@ func parseEvents(rawEvents []rawEvent) (events []*Event, err error) {
raw.off, size)
return
}
- if uint64(len(raw.args)) != size+2 {
+ want := 2 + 4*size
+ if ver < 1007 {
+ want = 2 + size
+ }
+ if uint64(len(raw.args)) != want {
err = fmt.Errorf("EvStack has wrong number of arguments at offset 0x%x: want %v, got %v",
- raw.off, size+2, len(raw.args))
+ raw.off, want, len(raw.args))
return
}
id := raw.args[0]
if id != 0 && size > 0 {
stk := make([]*Frame, size)
for i := 0; i < int(size); i++ {
- stk[i] = &Frame{PC: raw.args[i+2]}
+ if ver < 1007 {
+ stk[i] = &Frame{PC: raw.args[2+i]}
+ } else {
+ pc := raw.args[2+i*4+0]
+ fn := raw.args[2+i*4+1]
+ file := raw.args[2+i*4+2]
+ line := raw.args[2+i*4+3]
+ stk[i] = &Frame{PC: pc, Fn: strings[fn], File: strings[file], Line: int(line)}
+ }
}
stacks[id] = stk
}
default:
e := &Event{Off: raw.off, Type: raw.typ, P: lastP, G: lastG}
- e.Seq = lastSeq + int64(raw.args[0])
- e.Ts = lastTs + int64(raw.args[1])
- lastSeq = e.Seq
- lastTs = e.Ts
- for i := range desc.Args {
- e.Args[i] = raw.args[i+2]
+ var argOffset int
+ if ver < 1007 {
+ e.seq = lastSeq + int64(raw.args[0])
+ e.Ts = lastTs + int64(raw.args[1])
+ lastSeq = e.seq
+ argOffset = 2
+ } else {
+ e.Ts = lastTs + int64(raw.args[0])
+ argOffset = 1
}
- if desc.Stack {
- e.StkID = raw.args[len(desc.Args)+2]
+ lastTs = e.Ts
+ for i := argOffset; i < narg; i++ {
+ if i == narg-1 && desc.Stack {
+ e.StkID = raw.args[i]
+ } else {
+ e.Args[i-argOffset] = raw.args[i]
+ }
}
switch raw.typ {
- case EvGoStart:
+ case EvGoStart, EvGoStartLocal:
lastG = e.Args[0]
e.G = lastG
case EvGCStart, EvGCDone, EvGCScanStart, EvGCScanDone:
@@ -247,45 +359,51 @@ func parseEvents(rawEvents []rawEvent) (events []*Event, err error) {
EvGoBlockSelect, EvGoBlockSync, EvGoBlockCond, EvGoBlockNet,
EvGoSysBlock:
lastG = 0
- case EvGoSysExit:
- // EvGoSysExit emission is delayed until the thread has a P.
- // Give it the real sequence number and time stamp.
- e.Seq = int64(e.Args[1])
- if e.Args[2] != 0 {
- e.Ts = int64(e.Args[2])
- }
+ case EvGoSysExit, EvGoWaiting, EvGoInSyscall:
+ e.G = e.Args[0]
}
- events = append(events, e)
+ batches[lastP] = append(batches[lastP], e)
}
}
- if len(events) == 0 {
+ if len(batches) == 0 {
err = fmt.Errorf("trace is empty")
return
}
-
- // Attach stack traces.
- for _, ev := range events {
- if ev.StkID != 0 {
- ev.Stk = stacks[ev.StkID]
- }
- }
-
- // Sort by sequence number and translate cpu ticks to real time.
- sort.Sort(eventList(events))
if ticksPerSec == 0 {
err = fmt.Errorf("no EvFrequency event")
return
}
+ if BreakTimestampsForTesting {
+ var batchArr [][]*Event
+ for _, batch := range batches {
+ batchArr = append(batchArr, batch)
+ }
+ for i := 0; i < 5; i++ {
+ batch := batchArr[rand.Intn(len(batchArr))]
+ batch[rand.Intn(len(batch))].Ts += int64(rand.Intn(2000) - 1000)
+ }
+ }
+ if ver < 1007 {
+ events, err = order1005(batches)
+ } else {
+ events, err = order1007(batches)
+ }
+ if err != nil {
+ return
+ }
+
+ // Translate cpu ticks to real time.
minTs := events[0].Ts
+ // Use floating point to avoid integer overflows.
+ freq := 1e9 / float64(ticksPerSec)
for _, ev := range events {
- ev.Ts = (ev.Ts - minTs) * 1e9 / ticksPerSec
+ ev.Ts = int64(float64(ev.Ts-minTs) * freq)
// Move timers and syscalls to separate fake Ps.
if timerGoid != 0 && ev.G == timerGoid && ev.Type == EvGoUnblock {
ev.P = TimerP
}
if ev.Type == EvGoSysExit {
ev.P = SyscallP
- ev.G = ev.Args[0]
}
}
@@ -355,7 +473,7 @@ var ErrTimeOrder = fmt.Errorf("time stamps out of order")
// The resulting trace is guaranteed to be consistent
// (for example, a P does not run two Gs at the same time, or a G is indeed
// blocked before an unblock event).
-func postProcessTrace(events []*Event) error {
+func postProcessTrace(ver int, events []*Event) error {
const (
gDead = iota
gRunnable
@@ -446,19 +564,15 @@ func postProcessTrace(events []*Event) error {
p.evSweep.Link = ev
p.evSweep = nil
case EvGoWaiting:
- g1 := gs[ev.Args[0]]
- if g1.state != gRunnable {
- return fmt.Errorf("g %v is not runnable before EvGoWaiting (offset %v, time %v)", ev.Args[0], ev.Off, ev.Ts)
+ if g.state != gRunnable {
+ return fmt.Errorf("g %v is not runnable before EvGoWaiting (offset %v, time %v)", ev.G, ev.Off, ev.Ts)
}
- g1.state = gWaiting
- gs[ev.Args[0]] = g1
+ g.state = gWaiting
case EvGoInSyscall:
- g1 := gs[ev.Args[0]]
- if g1.state != gRunnable {
- return fmt.Errorf("g %v is not runnable before EvGoInSyscall (offset %v, time %v)", ev.Args[0], ev.Off, ev.Ts)
+ if g.state != gRunnable {
+ return fmt.Errorf("g %v is not runnable before EvGoInSyscall (offset %v, time %v)", ev.G, ev.Off, ev.Ts)
}
- g1.state = gWaiting
- gs[ev.Args[0]] = g1
+ g.state = gWaiting
case EvGoCreate:
if err := checkRunning(p, g, ev, true); err != nil {
return err
@@ -478,8 +592,12 @@ func postProcessTrace(events []*Event) error {
g.evStart = ev
p.g = ev.G
if g.evCreate != nil {
- // +1 because symbolizer expects return pc.
- ev.Stk = []*Frame{{PC: g.evCreate.Args[1] + 1}}
+ if ver < 1007 {
+ // +1 because symbolizer expects return pc.
+ ev.Stk = []*Frame{{PC: g.evCreate.Args[1] + 1}}
+ } else {
+ ev.StkID = g.evCreate.Args[1]
+ }
g.evCreate = nil
}
@@ -565,23 +683,11 @@ func postProcessTrace(events []*Event) error {
// TODO(dvyukov): restore stacks for EvGoStart events.
// TODO(dvyukov): test that all EvGoStart events has non-nil Link.
- // Last, after all the other consistency checks,
- // make sure time stamps respect sequence numbers.
- // The tests will skip (not fail) the test case if they see this error,
- // so check everything else that could possibly be wrong first.
- lastTs := int64(0)
- for _, ev := range events {
- if ev.Ts < lastTs {
- return ErrTimeOrder
- }
- lastTs = ev.Ts
- }
-
return nil
}
-// symbolizeTrace attaches func/file/line info to stack traces.
-func Symbolize(events []*Event, bin string) error {
+// symbolize attaches func/file/line info to stack traces.
+func symbolize(events []*Event, bin string) error {
// First, collect and dedup all pcs.
pcs := make(map[uint64]*Frame)
for _, ev := range events {
@@ -672,57 +778,81 @@ func readVal(r io.Reader, off0 int) (v uint64, off int, err error) {
return 0, 0, fmt.Errorf("bad value at offset 0x%x", off0)
}
-type eventList []*Event
-
-func (l eventList) Len() int {
- return len(l)
-}
-
-func (l eventList) Less(i, j int) bool {
- return l[i].Seq < l[j].Seq
+// Print dumps events to stdout. For debugging.
+func Print(events []*Event) {
+ for _, ev := range events {
+ PrintEvent(ev)
+ }
}
-func (l eventList) Swap(i, j int) {
- l[i], l[j] = l[j], l[i]
+// PrintEvent dumps the event to stdout. For debugging.
+func PrintEvent(ev *Event) {
+ desc := EventDescriptions[ev.Type]
+ fmt.Printf("%v %v p=%v g=%v off=%v", ev.Ts, desc.Name, ev.P, ev.G, ev.Off)
+ for i, a := range desc.Args {
+ fmt.Printf(" %v=%v", a, ev.Args[i])
+ }
+ fmt.Printf("\n")
}
-// Print dumps events to stdout. For debugging.
-func Print(events []*Event) {
- for _, ev := range events {
- desc := EventDescriptions[ev.Type]
- fmt.Printf("%v %v p=%v g=%v off=%v", ev.Ts, desc.Name, ev.P, ev.G, ev.Off)
- for i, a := range desc.Args {
- fmt.Printf(" %v=%v", a, ev.Args[i])
+// argNum returns total number of args for the event accounting for timestamps,
+// sequence numbers and differences between trace format versions.
+func argNum(raw rawEvent, ver int) int {
+ desc := EventDescriptions[raw.typ]
+ if raw.typ == EvStack {
+ return len(raw.args)
+ }
+ narg := len(desc.Args)
+ if desc.Stack {
+ narg++
+ }
+ switch raw.typ {
+ case EvBatch, EvFrequency, EvTimerGoroutine:
+ if ver < 1007 {
+ narg++ // there was an unused arg before 1.7
+ }
+ case EvGCStart, EvGoStart, EvGoUnblock:
+ if ver < 1007 {
+ narg-- // 1.7 added an additional seq arg
+ }
+ fallthrough
+ default:
+ narg++ // timestamp
+ if ver < 1007 {
+ narg++ // sequence
}
- fmt.Printf("\n")
}
+ return narg
}
+// BreakTimestampsForTesting causes the parser to randomly alter timestamps (for testing of broken cputicks).
+var BreakTimestampsForTesting bool
+
// Event types in the trace.
// Verbatim copy from src/runtime/trace.go.
const (
EvNone = 0 // unused
EvBatch = 1 // start of per-P batch of events [pid, timestamp]
EvFrequency = 2 // contains tracer timer frequency [frequency (ticks per second)]
- EvStack = 3 // stack [stack id, number of PCs, array of PCs]
+ EvStack = 3 // stack [stack id, number of PCs, array of {PC, func string ID, file string ID, line}]
EvGomaxprocs = 4 // current value of GOMAXPROCS [timestamp, GOMAXPROCS, stack id]
EvProcStart = 5 // start of P [timestamp, thread id]
EvProcStop = 6 // stop of P [timestamp]
- EvGCStart = 7 // GC start [timestamp, stack id]
+ EvGCStart = 7 // GC start [timestamp, seq, stack id]
EvGCDone = 8 // GC done [timestamp]
EvGCScanStart = 9 // GC scan start [timestamp]
EvGCScanDone = 10 // GC scan done [timestamp]
EvGCSweepStart = 11 // GC sweep start [timestamp, stack id]
EvGCSweepDone = 12 // GC sweep done [timestamp]
- EvGoCreate = 13 // goroutine creation [timestamp, new goroutine id, start PC, stack id]
- EvGoStart = 14 // goroutine starts running [timestamp, goroutine id]
+ EvGoCreate = 13 // goroutine creation [timestamp, new goroutine id, new stack id, stack id]
+ EvGoStart = 14 // goroutine starts running [timestamp, goroutine id, seq]
EvGoEnd = 15 // goroutine ends [timestamp]
EvGoStop = 16 // goroutine stops (like in select{}) [timestamp, stack]
EvGoSched = 17 // goroutine calls Gosched [timestamp, stack]
EvGoPreempt = 18 // goroutine is preempted [timestamp, stack]
EvGoSleep = 19 // goroutine calls Sleep [timestamp, stack]
EvGoBlock = 20 // goroutine blocks [timestamp, stack]
- EvGoUnblock = 21 // goroutine is unblocked [timestamp, goroutine id, stack]
+ EvGoUnblock = 21 // goroutine is unblocked [timestamp, goroutine id, seq, stack]
EvGoBlockSend = 22 // goroutine blocks on chan send [timestamp, stack]
EvGoBlockRecv = 23 // goroutine blocks on chan recv [timestamp, stack]
EvGoBlockSelect = 24 // goroutine blocks on select [timestamp, stack]
@@ -730,57 +860,66 @@ const (
EvGoBlockCond = 26 // goroutine blocks on Cond [timestamp, stack]
EvGoBlockNet = 27 // goroutine blocks on network [timestamp, stack]
EvGoSysCall = 28 // syscall enter [timestamp, stack]
- EvGoSysExit = 29 // syscall exit [timestamp, goroutine id, real timestamp]
+ EvGoSysExit = 29 // syscall exit [timestamp, goroutine id, seq, real timestamp]
EvGoSysBlock = 30 // syscall blocks [timestamp]
- EvGoWaiting = 31 // denotes that goroutine is blocked when tracing starts [goroutine id]
- EvGoInSyscall = 32 // denotes that goroutine is in syscall when tracing starts [goroutine id]
- EvHeapAlloc = 33 // memstats.heap_alloc change [timestamp, heap_alloc]
+ EvGoWaiting = 31 // denotes that goroutine is blocked when tracing starts [timestamp, goroutine id]
+ EvGoInSyscall = 32 // denotes that goroutine is in syscall when tracing starts [timestamp, goroutine id]
+ EvHeapAlloc = 33 // memstats.heap_live change [timestamp, heap_alloc]
EvNextGC = 34 // memstats.next_gc change [timestamp, next_gc]
EvTimerGoroutine = 35 // denotes timer goroutine [timer goroutine id]
EvFutileWakeup = 36 // denotes that the previous wakeup of this goroutine was futile [timestamp]
- EvCount = 37
+ EvString = 37 // string dictionary entry [ID, length, string]
+ EvGoStartLocal = 38 // goroutine starts running on the same P as the last event [timestamp, goroutine id]
+ EvGoUnblockLocal = 39 // goroutine is unblocked on the same P as the last event [timestamp, goroutine id, stack]
+ EvGoSysExitLocal = 40 // syscall exit on the same P as the last event [timestamp, goroutine id, real timestamp]
+ EvCount = 41
)
var EventDescriptions = [EvCount]struct {
- Name string
- Stack bool
- Args []string
+ Name string
+ minVersion int
+ Stack bool
+ Args []string
}{
- EvNone: {"None", false, []string{}},
- EvBatch: {"Batch", false, []string{"p", "seq", "ticks"}},
- EvFrequency: {"Frequency", false, []string{"freq", "unused"}},
- EvStack: {"Stack", false, []string{"id", "siz"}},
- EvGomaxprocs: {"Gomaxprocs", true, []string{"procs"}},
- EvProcStart: {"ProcStart", false, []string{"thread"}},
- EvProcStop: {"ProcStop", false, []string{}},
- EvGCStart: {"GCStart", true, []string{}},
- EvGCDone: {"GCDone", false, []string{}},
- EvGCScanStart: {"GCScanStart", false, []string{}},
- EvGCScanDone: {"GCScanDone", false, []string{}},
- EvGCSweepStart: {"GCSweepStart", true, []string{}},
- EvGCSweepDone: {"GCSweepDone", false, []string{}},
- EvGoCreate: {"GoCreate", true, []string{"g", "pc"}},
- EvGoStart: {"GoStart", false, []string{"g"}},
- EvGoEnd: {"GoEnd", false, []string{}},
- EvGoStop: {"GoStop", true, []string{}},
- EvGoSched: {"GoSched", true, []string{}},
- EvGoPreempt: {"GoPreempt", true, []string{}},
- EvGoSleep: {"GoSleep", true, []string{}},
- EvGoBlock: {"GoBlock", true, []string{}},
- EvGoUnblock: {"GoUnblock", true, []string{"g"}},
- EvGoBlockSend: {"GoBlockSend", true, []string{}},
- EvGoBlockRecv: {"GoBlockRecv", true, []string{}},
- EvGoBlockSelect: {"GoBlockSelect", true, []string{}},
- EvGoBlockSync: {"GoBlockSync", true, []string{}},
- EvGoBlockCond: {"GoBlockCond", true, []string{}},
- EvGoBlockNet: {"GoBlockNet", true, []string{}},
- EvGoSysCall: {"GoSysCall", true, []string{}},
- EvGoSysExit: {"GoSysExit", false, []string{"g", "seq", "ts"}},
- EvGoSysBlock: {"GoSysBlock", false, []string{}},
- EvGoWaiting: {"GoWaiting", false, []string{"g"}},
- EvGoInSyscall: {"GoInSyscall", false, []string{"g"}},
- EvHeapAlloc: {"HeapAlloc", false, []string{"mem"}},
- EvNextGC: {"NextGC", false, []string{"mem"}},
- EvTimerGoroutine: {"TimerGoroutine", false, []string{"g", "unused"}},
- EvFutileWakeup: {"FutileWakeup", false, []string{}},
+ EvNone: {"None", 1005, false, []string{}},
+ EvBatch: {"Batch", 1005, false, []string{"p", "ticks"}}, // in 1.5 format it was {"p", "seq", "ticks"}
+ EvFrequency: {"Frequency", 1005, false, []string{"freq"}}, // in 1.5 format it was {"freq", "unused"}
+ EvStack: {"Stack", 1005, false, []string{"id", "siz"}},
+ EvGomaxprocs: {"Gomaxprocs", 1005, true, []string{"procs"}},
+ EvProcStart: {"ProcStart", 1005, false, []string{"thread"}},
+ EvProcStop: {"ProcStop", 1005, false, []string{}},
+ EvGCStart: {"GCStart", 1005, true, []string{"seq"}}, // in 1.5 format it was {}
+ EvGCDone: {"GCDone", 1005, false, []string{}},
+ EvGCScanStart: {"GCScanStart", 1005, false, []string{}},
+ EvGCScanDone: {"GCScanDone", 1005, false, []string{}},
+ EvGCSweepStart: {"GCSweepStart", 1005, true, []string{}},
+ EvGCSweepDone: {"GCSweepDone", 1005, false, []string{}},
+ EvGoCreate: {"GoCreate", 1005, true, []string{"g", "stack"}},
+ EvGoStart: {"GoStart", 1005, false, []string{"g", "seq"}}, // in 1.5 format it was {"g"}
+ EvGoEnd: {"GoEnd", 1005, false, []string{}},
+ EvGoStop: {"GoStop", 1005, true, []string{}},
+ EvGoSched: {"GoSched", 1005, true, []string{}},
+ EvGoPreempt: {"GoPreempt", 1005, true, []string{}},
+ EvGoSleep: {"GoSleep", 1005, true, []string{}},
+ EvGoBlock: {"GoBlock", 1005, true, []string{}},
+ EvGoUnblock: {"GoUnblock", 1005, true, []string{"g", "seq"}}, // in 1.5 format it was {"g"}
+ EvGoBlockSend: {"GoBlockSend", 1005, true, []string{}},
+ EvGoBlockRecv: {"GoBlockRecv", 1005, true, []string{}},
+ EvGoBlockSelect: {"GoBlockSelect", 1005, true, []string{}},
+ EvGoBlockSync: {"GoBlockSync", 1005, true, []string{}},
+ EvGoBlockCond: {"GoBlockCond", 1005, true, []string{}},
+ EvGoBlockNet: {"GoBlockNet", 1005, true, []string{}},
+ EvGoSysCall: {"GoSysCall", 1005, true, []string{}},
+ EvGoSysExit: {"GoSysExit", 1005, false, []string{"g", "seq", "ts"}},
+ EvGoSysBlock: {"GoSysBlock", 1005, false, []string{}},
+ EvGoWaiting: {"GoWaiting", 1005, false, []string{"g"}},
+ EvGoInSyscall: {"GoInSyscall", 1005, false, []string{"g"}},
+ EvHeapAlloc: {"HeapAlloc", 1005, false, []string{"mem"}},
+ EvNextGC: {"NextGC", 1005, false, []string{"mem"}},
+ EvTimerGoroutine: {"TimerGoroutine", 1005, false, []string{"g"}}, // in 1.5 format it was {"g", "unused"}
+ EvFutileWakeup: {"FutileWakeup", 1005, false, []string{}},
+ EvString: {"String", 1007, false, []string{}},
+ EvGoStartLocal: {"GoStartLocal", 1007, false, []string{"g"}},
+ EvGoUnblockLocal: {"GoUnblockLocal", 1007, true, []string{"g"}},
+ EvGoSysExitLocal: {"GoSysExitLocal", 1007, false, []string{"g", "ts"}},
}
diff --git a/src/internal/trace/parser_test.go b/src/internal/trace/parser_test.go
index fecefc4053..340f106484 100644
--- a/src/internal/trace/parser_test.go
+++ b/src/internal/trace/parser_test.go
@@ -5,6 +5,9 @@
package trace
import (
+ "bytes"
+ "io/ioutil"
+ "path/filepath"
"strings"
"testing"
)
@@ -22,9 +25,115 @@ func TestCorruptedInputs(t *testing.T) {
"go 1.5 trace\x00\x00\x00\x00\xc3\x0200",
}
for _, data := range tests {
- events, err := Parse(strings.NewReader(data))
+ events, err := Parse(strings.NewReader(data), "")
if err == nil || events != nil {
- t.Fatalf("no error on input: %q\n", data)
+ t.Fatalf("no error on input: %q", data)
}
}
}
+
+func TestParseCanned(t *testing.T) {
+ files, err := ioutil.ReadDir("./testdata")
+ if err != nil {
+ t.Fatalf("failed to read ./testdata: %v", err)
+ }
+ for _, f := range files {
+ data, err := ioutil.ReadFile(filepath.Join("./testdata", f.Name()))
+ if err != nil {
+ t.Fatalf("failed to read input file: %v", err)
+ }
+ _, err = Parse(bytes.NewReader(data), "")
+ switch {
+ case strings.HasSuffix(f.Name(), "_good"):
+ if err != nil {
+ t.Errorf("failed to parse good trace %v: %v", f.Name(), err)
+ }
+ case strings.HasSuffix(f.Name(), "_unordered"):
+ if err != ErrTimeOrder {
+ t.Errorf("unordered trace is not detected %v: %v", f.Name(), err)
+ }
+ default:
+ t.Errorf("unknown input file suffix: %v", f.Name())
+ }
+ }
+}
+
+func TestParseVersion(t *testing.T) {
+ tests := map[string]int{
+ "go 1.5 trace\x00\x00\x00\x00": 1005,
+ "go 1.7 trace\x00\x00\x00\x00": 1007,
+ "go 1.10 trace\x00\x00\x00": 1010,
+ "go 1.25 trace\x00\x00\x00": 1025,
+ "go 1.234 trace\x00\x00": 1234,
+ "go 1.2345 trace\x00": -1,
+ "go 0.0 trace\x00\x00\x00\x00": -1,
+ "go a.b trace\x00\x00\x00\x00": -1,
+ }
+ for header, ver := range tests {
+ ver1, err := parseHeader([]byte(header))
+ if ver == -1 {
+ if err == nil {
+ t.Fatalf("no error on input: %q, version %v", header, ver1)
+ }
+ } else {
+ if err != nil {
+ t.Fatalf("failed to parse: %q (%v)", header, err)
+ }
+ if ver != ver1 {
+ t.Fatalf("wrong version: %v, want %v, input: %q", ver1, ver, header)
+ }
+ }
+ }
+}
+
+func TestTimestampOverflow(t *testing.T) {
+ // Test that parser correctly handles large timestamps (long tracing).
+ w := newWriter()
+ w.emit(EvBatch, 0, 0)
+ w.emit(EvFrequency, 1e9)
+ for ts := uint64(1); ts < 1e16; ts *= 2 {
+ w.emit(EvGoCreate, ts, ts, 0, 0)
+ }
+ if _, err := Parse(w, ""); err != nil {
+ t.Fatalf("failed to parse: %v", err)
+ }
+}
+
+type writer struct {
+ bytes.Buffer
+}
+
+func newWriter() *writer {
+ w := new(writer)
+ w.Write([]byte("go 1.7 trace\x00\x00\x00\x00"))
+ return w
+}
+
+func (w *writer) emit(typ byte, args ...uint64) {
+ nargs := byte(len(args)) - 1
+ if nargs > 3 {
+ nargs = 3
+ }
+ buf := []byte{typ | nargs<<6}
+ if nargs == 3 {
+ buf = append(buf, 0)
+ }
+ for _, a := range args {
+ buf = appendVarint(buf, a)
+ }
+ if nargs == 3 {
+ buf[1] = byte(len(buf) - 2)
+ }
+ n, err := w.Write(buf)
+ if n != len(buf) || err != nil {
+ panic("failed to write")
+ }
+}
+
+func appendVarint(buf []byte, v uint64) []byte {
+ for ; v >= 0x80; v >>= 7 {
+ buf = append(buf, 0x80|byte(v))
+ }
+ buf = append(buf, byte(v))
+ return buf
+}
diff --git a/src/internal/trace/testdata/http_1_5_good b/src/internal/trace/testdata/http_1_5_good
new file mode 100644
index 0000000000..0736cae674
--- /dev/null
+++ b/src/internal/trace/testdata/http_1_5_good
Binary files differ
diff --git a/src/internal/trace/testdata/stress_1_5_good b/src/internal/trace/testdata/stress_1_5_good
new file mode 100644
index 0000000000..c5055ebd19
--- /dev/null
+++ b/src/internal/trace/testdata/stress_1_5_good
Binary files differ
diff --git a/src/internal/trace/testdata/stress_1_5_unordered b/src/internal/trace/testdata/stress_1_5_unordered
new file mode 100644
index 0000000000..11f7d745ca
--- /dev/null
+++ b/src/internal/trace/testdata/stress_1_5_unordered
Binary files differ
diff --git a/src/internal/trace/testdata/stress_start_stop_1_5_good b/src/internal/trace/testdata/stress_start_stop_1_5_good
new file mode 100644
index 0000000000..72a887b844
--- /dev/null
+++ b/src/internal/trace/testdata/stress_start_stop_1_5_good
Binary files differ
diff --git a/src/io/io.go b/src/io/io.go
index 6e33192052..c36ec2afbb 100644
--- a/src/io/io.go
+++ b/src/io/io.go
@@ -274,6 +274,16 @@ type RuneScanner interface {
UnreadRune() error
}
+// SizedReaderAt is the interface that groups the basic ReadAt method
+// with a Size method that reports the total size of the underlying
+// object. It represents a fixed-size data source that supports random
+// access by multiple concurrent goroutines.
+type SizedReaderAt interface {
+ ReaderAt
+ // Size reports the length of the data source in bytes.
+ Size() int64
+}
+
// stringWriter is the interface that wraps the WriteString method.
type stringWriter interface {
WriteString(s string) (n int, err error)
@@ -281,6 +291,7 @@ type stringWriter interface {
// WriteString writes the contents of the string s to w, which accepts a slice of bytes.
// If w implements a WriteString method, it is invoked directly.
+// Otherwise, w.Write is called exactly once.
func WriteString(w Writer, s string) (n int, err error) {
if sw, ok := w.(stringWriter); ok {
return sw.WriteString(s)
diff --git a/src/math/big/arith_s390x.s b/src/math/big/arith_s390x.s
new file mode 100644
index 0000000000..a691970810
--- /dev/null
+++ b/src/math/big/arith_s390x.s
@@ -0,0 +1,565 @@
+// Copyright 2016 The Go 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 !math_big_pure_go,s390x
+
+#include "textflag.h"
+
+// This file provides fast assembly versions for the elementary
+// arithmetic operations on vectors implemented in arith.go.
+
+TEXT ·mulWW(SB),NOSPLIT,$0
+ MOVD x+0(FP), R3
+ MOVD y+8(FP), R4
+ MULHDU R3, R4
+ MOVD R10, z1+16(FP)
+ MOVD R11, z0+24(FP)
+ RET
+
+// func divWW(x1, x0, y Word) (q, r Word)
+TEXT ·divWW(SB),NOSPLIT,$0
+ MOVD x1+0(FP), R10
+ MOVD x0+8(FP), R11
+ MOVD y+16(FP), R5
+ WORD $0xb98700a5 // dlgr r10,r5
+ MOVD R11, q+24(FP)
+ MOVD R10, r+32(FP)
+ RET
+
+// DI = R3, CX = R4, SI = r10, r8 = r8, r9=r9, r10 = r2 , r11 = r5, r12 = r6, r13 = r7, r14 = r1 (R0 set to 0) + use R11
+// func addVV(z, x, y []Word) (c Word)
+TEXT ·addVV(SB),NOSPLIT,$0
+ MOVD z_len+8(FP), R3
+ MOVD x+24(FP), R8
+ MOVD y+48(FP), R9
+ MOVD z+0(FP), R2
+
+ MOVD $0, R4 // c = 0
+ MOVD $0, R0 // make sure it's zero
+ MOVD $0, R10 // i = 0
+
+ // s/JL/JMP/ below to disable the unrolled loop
+ SUB $4, R3 // n -= 4
+ BLT v1 // if n < 0 goto v1
+
+U1: // n >= 0
+ // regular loop body unrolled 4x
+ MOVD 0(R8)(R10*1), R5
+ MOVD 8(R8)(R10*1), R6
+ MOVD 16(R8)(R10*1), R7
+ MOVD 24(R8)(R10*1), R1
+ ADDC R4, R4 // restore CF
+ MOVD 0(R9)(R10*1), R11
+ ADDE R11, R5
+ MOVD 8(R9)(R10*1), R11
+ ADDE R11, R6
+ MOVD 16(R9)(R10*1), R11
+ ADDE R11, R7
+ MOVD 24(R9)(R10*1), R11
+ ADDE R11, R1
+ MOVD R0, R4
+ ADDE R4, R4 // save CF
+ NEG R4, R4
+ MOVD R5, 0(R2)(R10*1)
+ MOVD R6, 8(R2)(R10*1)
+ MOVD R7, 16(R2)(R10*1)
+ MOVD R1, 24(R2)(R10*1)
+
+
+ ADD $32, R10 // i += 4
+ SUB $4, R3 // n -= 4
+ BGE U1 // if n >= 0 goto U1
+
+v1: ADD $4, R3 // n += 4
+ BLE E1 // if n <= 0 goto E1
+
+L1: // n > 0
+ ADDC R4, R4 // restore CF
+ MOVD 0(R8)(R10*1), R5
+ MOVD 0(R9)(R10*1), R11
+ ADDE R11, R5
+ MOVD R5, 0(R2)(R10*1)
+ MOVD R0, R4
+ ADDE R4, R4 // save CF
+ NEG R4, R4
+
+ ADD $8, R10 // i++
+ SUB $1, R3 // n--
+ BGT L1 // if n > 0 goto L1
+
+E1: NEG R4, R4
+ MOVD R4, c+72(FP) // return c
+ RET
+
+// DI = R3, CX = R4, SI = r10, r8 = r8, r9=r9, r10 = r2 , r11 = r5, r12 = r6, r13 = r7, r14 = r1 (R0 set to 0) + use R11
+// func subVV(z, x, y []Word) (c Word)
+// (same as addVV except for SUBC/SUBE instead of ADDC/ADDE and label names)
+TEXT ·subVV(SB),NOSPLIT,$0
+ MOVD z_len+8(FP), R3
+ MOVD x+24(FP), R8
+ MOVD y+48(FP), R9
+ MOVD z+0(FP), R2
+
+ MOVD $0, R4 // c = 0
+ MOVD $0, R0 // make sure it's zero
+ MOVD $0, R10 // i = 0
+
+ // s/JL/JMP/ below to disable the unrolled loop
+ SUB $4, R3 // n -= 4
+ BLT v1 // if n < 0 goto v1
+
+U1: // n >= 0
+ // regular loop body unrolled 4x
+ MOVD 0(R8)(R10*1), R5
+ MOVD 8(R8)(R10*1), R6
+ MOVD 16(R8)(R10*1), R7
+ MOVD 24(R8)(R10*1), R1
+ MOVD R0, R11
+ SUBC R4, R11 // restore CF
+ MOVD 0(R9)(R10*1), R11
+ SUBE R11, R5
+ MOVD 8(R9)(R10*1), R11
+ SUBE R11, R6
+ MOVD 16(R9)(R10*1), R11
+ SUBE R11, R7
+ MOVD 24(R9)(R10*1), R11
+ SUBE R11, R1
+ MOVD R0, R4
+ SUBE R4, R4 // save CF
+ MOVD R5, 0(R2)(R10*1)
+ MOVD R6, 8(R2)(R10*1)
+ MOVD R7, 16(R2)(R10*1)
+ MOVD R1, 24(R2)(R10*1)
+
+
+ ADD $32, R10 // i += 4
+ SUB $4, R3 // n -= 4
+ BGE U1 // if n >= 0 goto U1
+
+v1: ADD $4, R3 // n += 4
+ BLE E1 // if n <= 0 goto E1
+
+L1: // n > 0
+ MOVD R0, R11
+ SUBC R4, R11 // restore CF
+ MOVD 0(R8)(R10*1), R5
+ MOVD 0(R9)(R10*1), R11
+ SUBE R11, R5
+ MOVD R5, 0(R2)(R10*1)
+ MOVD R0, R4
+ SUBE R4, R4 // save CF
+
+ ADD $8, R10 // i++
+ SUB $1, R3 // n--
+ BGT L1 // if n > 0 goto L1
+
+E1: NEG R4, R4
+ MOVD R4, c+72(FP) // return c
+ RET
+
+
+// func addVW(z, x []Word, y Word) (c Word)
+TEXT ·addVW(SB),NOSPLIT,$0
+//DI = R3, CX = R4, SI = r10, r8 = r8, r10 = r2 , r11 = r5, r12 = r6, r13 = r7, r14 = r1 (R0 set to 0)
+ MOVD z_len+8(FP), R3
+ MOVD x+24(FP), R8
+ MOVD y+48(FP), R4 // c = y
+ MOVD z+0(FP), R2
+ MOVD $0, R0 // make sure it's 0
+ MOVD $0, R10 // i = 0
+
+ // s/JL/JMP/ below to disable the unrolled loop
+ SUB $4, R3 // n -= 4
+ BLT v4 // if n < 4 goto v4
+
+U4: // n >= 0
+ // regular loop body unrolled 4x
+ MOVD 0(R8)(R10*1), R5
+ MOVD 8(R8)(R10*1), R6
+ MOVD 16(R8)(R10*1), R7
+ MOVD 24(R8)(R10*1), R1
+ ADDC R4, R5
+ ADDE R0, R6
+ ADDE R0, R7
+ ADDE R0, R1
+ ADDE R0, R0
+ MOVD R0, R4 // save CF
+ SUB R0, R0
+ MOVD R5, 0(R2)(R10*1)
+ MOVD R6, 8(R2)(R10*1)
+ MOVD R7, 16(R2)(R10*1)
+ MOVD R1, 24(R2)(R10*1)
+
+ ADD $32, R10 // i += 4 -> i +=32
+ SUB $4, R3 // n -= 4
+ BGE U4 // if n >= 0 goto U4
+
+v4: ADD $4, R3 // n += 4
+ BLE E4 // if n <= 0 goto E4
+
+L4: // n > 0
+ MOVD 0(R8)(R10*1), R5
+ ADDC R4, R5
+ ADDE R0, R0
+ MOVD R0, R4 // save CF
+ SUB R0, R0
+ MOVD R5, 0(R2)(R10*1)
+
+ ADD $8, R10 // i++
+ SUB $1, R3 // n--
+ BGT L4 // if n > 0 goto L4
+
+E4: MOVD R4, c+56(FP) // return c
+
+ RET
+
+//DI = R3, CX = R4, SI = r10, r8 = r8, r10 = r2 , r11 = r5, r12 = r6, r13 = r7, r14 = r1 (R0 set to 0)
+// func subVW(z, x []Word, y Word) (c Word)
+// (same as addVW except for SUBC/SUBE instead of ADDC/ADDE and label names)
+TEXT ·subVW(SB),NOSPLIT,$0
+ MOVD z_len+8(FP), R3
+ MOVD x+24(FP), R8
+ MOVD y+48(FP), R4 // c = y
+ MOVD z+0(FP), R2
+ MOVD $0, R0 // make sure it's 0
+ MOVD $0, R10 // i = 0
+
+ // s/JL/JMP/ below to disable the unrolled loop
+ SUB $4, R3 // n -= 4
+ BLT v4 // if n < 4 goto v4
+
+U4: // n >= 0
+ // regular loop body unrolled 4x
+ MOVD 0(R8)(R10*1), R5
+ MOVD 8(R8)(R10*1), R6
+ MOVD 16(R8)(R10*1), R7
+ MOVD 24(R8)(R10*1), R1
+ SUBC R4, R5 //SLGR -> SUBC
+ SUBE R0, R6 //SLBGR -> SUBE
+ SUBE R0, R7
+ SUBE R0, R1
+ SUBE R4, R4 // save CF
+ NEG R4, R4
+ MOVD R5, 0(R2)(R10*1)
+ MOVD R6, 8(R2)(R10*1)
+ MOVD R7, 16(R2)(R10*1)
+ MOVD R1, 24(R2)(R10*1)
+
+ ADD $32, R10 // i += 4 -> i +=32
+ SUB $4, R3 // n -= 4
+ BGE U4 // if n >= 0 goto U4
+
+v4: ADD $4, R3 // n += 4
+ BLE E4 // if n <= 0 goto E4
+
+L4: // n > 0
+ MOVD 0(R8)(R10*1), R5
+ SUBC R4, R5
+ SUBE R4, R4 // save CF
+ NEG R4, R4
+ MOVD R5, 0(R2)(R10*1)
+
+ ADD $8, R10 // i++
+ SUB $1, R3 // n--
+ BGT L4 // if n > 0 goto L4
+
+E4: MOVD R4, c+56(FP) // return c
+
+ RET
+
+// func shlVU(z, x []Word, s uint) (c Word)
+TEXT ·shlVU(SB),NOSPLIT,$0
+ MOVD z_len+8(FP), R5
+ SUB $1, R5 // n--
+ BLT X8b // n < 0 (n <= 0)
+
+ // n > 0
+ MOVD s+48(FP), R4
+ CMPBEQ R0, R4, Z80 //handle 0 case beq
+ MOVD $64, R6
+ CMPBEQ R6, R4, Z864 //handle 64 case beq
+ MOVD z+0(FP), R2
+ MOVD x+24(FP), R8
+ SLD $3, R5 // n = n*8
+ SUB R4, R6, R7
+ MOVD (R8)(R5*1), R10 // w1 = x[i-1]
+ SRD R7, R10, R3
+ MOVD R3, c+56(FP)
+
+ MOVD $0, R1 // i = 0
+ BR E8
+
+ // i < n-1
+L8: MOVD R10, R3 // w = w1
+ MOVD -8(R8)(R5*1), R10 // w1 = x[i+1]
+
+ SLD R4, R3 // w<<s | w1>>ŝ
+ SRD R7, R10, R6
+ OR R6, R3
+ MOVD R3, (R2)(R5*1) // z[i] = w<<s | w1>>ŝ
+ SUB $8, R5 // i--
+
+E8: CMPBGT R5, R0, L8 // i < n-1
+
+ // i >= n-1
+X8a: SLD R4, R10 // w1<<s
+ MOVD R10, (R2) // z[0] = w1<<s
+ RET
+
+X8b: MOVD R0, c+56(FP)
+ RET
+
+Z80: MOVD z+0(FP), R2
+ MOVD x+24(FP), R8
+ SLD $3, R5 // n = n*8
+
+ MOVD (R8), R10
+ MOVD $0, R3
+ MOVD R3, c+56(FP)
+
+ MOVD $0, R1 // i = 0
+ BR E8Z
+
+ // i < n-1
+L8Z: MOVD R10, R3
+ MOVD 8(R8)(R1*1), R10
+
+ MOVD R3, (R2)(R1*1)
+ ADD $8, R1
+
+E8Z: CMPBLT R1, R5, L8Z
+
+ // i >= n-1
+ MOVD R10, (R2)(R5*1)
+ RET
+
+Z864: MOVD z+0(FP), R2
+ MOVD x+24(FP), R8
+ SLD $3, R5 // n = n*8
+ MOVD (R8)(R5*1), R3 // w1 = x[n-1]
+ MOVD R3, c+56(FP) // z[i] = x[n-1]
+
+ BR E864
+
+ // i < n-1
+L864: MOVD -8(R8)(R5*1), R3
+
+ MOVD R3, (R2)(R5*1) // z[i] = x[n-1]
+ SUB $8, R5 // i--
+
+E864: CMPBGT R5, R0, L864 // i < n-1
+
+ MOVD R0, (R2) // z[n-1] = 0
+ RET
+
+
+// CX = R4, r8 = r8, r10 = r2 , r11 = r5, DX = r3, AX = r10 , BX = R1 , 64-count = r7 (R0 set to 0) temp = R6
+// func shrVU(z, x []Word, s uint) (c Word)
+TEXT ·shrVU(SB),NOSPLIT,$0
+ MOVD z_len+8(FP), R5
+ SUB $1, R5 // n--
+ BLT X9b // n < 0 (n <= 0)
+
+ // n > 0
+ MOVD s+48(FP), R4
+ CMPBEQ R0, R4, ZB0 //handle 0 case beq
+ MOVD $64, R6
+ CMPBEQ R6, R4, ZB64 //handle 64 case beq
+ MOVD z+0(FP), R2
+ MOVD x+24(FP), R8
+ SLD $3, R5 // n = n*8
+ SUB R4, R6, R7
+ MOVD (R8), R10 // w1 = x[0]
+ SLD R7, R10, R3
+ MOVD R3, c+56(FP)
+
+ MOVD $0, R1 // i = 0
+ BR E9
+
+ // i < n-1
+L9: MOVD R10, R3 // w = w1
+ MOVD 8(R8)(R1*1), R10 // w1 = x[i+1]
+
+ SRD R4, R3 // w>>s | w1<<s
+ SLD R7, R10, R6
+ OR R6, R3
+ MOVD R3, (R2)(R1*1) // z[i] = w>>s | w1<<s
+ ADD $8, R1 // i++
+
+E9: CMPBLT R1, R5, L9 // i < n-1
+
+ // i >= n-1
+X9a: SRD R4, R10 // w1>>s
+ MOVD R10, (R2)(R5*1) // z[n-1] = w1>>s
+ RET
+
+X9b: MOVD R0, c+56(FP)
+ RET
+
+ZB0: MOVD z+0(FP), R2
+ MOVD x+24(FP), R8
+ SLD $3, R5 // n = n*8
+
+ MOVD (R8), R10 // w1 = x[0]
+ MOVD $0, R3 // R10 << 64
+ MOVD R3, c+56(FP)
+
+ MOVD $0, R1 // i = 0
+ BR E9Z
+
+ // i < n-1
+L9Z: MOVD R10, R3 // w = w1
+ MOVD 8(R8)(R1*1), R10 // w1 = x[i+1]
+
+ MOVD R3, (R2)(R1*1) // z[i] = w>>s | w1<<s
+ ADD $8, R1 // i++
+
+E9Z: CMPBLT R1, R5, L9Z // i < n-1
+
+ // i >= n-1
+ MOVD R10, (R2)(R5*1) // z[n-1] = w1>>s
+ RET
+
+ZB64: MOVD z+0(FP), R2
+ MOVD x+24(FP), R8
+ SLD $3, R5 // n = n*8
+ MOVD (R8), R3 // w1 = x[0]
+ MOVD R3, c+56(FP)
+
+ MOVD $0, R1 // i = 0
+ BR E964
+
+ // i < n-1
+L964: MOVD 8(R8)(R1*1), R3 // w1 = x[i+1]
+
+ MOVD R3, (R2)(R1*1) // z[i] = w>>s | w1<<s
+ ADD $8, R1 // i++
+
+E964: CMPBLT R1, R5, L964 // i < n-1
+
+ // i >= n-1
+ MOVD $0, R10 // w1>>s
+ MOVD R10, (R2)(R5*1) // z[n-1] = w1>>s
+ RET
+
+// CX = R4, r8 = r8, r9=r9, r10 = r2 , r11 = r5, DX = r3, AX = r6 , BX = R1 , (R0 set to 0) + use R11 + use R7 for i
+// func mulAddVWW(z, x []Word, y, r Word) (c Word)
+TEXT ·mulAddVWW(SB),NOSPLIT,$0
+ MOVD z+0(FP), R2
+ MOVD x+24(FP), R8
+ MOVD y+48(FP), R9
+ MOVD r+56(FP), R4 // c = r
+ MOVD z_len+8(FP), R5
+ MOVD $0, R1 // i = 0
+ MOVD $0, R7 // i*8 = 0
+ MOVD $0, R0 // make sure it's zero
+ BR E5
+
+L5: MOVD (R8)(R1*1), R6
+ MULHDU R9, R6
+ ADDC R4, R11 //add to low order bits
+ ADDE R0, R6
+ MOVD R11, (R2)(R1*1)
+ MOVD R6, R4
+ ADD $8, R1 // i*8 + 8
+ ADD $1, R7 // i++
+
+E5: CMPBLT R7, R5, L5 // i < n
+
+ MOVD R4, c+64(FP)
+ RET
+
+// func addMulVVW(z, x []Word, y Word) (c Word)
+// CX = R4, r8 = r8, r9=r9, r10 = r2 , r11 = r5, AX = r11, DX = R6, r12=r12, BX = R1 , (R0 set to 0) + use R11 + use R7 for i
+TEXT ·addMulVVW(SB),NOSPLIT,$0
+ MOVD z+0(FP), R2
+ MOVD x+24(FP), R8
+ MOVD y+48(FP), R9
+ MOVD z_len+8(FP), R5
+
+ MOVD $0, R1 // i*8 = 0
+ MOVD $0, R7 // i = 0
+ MOVD $0, R0 // make sure it's zero
+ MOVD $0, R4 // c = 0
+
+ MOVD R5, R12
+ AND $-2, R12
+ CMPBGE R5, $2, A6
+ BR E6
+
+A6: MOVD (R8)(R1*1), R6
+ MULHDU R9, R6
+ MOVD (R2)(R1*1), R10
+ ADDC R10, R11 //add to low order bits
+ ADDE R0, R6
+ ADDC R4, R11
+ ADDE R0, R6
+ MOVD R6, R4
+ MOVD R11, (R2)(R1*1)
+
+ MOVD (8)(R8)(R1*1), R6
+ MULHDU R9, R6
+ MOVD (8)(R2)(R1*1), R10
+ ADDC R10, R11 //add to low order bits
+ ADDE R0, R6
+ ADDC R4, R11
+ ADDE R0, R6
+ MOVD R6, R4
+ MOVD R11, (8)(R2)(R1*1)
+
+ ADD $16, R1 // i*8 + 8
+ ADD $2, R7 // i++
+
+ CMPBLT R7, R12, A6
+ BR E6
+
+L6: MOVD (R8)(R1*1), R6
+ MULHDU R9, R6
+ MOVD (R2)(R1*1), R10
+ ADDC R10, R11 //add to low order bits
+ ADDE R0, R6
+ ADDC R4, R11
+ ADDE R0, R6
+ MOVD R6, R4
+ MOVD R11, (R2)(R1*1)
+
+ ADD $8, R1 // i*8 + 8
+ ADD $1, R7 // i++
+
+E6: CMPBLT R7, R5, L6 // i < n
+
+ MOVD R4, c+56(FP)
+ RET
+
+// func divWVW(z []Word, xn Word, x []Word, y Word) (r Word)
+// CX = R4, r8 = r8, r9=r9, r10 = r2 , r11 = r5, AX = r11, DX = R6, r12=r12, BX = R1(*8) , (R0 set to 0) + use R11 + use R7 for i
+TEXT ·divWVW(SB),NOSPLIT,$0
+ MOVD z+0(FP), R2
+ MOVD xn+24(FP), R10 // r = xn
+ MOVD x+32(FP), R8
+ MOVD y+56(FP), R9
+ MOVD z_len+8(FP), R7 // i = z
+ SLD $3, R7, R1 // i*8
+ MOVD $0, R0 // make sure it's zero
+ BR E7
+
+L7: MOVD (R8)(R1*1), R11
+ WORD $0xB98700A9 //DLGR R10,R9
+ MOVD R11, (R2)(R1*1)
+
+E7: SUB $1, R7 // i--
+ SUB $8, R1
+ BGE L7 // i >= 0
+
+ MOVD R10, r+64(FP)
+ RET
+
+// func bitLen(x Word) (n int)
+TEXT ·bitLen(SB),NOSPLIT,$0
+ MOVD x+0(FP), R2
+ WORD $0xb9830022 // FLOGR R2,R2
+ MOVD $64, R3
+ SUB R2, R3
+ MOVD R3, n+8(FP)
+ RET
diff --git a/src/math/big/float.go b/src/math/big/float.go
index 4b8ad388d3..7a9c2b3dfb 100644
--- a/src/math/big/float.go
+++ b/src/math/big/float.go
@@ -1008,9 +1008,9 @@ func (x *Float) Float64() (float64, Accuracy) {
if r.form == inf || e > emax {
// overflow
if x.neg {
- return float64(math.Inf(-1)), Below
+ return math.Inf(-1), Below
}
- return float64(math.Inf(+1)), Above
+ return math.Inf(+1), Above
}
// e <= emax
diff --git a/src/math/big/floatmarsh.go b/src/math/big/floatmarsh.go
index 44987ee03a..3725d4b834 100644
--- a/src/math/big/floatmarsh.go
+++ b/src/math/big/floatmarsh.go
@@ -6,7 +6,94 @@
package big
-import "fmt"
+import (
+ "encoding/binary"
+ "fmt"
+)
+
+// Gob codec version. Permits backward-compatible changes to the encoding.
+const floatGobVersion byte = 1
+
+// GobEncode implements the gob.GobEncoder interface.
+// The Float value and all its attributes (precision,
+// rounding mode, accuracy) are marshalled.
+func (x *Float) GobEncode() ([]byte, error) {
+ if x == nil {
+ return nil, nil
+ }
+
+ // determine max. space (bytes) required for encoding
+ sz := 1 + 1 + 4 // version + mode|acc|form|neg (3+2+2+1bit) + prec
+ n := 0 // number of mantissa words
+ if x.form == finite {
+ // add space for mantissa and exponent
+ n = int((x.prec + (_W - 1)) / _W) // required mantissa length in words for given precision
+ // actual mantissa slice could be shorter (trailing 0's) or longer (unused bits):
+ // - if shorter, only encode the words present
+ // - if longer, cut off unused words when encoding in bytes
+ // (in practice, this should never happen since rounding
+ // takes care of it, but be safe and do it always)
+ if len(x.mant) < n {
+ n = len(x.mant)
+ }
+ // len(x.mant) >= n
+ sz += 4 + n*_S // exp + mant
+ }
+ buf := make([]byte, sz)
+
+ buf[0] = floatGobVersion
+ b := byte(x.mode&7)<<5 | byte((x.acc+1)&3)<<3 | byte(x.form&3)<<1
+ if x.neg {
+ b |= 1
+ }
+ buf[1] = b
+ binary.BigEndian.PutUint32(buf[2:], x.prec)
+
+ if x.form == finite {
+ binary.BigEndian.PutUint32(buf[6:], uint32(x.exp))
+ x.mant[len(x.mant)-n:].bytes(buf[10:]) // cut off unused trailing words
+ }
+
+ return buf, nil
+}
+
+// GobDecode implements the gob.GobDecoder interface.
+// The result is rounded per the precision and rounding mode of
+// z unless z's precision is 0, in which case z is set exactly
+// to the decoded value.
+func (z *Float) GobDecode(buf []byte) error {
+ if len(buf) == 0 {
+ // Other side sent a nil or default value.
+ *z = Float{}
+ return nil
+ }
+
+ if buf[0] != floatGobVersion {
+ return fmt.Errorf("Float.GobDecode: encoding version %d not supported", buf[0])
+ }
+
+ oldPrec := z.prec
+ oldMode := z.mode
+
+ b := buf[1]
+ z.mode = RoundingMode((b >> 5) & 7)
+ z.acc = Accuracy((b>>3)&3) - 1
+ z.form = form((b >> 1) & 3)
+ z.neg = b&1 != 0
+ z.prec = binary.BigEndian.Uint32(buf[2:])
+
+ if z.form == finite {
+ z.exp = int32(binary.BigEndian.Uint32(buf[6:]))
+ z.mant = z.mant.setBytes(buf[10:])
+ }
+
+ if oldPrec != 0 {
+ z.mode = oldMode
+ z.SetPrec(uint(oldPrec))
+ }
+
+ return nil
+}
// MarshalText implements the encoding.TextMarshaler interface.
// Only the Float value is marshaled (in full precision), other
diff --git a/src/math/big/floatmarsh_test.go b/src/math/big/floatmarsh_test.go
index d7ef2fca68..5bd906ddae 100644
--- a/src/math/big/floatmarsh_test.go
+++ b/src/math/big/floatmarsh_test.go
@@ -5,7 +5,10 @@
package big
import (
+ "bytes"
+ "encoding/gob"
"encoding/json"
+ "io"
"testing"
)
@@ -23,6 +26,85 @@ var floatVals = []string{
"Inf",
}
+func TestFloatGobEncoding(t *testing.T) {
+ var medium bytes.Buffer
+ enc := gob.NewEncoder(&medium)
+ dec := gob.NewDecoder(&medium)
+ for _, test := range floatVals {
+ for _, sign := range []string{"", "+", "-"} {
+ for _, prec := range []uint{0, 1, 2, 10, 53, 64, 100, 1000} {
+ for _, mode := range []RoundingMode{ToNearestEven, ToNearestAway, ToZero, AwayFromZero, ToNegativeInf, ToPositiveInf} {
+ medium.Reset() // empty buffer for each test case (in case of failures)
+ x := sign + test
+
+ var tx Float
+ _, _, err := tx.SetPrec(prec).SetMode(mode).Parse(x, 0)
+ if err != nil {
+ t.Errorf("parsing of %s (%dbits, %v) failed (invalid test case): %v", x, prec, mode, err)
+ continue
+ }
+
+ // If tx was set to prec == 0, tx.Parse(x, 0) assumes precision 64. Correct it.
+ if prec == 0 {
+ tx.SetPrec(0)
+ }
+
+ if err := enc.Encode(&tx); err != nil {
+ t.Errorf("encoding of %v (%dbits, %v) failed: %v", &tx, prec, mode, err)
+ continue
+ }
+
+ var rx Float
+ if err := dec.Decode(&rx); err != nil {
+ t.Errorf("decoding of %v (%dbits, %v) failed: %v", &tx, prec, mode, err)
+ continue
+ }
+
+ if rx.Cmp(&tx) != 0 {
+ t.Errorf("transmission of %s failed: got %s want %s", x, rx.String(), tx.String())
+ continue
+ }
+
+ if rx.Prec() != prec {
+ t.Errorf("transmission of %s's prec failed: got %d want %d", x, rx.Prec(), prec)
+ }
+
+ if rx.Mode() != mode {
+ t.Errorf("transmission of %s's mode failed: got %s want %s", x, rx.Mode(), mode)
+ }
+
+ if rx.Acc() != tx.Acc() {
+ t.Errorf("transmission of %s's accuracy failed: got %s want %s", x, rx.Acc(), tx.Acc())
+ }
+ }
+ }
+ }
+ }
+}
+
+func TestFloatCorruptGob(t *testing.T) {
+ var buf bytes.Buffer
+ tx := NewFloat(4 / 3).SetPrec(1000).SetMode(ToPositiveInf)
+ if err := gob.NewEncoder(&buf).Encode(tx); err != nil {
+ t.Fatal(err)
+ }
+ b := buf.Bytes()
+
+ var rx Float
+ if err := gob.NewDecoder(bytes.NewReader(b)).Decode(&rx); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := gob.NewDecoder(bytes.NewReader(b[:10])).Decode(&rx); err != io.ErrUnexpectedEOF {
+ t.Errorf("got %v want EOF", err)
+ }
+
+ b[1] = 0
+ if err := gob.NewDecoder(bytes.NewReader(b)).Decode(&rx); err == nil {
+ t.Fatal("got nil want version error")
+ }
+}
+
func TestFloatJSONEncoding(t *testing.T) {
for _, test := range floatVals {
for _, sign := range []string{"", "+", "-"} {
diff --git a/src/math/big/gcd_test.go b/src/math/big/gcd_test.go
index c0b9f58300..a929bf597f 100644
--- a/src/math/big/gcd_test.go
+++ b/src/math/big/gcd_test.go
@@ -20,13 +20,27 @@ func randInt(r *rand.Rand, size uint) *Int {
}
func runGCD(b *testing.B, aSize, bSize uint) {
+ b.Run("WithoutXY", func(b *testing.B) {
+ runGCDExt(b, aSize, bSize, false)
+ })
+ b.Run("WithXY", func(b *testing.B) {
+ runGCDExt(b, aSize, bSize, true)
+ })
+}
+
+func runGCDExt(b *testing.B, aSize, bSize uint, calcXY bool) {
b.StopTimer()
var r = rand.New(rand.NewSource(1234))
aa := randInt(r, aSize)
bb := randInt(r, bSize)
+ var x, y *Int
+ if calcXY {
+ x = new(Int)
+ y = new(Int)
+ }
b.StartTimer()
for i := 0; i < b.N; i++ {
- new(Int).GCD(nil, nil, aa, bb)
+ new(Int).GCD(x, y, aa, bb)
}
}
diff --git a/src/math/big/int.go b/src/math/big/int.go
index 67ab7042ff..f2a75d1cd5 100644
--- a/src/math/big/int.go
+++ b/src/math/big/int.go
@@ -459,11 +459,11 @@ func (z *Int) GCD(x, y, a, b *Int) *Int {
q := new(Int)
temp := new(Int)
+ r := new(Int)
for len(B.abs) > 0 {
- r := new(Int)
q, r = q.QuoRem(A, B, r)
- A, B = B, r
+ A, B, r = B, r, A
temp.Set(X)
X.Mul(X, q)
diff --git a/src/math/big/nat.go b/src/math/big/nat.go
index 7668b6481b..2e65d2a7ef 100644
--- a/src/math/big/nat.go
+++ b/src/math/big/nat.go
@@ -8,7 +8,10 @@
package big
-import "math/rand"
+import (
+ "math/rand"
+ "sync"
+)
// An unsigned integer x of the form
//
@@ -539,6 +542,21 @@ func (z nat) div(z2, u, v nat) (q, r nat) {
return
}
+// getNat returns a nat of len n. The contents may not be zero.
+func getNat(n int) nat {
+ var z nat
+ if v := natPool.Get(); v != nil {
+ z = v.(nat)
+ }
+ return z.make(n)
+}
+
+func putNat(x nat) {
+ natPool.Put(x)
+}
+
+var natPool sync.Pool
+
// 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.
@@ -557,7 +575,7 @@ func (z nat) divLarge(u, uIn, v nat) (q, r nat) {
}
q = z.make(m + 1)
- qhatv := make(nat, n+1)
+ qhatv := getNat(n + 1)
if alias(u, uIn) || alias(u, v) {
u = nil // u is an alias for uIn or v - cannot reuse
}
@@ -565,10 +583,11 @@ func (z nat) divLarge(u, uIn, v nat) (q, r nat) {
u.clear() // TODO(gri) no need to clear if we allocated a new u
// D1.
+ var v1 nat
shift := nlz(v[n-1])
if shift > 0 {
// do not modify v, it may be used by another goroutine simultaneously
- v1 := make(nat, n)
+ v1 = getNat(n)
shlVU(v1, v, shift)
v = v1
}
@@ -609,6 +628,10 @@ func (z nat) divLarge(u, uIn, v nat) (q, r nat) {
q[j] = qhat
}
+ if v1 != nil {
+ putNat(v1)
+ }
+ putNat(qhatv)
q = q.norm()
shrVU(u, u, shift)
diff --git a/src/math/big/natconv.go b/src/math/big/natconv.go
index d2ce667fb6..e216bd288c 100644
--- a/src/math/big/natconv.go
+++ b/src/math/big/natconv.go
@@ -302,7 +302,7 @@ func (x nat) itoa(neg bool, base int) []byte {
}
} else {
- bb, ndigits := maxPow(Word(b))
+ bb, ndigits := maxPow(b)
// construct table of successive squares of bb*leafSize to use in subdivisions
// result (table != nil) <=> (len(x) > leafSize > 0)
diff --git a/src/math/big/ratconv.go b/src/math/big/ratconv.go
index 57df124e88..7c127f8585 100644
--- a/src/math/big/ratconv.go
+++ b/src/math/big/ratconv.go
@@ -178,7 +178,7 @@ func scanExponent(r io.ByteScanner, binExpOk bool) (exp int64, base int, err err
}
break // i > 0
}
- digits = append(digits, byte(ch))
+ digits = append(digits, ch)
}
// i > 0 => we have at least one digit
diff --git a/src/math/dim_s390x.s b/src/math/dim_s390x.s
new file mode 100644
index 0000000000..503d2611f8
--- /dev/null
+++ b/src/math/dim_s390x.s
@@ -0,0 +1,132 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Based on dim_amd64.s
+
+#include "textflag.h"
+
+#define PosInf 0x7FF0000000000000
+#define NaN 0x7FF8000000000001
+#define NegInf 0xFFF0000000000000
+
+// func Dim(x, y float64) float64
+TEXT ·Dim(SB),NOSPLIT,$0
+ // (+Inf, +Inf) special case
+ MOVD x+0(FP), R2
+ MOVD y+8(FP), R3
+ MOVD $PosInf, R4
+ CMPUBNE R4, R2, dim2
+ CMPUBEQ R4, R3, bothInf
+dim2: // (-Inf, -Inf) special case
+ MOVD $NegInf, R4
+ CMPUBNE R4, R2, dim3
+ CMPUBEQ R4, R3, bothInf
+dim3: // (NaN, x) or (x, NaN)
+ MOVD $~(1<<63), R5
+ MOVD $PosInf, R4
+ AND R5, R2 // x = |x|
+ CMPUBLT R4, R2, isDimNaN
+ AND R5, R3 // y = |y|
+ CMPUBLT R4, R3, isDimNaN
+
+ FMOVD x+0(FP), F1
+ FMOVD y+8(FP), F2
+ FSUB F2, F1
+ FMOVD $(0.0), F2
+ FCMPU F2, F1
+ BGE +3(PC)
+ FMOVD F1, ret+16(FP)
+ RET
+ FMOVD F2, ret+16(FP)
+ RET
+bothInf: // Dim(-Inf, -Inf) or Dim(+Inf, +Inf)
+isDimNaN:
+ MOVD $NaN, R4
+ MOVD R4, ret+16(FP)
+ RET
+
+// func ·Max(x, y float64) float64
+TEXT ·Max(SB),NOSPLIT,$0
+ // +Inf special cases
+ MOVD $PosInf, R4
+ MOVD x+0(FP), R8
+ CMPUBEQ R4, R8, isPosInf
+ MOVD y+8(FP), R9
+ CMPUBEQ R4, R9, isPosInf
+ // NaN special cases
+ MOVD $~(1<<63), R5 // bit mask
+ MOVD $PosInf, R4
+ MOVD R8, R2
+ AND R5, R2 // x = |x|
+ CMPUBLT R4, R2, isMaxNaN
+ MOVD R9, R3
+ AND R5, R3 // y = |y|
+ CMPUBLT R4, R3, isMaxNaN
+ // ±0 special cases
+ OR R3, R2
+ BEQ isMaxZero
+
+ FMOVD x+0(FP), F1
+ FMOVD y+8(FP), F2
+ FCMPU F2, F1
+ BGT +3(PC)
+ FMOVD F1, ret+16(FP)
+ RET
+ FMOVD F2, ret+16(FP)
+ RET
+isMaxNaN: // return NaN
+ MOVD $NaN, R4
+isPosInf: // return +Inf
+ MOVD R4, ret+16(FP)
+ RET
+isMaxZero:
+ MOVD $(1<<63), R4 // -0.0
+ CMPUBEQ R4, R8, +3(PC)
+ MOVD R8, ret+16(FP) // return 0
+ RET
+ MOVD R9, ret+16(FP) // return other 0
+ RET
+
+// func Min(x, y float64) float64
+TEXT ·Min(SB),NOSPLIT,$0
+ // -Inf special cases
+ MOVD $NegInf, R4
+ MOVD x+0(FP), R8
+ CMPUBEQ R4, R8, isNegInf
+ MOVD y+8(FP), R9
+ CMPUBEQ R4, R9, isNegInf
+ // NaN special cases
+ MOVD $~(1<<63), R5
+ MOVD $PosInf, R4
+ MOVD R8, R2
+ AND R5, R2 // x = |x|
+ CMPUBLT R4, R2, isMinNaN
+ MOVD R9, R3
+ AND R5, R3 // y = |y|
+ CMPUBLT R4, R3, isMinNaN
+ // ±0 special cases
+ OR R3, R2
+ BEQ isMinZero
+
+ FMOVD x+0(FP), F1
+ FMOVD y+8(FP), F2
+ FCMPU F2, F1
+ BLT +3(PC)
+ FMOVD F1, ret+16(FP)
+ RET
+ FMOVD F2, ret+16(FP)
+ RET
+isMinNaN: // return NaN
+ MOVD $NaN, R4
+isNegInf: // return -Inf
+ MOVD R4, ret+16(FP)
+ RET
+isMinZero:
+ MOVD $(1<<63), R4 // -0.0
+ CMPUBEQ R4, R8, +3(PC)
+ MOVD R9, ret+16(FP) // return other 0
+ RET
+ MOVD R8, ret+16(FP) // return -0
+ RET
+
diff --git a/src/math/sqrt_s390x.s b/src/math/sqrt_s390x.s
new file mode 100644
index 0000000000..37ca0bec91
--- /dev/null
+++ b/src/math/sqrt_s390x.s
@@ -0,0 +1,12 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+// func Sqrt(x float64) float64
+TEXT ·Sqrt(SB),NOSPLIT,$0
+ FMOVD x+0(FP), F1
+ FSQRT F1, F1
+ FMOVD F1, ret+8(FP)
+ RET
diff --git a/src/math/stubs_s390x.s b/src/math/stubs_s390x.s
new file mode 100644
index 0000000000..76868447cd
--- /dev/null
+++ b/src/math/stubs_s390x.s
@@ -0,0 +1,77 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "../runtime/textflag.h"
+
+TEXT ·Asin(SB),NOSPLIT,$0
+ BR ·asin(SB)
+
+TEXT ·Acos(SB),NOSPLIT,$0
+ BR ·acos(SB)
+
+TEXT ·Atan2(SB),NOSPLIT,$0
+ BR ·atan2(SB)
+
+TEXT ·Atan(SB),NOSPLIT,$0
+ BR ·atan(SB)
+
+TEXT ·Exp2(SB),NOSPLIT,$0
+ BR ·exp2(SB)
+
+TEXT ·Expm1(SB),NOSPLIT,$0
+ BR ·expm1(SB)
+
+TEXT ·Exp(SB),NOSPLIT,$0
+ BR ·exp(SB)
+
+TEXT ·Floor(SB),NOSPLIT,$0
+ BR ·floor(SB)
+
+TEXT ·Ceil(SB),NOSPLIT,$0
+ BR ·ceil(SB)
+
+TEXT ·Trunc(SB),NOSPLIT,$0
+ BR ·trunc(SB)
+
+TEXT ·Frexp(SB),NOSPLIT,$0
+ BR ·frexp(SB)
+
+TEXT ·Hypot(SB),NOSPLIT,$0
+ BR ·hypot(SB)
+
+TEXT ·Ldexp(SB),NOSPLIT,$0
+ BR ·ldexp(SB)
+
+TEXT ·Log10(SB),NOSPLIT,$0
+ BR ·log10(SB)
+
+TEXT ·Log2(SB),NOSPLIT,$0
+ BR ·log2(SB)
+
+TEXT ·Log1p(SB),NOSPLIT,$0
+ BR ·log1p(SB)
+
+TEXT ·Log(SB),NOSPLIT,$0
+ BR ·log(SB)
+
+TEXT ·Modf(SB),NOSPLIT,$0
+ BR ·modf(SB)
+
+TEXT ·Mod(SB),NOSPLIT,$0
+ BR ·mod(SB)
+
+TEXT ·Remainder(SB),NOSPLIT,$0
+ BR ·remainder(SB)
+
+TEXT ·Sincos(SB),NOSPLIT,$0
+ BR ·sincos(SB)
+
+TEXT ·Sin(SB),NOSPLIT,$0
+ BR ·sin(SB)
+
+TEXT ·Cos(SB),NOSPLIT,$0
+ BR ·cos(SB)
+
+TEXT ·Tan(SB),NOSPLIT,$0
+ BR ·tan(SB)
diff --git a/src/mime/encodedword.go b/src/mime/encodedword.go
index e6cbebe946..c3ca4bacd1 100644
--- a/src/mime/encodedword.go
+++ b/src/mime/encodedword.go
@@ -16,7 +16,7 @@ import (
"unicode/utf8"
)
-// A WordEncoder is a RFC 2047 encoded-word encoder.
+// A WordEncoder is an RFC 2047 encoded-word encoder.
type WordEncoder byte
const (
diff --git a/src/naclmake.bash b/src/naclmake.bash
new file mode 100755
index 0000000000..046f50aa87
--- /dev/null
+++ b/src/naclmake.bash
@@ -0,0 +1,48 @@
+#!/bin/bash
+# Copyright 2016 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+# naclmake.bash builds runs make.bash for nacl, but not does run any
+# tests. This is used by the continuous build.
+
+# Assumes that sel_ldr binaries and go_nacl_$GOARCH_exec scripts are in $PATH;
+# see ../misc/nacl/README.
+
+set -e
+ulimit -c 0
+
+# guess GOARCH if not set
+naclGOARCH=$GOARCH
+if [ -z "$naclGOARCH" ]; then
+ case "$(uname -m)" in
+ x86_64)
+ naclGOARCH=amd64p32
+ ;;
+ armv7l) # NativeClient on ARM only supports ARMv7A.
+ naclGOARCH=arm
+ ;;
+ i?86)
+ naclGOARCH=386
+ ;;
+ esac
+fi
+
+unset GOOS GOARCH
+if [ ! -f make.bash ]; then
+ echo 'nacltest.bash must be run from $GOROOT/src' 1>&2
+ exit 1
+fi
+
+# the builder might have set GOROOT_FINAL.
+export GOROOT=$(pwd)/..
+
+# Build zip file embedded in package syscall.
+echo "##### Building fake file system zip for nacl"
+rm -f syscall/fstest_nacl.go
+GOROOT_BOOTSTRAP=${GOROOT_BOOTSTRAP:-$HOME/go1.4}
+gobin=$GOROOT_BOOTSTRAP/bin
+GOROOT=$GOROOT_BOOTSTRAP $gobin/go run ../misc/nacl/mkzip.go -p syscall -r .. ../misc/nacl/testzip.proto syscall/fstest_nacl.go
+
+# Run standard build and tests.
+GOOS=nacl GOARCH=$naclGOARCH ./make.bash
diff --git a/src/nacltest.bash b/src/nacltest.bash
index 049aad2ff2..538d6b7e9b 100755
--- a/src/nacltest.bash
+++ b/src/nacltest.bash
@@ -13,21 +13,7 @@
set -e
ulimit -c 0
-# guess GOARCH if not set
-naclGOARCH=$GOARCH
-if [ -z "$naclGOARCH" ]; then
- case "$(uname -m)" in
- x86_64)
- naclGOARCH=amd64p32
- ;;
- armv7l) # NativeClient on ARM only supports ARMv7A.
- naclGOARCH=arm
- ;;
- i?86)
- naclGOARCH=386
- ;;
- esac
-fi
+. ./naclmake.bash
# Check GOARCH.
case "$naclGOARCH" in
@@ -59,24 +45,8 @@ if ! which go_nacl_${naclGOARCH}_exec >/dev/null; then
exit 1
fi
-unset GOOS GOARCH
-if [ ! -f make.bash ]; then
- echo 'nacltest.bash must be run from $GOROOT/src' 1>&2
- exit 1
-fi
-
-# the builder might have set GOROOT_FINAL.
-export GOROOT=$(pwd)/..
-
-# Build zip file embedded in package syscall.
-echo "##### Building fake file system zip for nacl"
-rm -f syscall/fstest_nacl.go
-GOROOT_BOOTSTRAP=${GOROOT_BOOTSTRAP:-$HOME/go1.4}
-gobin=$GOROOT_BOOTSTRAP/bin
-GOROOT=$GOROOT_BOOTSTRAP $gobin/go run ../misc/nacl/mkzip.go -p syscall -r .. ../misc/nacl/testzip.proto syscall/fstest_nacl.go
-
-# Run standard build and tests.
-export PATH=$(pwd)/../misc/nacl:$PATH
-GOOS=nacl GOARCH=$naclGOARCH ./all.bash
+export PATH=$(pwd)/../bin:$(pwd)/../misc/nacl:$PATH
+GOROOT=$(../bin/go env GOROOT)
+GOOS=nacl GOARCH=$naclGOARCH go tool dist test --no-rebuild
rm -f syscall/fstest_nacl.go
diff --git a/src/net/cgo_unix_test.go b/src/net/cgo_unix_test.go
index 4d5ab23fd3..5dc7b1a62d 100644
--- a/src/net/cgo_unix_test.go
+++ b/src/net/cgo_unix_test.go
@@ -7,7 +7,10 @@
package net
-import "testing"
+import (
+ "context"
+ "testing"
+)
func TestCgoLookupIP(t *testing.T) {
host := "localhost"
@@ -18,7 +21,7 @@ func TestCgoLookupIP(t *testing.T) {
if err != nil {
t.Error(err)
}
- if _, err := goLookupIP(host); err != nil {
+ if _, err := goLookupIP(context.Background(), host); err != nil {
t.Error(err)
}
}
diff --git a/src/net/dial.go b/src/net/dial.go
index 22992d5b7a..3443161004 100644
--- a/src/net/dial.go
+++ b/src/net/dial.go
@@ -5,7 +5,7 @@
package net
import (
- "runtime"
+ "context"
"time"
)
@@ -61,21 +61,34 @@ type Dialer struct {
// Cancel is an optional channel whose closure indicates that
// the dial should be canceled. Not all types of dials support
// cancelation.
+ //
+ // Deprecated: Use DialContext instead.
Cancel <-chan struct{}
}
-// Return either now+Timeout or Deadline, whichever comes first.
-// Or zero, if neither is set.
-func (d *Dialer) deadline(now time.Time) time.Time {
- if d.Timeout == 0 {
- return d.Deadline
+func minNonzeroTime(a, b time.Time) time.Time {
+ if a.IsZero() {
+ return b
}
- timeoutDeadline := now.Add(d.Timeout)
- if d.Deadline.IsZero() || timeoutDeadline.Before(d.Deadline) {
- return timeoutDeadline
- } else {
- return d.Deadline
+ if b.IsZero() || a.Before(b) {
+ return a
}
+ return b
+}
+
+// deadline returns the earliest of:
+// - now+Timeout
+// - d.Deadline
+// - the context's deadline
+// Or zero, if none of Timeout, Deadline, or context's deadline is set.
+func (d *Dialer) deadline(ctx context.Context, now time.Time) (earliest time.Time) {
+ if d.Timeout != 0 { // including negative, for historical reasons
+ earliest = now.Add(d.Timeout)
+ }
+ if d, ok := ctx.Deadline(); ok {
+ earliest = minNonzeroTime(earliest, d)
+ }
+ return minNonzeroTime(earliest, d.Deadline)
}
// partialDeadline returns the deadline to use for a single address,
@@ -110,7 +123,7 @@ func (d *Dialer) fallbackDelay() time.Duration {
}
}
-func parseNetwork(net string) (afnet string, proto int, err error) {
+func parseNetwork(ctx context.Context, net string) (afnet string, proto int, err error) {
i := last(net, ':')
if i < 0 { // no colon
switch net {
@@ -129,7 +142,7 @@ func parseNetwork(net string) (afnet string, proto int, err error) {
protostr := net[i+1:]
proto, i, ok := dtoi(protostr, 0)
if !ok || i != len(protostr) {
- proto, err = lookupProtocol(protostr)
+ proto, err = lookupProtocol(ctx, protostr)
if err != nil {
return "", 0, err
}
@@ -142,8 +155,8 @@ func parseNetwork(net string) (afnet string, proto int, err error) {
// resolverAddrList resolves addr using hint and returns a list of
// addresses. The result contains at least one address when error is
// nil.
-func resolveAddrList(op, network, addr string, hint Addr, deadline time.Time) (addrList, error) {
- afnet, _, err := parseNetwork(network)
+func resolveAddrList(ctx context.Context, op, network, addr string, hint Addr) (addrList, error) {
+ afnet, _, err := parseNetwork(ctx, network)
if err != nil {
return nil, err
}
@@ -152,6 +165,7 @@ func resolveAddrList(op, network, addr string, hint Addr, deadline time.Time) (a
}
switch afnet {
case "unix", "unixgram", "unixpacket":
+ // TODO(bradfitz): push down context
addr, err := ResolveUnixAddr(afnet, addr)
if err != nil {
return nil, err
@@ -161,7 +175,7 @@ func resolveAddrList(op, network, addr string, hint Addr, deadline time.Time) (a
}
return addrList{addr}, nil
}
- addrs, err := internetAddrList(afnet, addr, deadline)
+ addrs, err := internetAddrList(ctx, afnet, addr)
if err != nil || op != "dial" || hint == nil {
return addrs, err
}
@@ -253,11 +267,10 @@ func DialTimeout(network, address string, timeout time.Duration) (Conn, error) {
return d.Dial(network, address)
}
-// dialContext holds common state for all dial operations.
-type dialContext struct {
+// dialParam contains a Dial's parameters and configuration.
+type dialParam struct {
Dialer
network, address string
- finalDeadline time.Time
}
// Dial connects to the address on the named network.
@@ -265,161 +278,182 @@ type dialContext struct {
// See func Dial for a description of the network and address
// parameters.
func (d *Dialer) Dial(network, address string) (Conn, error) {
- finalDeadline := d.deadline(time.Now())
- addrs, err := resolveAddrList("dial", network, address, d.LocalAddr, finalDeadline)
+ return d.DialContext(context.Background(), network, address)
+}
+
+// DialContext connects to the address on the named network using
+// the provided context.
+//
+// The provided Context must be non-nil.
+//
+// See func Dial for a description of the network and address
+// parameters.
+func (d *Dialer) DialContext(ctx context.Context, network, address string) (Conn, error) {
+ if ctx == nil {
+ panic("nil context")
+ }
+ deadline := d.deadline(ctx, time.Now())
+ if !deadline.IsZero() {
+ if d, ok := ctx.Deadline(); !ok || deadline.Before(d) {
+ subCtx, cancel := context.WithDeadline(ctx, deadline)
+ defer cancel()
+ ctx = subCtx
+ }
+ }
+ if oldCancel := d.Cancel; oldCancel != nil {
+ subCtx, cancel := context.WithCancel(ctx)
+ defer cancel()
+ go func() {
+ select {
+ case <-oldCancel:
+ cancel()
+ case <-subCtx.Done():
+ }
+ }()
+ ctx = subCtx
+ }
+
+ addrs, err := resolveAddrList(ctx, "dial", network, address, d.LocalAddr)
if err != nil {
return nil, &OpError{Op: "dial", Net: network, Source: nil, Addr: nil, Err: err}
}
- ctx := &dialContext{
- Dialer: *d,
- network: network,
- address: address,
- finalDeadline: finalDeadline,
+ dp := &dialParam{
+ Dialer: *d,
+ network: network,
+ address: address,
}
- // DualStack mode requires that dialTCP support cancelation. This is
- // not available on plan9 (golang.org/issue/11225), so we ignore it.
var primaries, fallbacks addrList
- if d.DualStack && network == "tcp" && runtime.GOOS != "plan9" {
+ if d.DualStack && network == "tcp" {
primaries, fallbacks = addrs.partition(isIPv4)
} else {
primaries = addrs
}
var c Conn
- if len(fallbacks) == 0 {
- // dialParallel can accept an empty fallbacks list,
- // but this shortcut avoids the goroutine/channel overhead.
- c, err = dialSerial(ctx, primaries, ctx.Cancel)
+ if len(fallbacks) > 0 {
+ c, err = dialParallel(ctx, dp, primaries, fallbacks)
} else {
- c, err = dialParallel(ctx, primaries, fallbacks, ctx.Cancel)
+ c, err = dialSerial(ctx, dp, primaries)
+ }
+ if err != nil {
+ return nil, err
}
- if d.KeepAlive > 0 && err == nil {
- if tc, ok := c.(*TCPConn); ok {
- setKeepAlive(tc.fd, true)
- setKeepAlivePeriod(tc.fd, d.KeepAlive)
- testHookSetKeepAlive()
- }
+ if tc, ok := c.(*TCPConn); ok && d.KeepAlive > 0 {
+ setKeepAlive(tc.fd, true)
+ setKeepAlivePeriod(tc.fd, d.KeepAlive)
+ testHookSetKeepAlive()
}
- return c, err
+ return c, nil
}
// dialParallel races two copies of dialSerial, giving the first a
// head start. It returns the first established connection and
// closes the others. Otherwise it returns an error from the first
// primary address.
-func dialParallel(ctx *dialContext, primaries, fallbacks addrList, userCancel <-chan struct{}) (Conn, error) {
- results := make(chan dialResult, 2)
- cancel := make(chan struct{})
+func dialParallel(ctx context.Context, dp *dialParam, primaries, fallbacks addrList) (Conn, error) {
+ if len(fallbacks) == 0 {
+ return dialSerial(ctx, dp, primaries)
+ }
- // Spawn the primary racer.
- go dialSerialAsync(ctx, primaries, nil, cancel, results)
+ returned := make(chan struct{})
+ defer close(returned)
- // Spawn the fallback racer.
- fallbackTimer := time.NewTimer(ctx.fallbackDelay())
- go dialSerialAsync(ctx, fallbacks, fallbackTimer, cancel, results)
+ type dialResult struct {
+ Conn
+ error
+ primary bool
+ done bool
+ }
+ results := make(chan dialResult) // unbuffered
- // Wait for both racers to succeed or fail.
- var primaryResult, fallbackResult dialResult
- for !primaryResult.done || !fallbackResult.done {
+ startRacer := func(ctx context.Context, primary bool) {
+ ras := primaries
+ if !primary {
+ ras = fallbacks
+ }
+ c, err := dialSerial(ctx, dp, ras)
select {
- case <-userCancel:
- // Forward an external cancelation request.
- if cancel != nil {
- close(cancel)
- cancel = nil
+ case results <- dialResult{Conn: c, error: err, primary: primary, done: true}:
+ case <-returned:
+ if c != nil {
+ c.Close()
}
- userCancel = nil
+ }
+ }
+
+ var primary, fallback dialResult
+
+ // Start the main racer.
+ primaryCtx, primaryCancel := context.WithCancel(ctx)
+ defer primaryCancel()
+ go startRacer(primaryCtx, true)
+
+ // Start the timer for the fallback racer.
+ fallbackTimer := time.NewTimer(dp.fallbackDelay())
+ defer fallbackTimer.Stop()
+
+ for {
+ select {
+ case <-fallbackTimer.C:
+ fallbackCtx, fallbackCancel := context.WithCancel(ctx)
+ defer fallbackCancel()
+ go startRacer(fallbackCtx, false)
+
case res := <-results:
- // Drop the result into its assigned bucket.
+ if res.error == nil {
+ return res.Conn, nil
+ }
if res.primary {
- primaryResult = res
+ primary = res
} else {
- fallbackResult = res
+ fallback = res
}
- // On success, cancel the other racer (if one exists.)
- if res.error == nil && cancel != nil {
- close(cancel)
- cancel = nil
+ if primary.done && fallback.done {
+ return nil, primary.error
}
- // If the fallbackTimer was pending, then either we've canceled the
- // fallback because we no longer want it, or we haven't canceled yet
- // and therefore want it to wake up immediately.
- if fallbackTimer.Stop() && cancel != nil {
+ if res.primary && fallbackTimer.Stop() {
+ // If we were able to stop the timer, that means it
+ // was running (hadn't yet started the fallback), but
+ // we just got an error on the primary path, so start
+ // the fallback immediately (in 0 nanoseconds).
fallbackTimer.Reset(0)
}
}
}
-
- // Return, in order of preference:
- // 1. The primary connection (but close the other if we got both.)
- // 2. The fallback connection.
- // 3. The primary error.
- if primaryResult.error == nil {
- if fallbackResult.error == nil {
- fallbackResult.Conn.Close()
- }
- return primaryResult.Conn, nil
- } else if fallbackResult.error == nil {
- return fallbackResult.Conn, nil
- } else {
- return nil, primaryResult.error
- }
-}
-
-type dialResult struct {
- Conn
- error
- primary bool
- done bool
-}
-
-// dialSerialAsync runs dialSerial after some delay, and returns the
-// resulting connection through a channel. When racing two connections,
-// the primary goroutine uses a nil timer to omit the delay.
-func dialSerialAsync(ctx *dialContext, ras addrList, timer *time.Timer, cancel <-chan struct{}, results chan<- dialResult) {
- if timer != nil {
- // We're in the fallback goroutine; sleep before connecting.
- select {
- case <-timer.C:
- case <-cancel:
- // dialSerial will immediately return errCanceled in this case.
- }
- }
- c, err := dialSerial(ctx, ras, cancel)
- results <- dialResult{Conn: c, error: err, primary: timer == nil, done: true}
}
// dialSerial connects to a list of addresses in sequence, returning
// either the first successful connection, or the first error.
-func dialSerial(ctx *dialContext, ras addrList, cancel <-chan struct{}) (Conn, error) {
+func dialSerial(ctx context.Context, dp *dialParam, ras addrList) (Conn, error) {
var firstErr error // The error from the first address is most relevant.
for i, ra := range ras {
select {
- case <-cancel:
- return nil, &OpError{Op: "dial", Net: ctx.network, Source: ctx.LocalAddr, Addr: ra, Err: errCanceled}
+ case <-ctx.Done():
+ return nil, &OpError{Op: "dial", Net: dp.network, Source: dp.LocalAddr, Addr: ra, Err: mapErr(ctx.Err())}
default:
}
- partialDeadline, err := partialDeadline(time.Now(), ctx.finalDeadline, len(ras)-i)
+ deadline, _ := ctx.Deadline()
+ partialDeadline, err := partialDeadline(time.Now(), deadline, len(ras)-i)
if err != nil {
// Ran out of time.
if firstErr == nil {
- firstErr = &OpError{Op: "dial", Net: ctx.network, Source: ctx.LocalAddr, Addr: ra, Err: err}
+ firstErr = &OpError{Op: "dial", Net: dp.network, Source: dp.LocalAddr, Addr: ra, Err: err}
}
break
}
-
- // If this dial is canceled, the implementation is expected to complete
- // quickly, but it's still possible that we could return a spurious Conn,
- // which the caller must Close.
- dialer := func(d time.Time) (Conn, error) {
- return dialSingle(ctx, ra, d, cancel)
+ dialCtx := ctx
+ if partialDeadline.Before(deadline) {
+ var cancel context.CancelFunc
+ dialCtx, cancel = context.WithDeadline(ctx, partialDeadline)
+ defer cancel()
}
- c, err := dial(ctx.network, ra, dialer, partialDeadline)
+
+ c, err := dialSingle(dialCtx, dp, ra)
if err == nil {
return c, nil
}
@@ -429,34 +463,33 @@ func dialSerial(ctx *dialContext, ras addrList, cancel <-chan struct{}) (Conn, e
}
if firstErr == nil {
- firstErr = &OpError{Op: "dial", Net: ctx.network, Source: nil, Addr: nil, Err: errMissingAddress}
+ firstErr = &OpError{Op: "dial", Net: dp.network, Source: nil, Addr: nil, Err: errMissingAddress}
}
return nil, firstErr
}
// dialSingle attempts to establish and returns a single connection to
-// the destination address. This must be called through the OS-specific
-// dial function, because some OSes don't implement the deadline feature.
-func dialSingle(ctx *dialContext, ra Addr, deadline time.Time, cancel <-chan struct{}) (c Conn, err error) {
- la := ctx.LocalAddr
+// the destination address.
+func dialSingle(ctx context.Context, dp *dialParam, ra Addr) (c Conn, err error) {
+ la := dp.LocalAddr
switch ra := ra.(type) {
case *TCPAddr:
la, _ := la.(*TCPAddr)
- c, err = testHookDialTCP(ctx.network, la, ra, deadline, cancel)
+ c, err = dialTCP(ctx, dp.network, la, ra)
case *UDPAddr:
la, _ := la.(*UDPAddr)
- c, err = dialUDP(ctx.network, la, ra, deadline)
+ c, err = dialUDP(ctx, dp.network, la, ra)
case *IPAddr:
la, _ := la.(*IPAddr)
- c, err = dialIP(ctx.network, la, ra, deadline)
+ c, err = dialIP(ctx, dp.network, la, ra)
case *UnixAddr:
la, _ := la.(*UnixAddr)
- c, err = dialUnix(ctx.network, la, ra, deadline)
+ c, err = dialUnix(ctx, dp.network, la, ra)
default:
- return nil, &OpError{Op: "dial", Net: ctx.network, Source: la, Addr: ra, Err: &AddrError{Err: "unexpected address type", Addr: ctx.address}}
+ return nil, &OpError{Op: "dial", Net: dp.network, Source: la, Addr: ra, Err: &AddrError{Err: "unexpected address type", Addr: dp.address}}
}
if err != nil {
- return nil, &OpError{Op: "dial", Net: ctx.network, Source: la, Addr: ra, Err: err} // c is non-nil interface containing nil pointer
+ return nil, &OpError{Op: "dial", Net: dp.network, Source: la, Addr: ra, Err: err} // c is non-nil interface containing nil pointer
}
return c, nil
}
@@ -469,7 +502,7 @@ func dialSingle(ctx *dialContext, ra Addr, deadline time.Time, cancel <-chan str
// instead of just the interface with the given host address.
// See Dial for more details about address syntax.
func Listen(net, laddr string) (Listener, error) {
- addrs, err := resolveAddrList("listen", net, laddr, nil, noDeadline)
+ addrs, err := resolveAddrList(context.Background(), "listen", net, laddr, nil)
if err != nil {
return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: nil, Err: err}
}
@@ -496,7 +529,7 @@ func Listen(net, laddr string) (Listener, error) {
// instead of just the interface with the given host address.
// See Dial for the syntax of laddr.
func ListenPacket(net, laddr string) (PacketConn, error) {
- addrs, err := resolveAddrList("listen", net, laddr, nil, noDeadline)
+ addrs, err := resolveAddrList(context.Background(), "listen", net, laddr, nil)
if err != nil {
return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: nil, Err: err}
}
diff --git a/src/net/dial_gen.go b/src/net/dial_gen.go
deleted file mode 100644
index a2cd8cb44d..0000000000
--- a/src/net/dial_gen.go
+++ /dev/null
@@ -1,40 +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.
-
-// +build windows plan9
-
-package net
-
-import "time"
-
-// dialChannel is the simple pure-Go implementation of dial, still
-// used on operating systems where the deadline hasn't been pushed
-// down into the pollserver. (Plan 9 and some old versions of Windows)
-func dialChannel(net string, ra Addr, dialer func(time.Time) (Conn, error), deadline time.Time) (Conn, error) {
- if deadline.IsZero() {
- return dialer(noDeadline)
- }
- timeout := deadline.Sub(time.Now())
- if timeout <= 0 {
- return nil, &OpError{Op: "dial", Net: net, Source: nil, Addr: ra, Err: errTimeout}
- }
- t := time.NewTimer(timeout)
- defer t.Stop()
- type racer struct {
- Conn
- error
- }
- ch := make(chan racer, 1)
- go func() {
- testHookDialChannel()
- c, err := dialer(noDeadline)
- ch <- racer{c, err}
- }()
- select {
- case <-t.C:
- return nil, &OpError{Op: "dial", Net: net, Source: nil, Addr: ra, Err: errTimeout}
- case racer := <-ch:
- return racer.Conn, racer.error
- }
-}
diff --git a/src/net/dial_test.go b/src/net/dial_test.go
index 2fc75c6356..ead1e68d46 100644
--- a/src/net/dial_test.go
+++ b/src/net/dial_test.go
@@ -6,6 +6,7 @@ package net
import (
"bufio"
+ "context"
"internal/testenv"
"io"
"net/internal/socktest"
@@ -24,13 +25,12 @@ var prohibitionaryDialArgTests = []struct {
}
func TestProhibitionaryDialArg(t *testing.T) {
+ testenv.MustHaveExternalNetwork(t)
+
switch runtime.GOOS {
case "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
- if testing.Short() || !*testExternal {
- t.Skip("avoid external network")
- }
if !supportsIPv4map {
t.Skip("mapping ipv4 address inside ipv6 address not supported")
}
@@ -59,6 +59,8 @@ func TestDialTimeoutFDLeak(t *testing.T) {
switch runtime.GOOS {
case "plan9":
t.Skipf("%s does not have full support of socktest", runtime.GOOS)
+ case "openbsd":
+ testenv.SkipFlaky(t, 15157)
}
const T = 100 * time.Millisecond
@@ -126,6 +128,8 @@ func TestDialerDualStackFDLeak(t *testing.T) {
t.Skipf("%s does not have full support of socktest", runtime.GOOS)
case "windows":
t.Skipf("not implemented a way to cancel dial racers in TCP SYN-SENT state on %s", runtime.GOOS)
+ case "openbsd":
+ testenv.SkipFlaky(t, 15157)
}
if !supportsIPv4 || !supportsIPv6 {
t.Skip("both IPv4 and IPv6 are required")
@@ -190,18 +194,11 @@ const (
// In some environments, the slow IPs may be explicitly unreachable, and fail
// more quickly than expected. This test hook prevents dialTCP from returning
// before the deadline.
-func slowDialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time, cancel <-chan struct{}) (*TCPConn, error) {
- c, err := dialTCP(net, laddr, raddr, deadline, cancel)
+func slowDialTCP(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
+ c, err := doDialTCP(ctx, net, laddr, raddr)
if ParseIP(slowDst4).Equal(raddr.IP) || ParseIP(slowDst6).Equal(raddr.IP) {
// Wait for the deadline, or indefinitely if none exists.
- var wait <-chan time.Time
- if !deadline.IsZero() {
- wait = time.After(deadline.Sub(time.Now()))
- }
- select {
- case <-cancel:
- case <-wait:
- }
+ <-ctx.Done()
}
return c, err
}
@@ -239,15 +236,11 @@ func dialClosedPort() (actual, expected time.Duration) {
}
func TestDialParallel(t *testing.T) {
- if testing.Short() || !*testExternal {
- t.Skip("avoid external network")
- }
+ testenv.MustHaveExternalNetwork(t)
+
if !supportsIPv4 || !supportsIPv6 {
t.Skip("both IPv4 and IPv6 are required")
}
- if runtime.GOOS == "plan9" {
- t.Skip("skipping on plan9; cannot cancel dialTCP, golang.org/issue/11225")
- }
closedPortDelay, expectClosedPortDelay := dialClosedPort()
if closedPortDelay > expectClosedPortDelay {
@@ -354,15 +347,14 @@ func TestDialParallel(t *testing.T) {
d := Dialer{
FallbackDelay: fallbackDelay,
}
- ctx := &dialContext{
- Dialer: d,
- network: "tcp",
- address: "?",
- finalDeadline: d.deadline(time.Now()),
- }
startTime := time.Now()
- c, err := dialParallel(ctx, primaries, fallbacks, nil)
- elapsed := time.Now().Sub(startTime)
+ dp := &dialParam{
+ Dialer: d,
+ network: "tcp",
+ address: "?",
+ }
+ c, err := dialParallel(context.Background(), dp, primaries, fallbacks)
+ elapsed := time.Since(startTime)
if c != nil {
c.Close()
@@ -383,16 +375,16 @@ func TestDialParallel(t *testing.T) {
}
// Repeat each case, ensuring that it can be canceled quickly.
- cancel := make(chan struct{})
+ ctx, cancel := context.WithCancel(context.Background())
var wg sync.WaitGroup
wg.Add(1)
go func() {
time.Sleep(5 * time.Millisecond)
- close(cancel)
+ cancel()
wg.Done()
}()
startTime = time.Now()
- c, err = dialParallel(ctx, primaries, fallbacks, cancel)
+ c, err = dialParallel(ctx, dp, primaries, fallbacks)
if c != nil {
c.Close()
}
@@ -404,7 +396,7 @@ func TestDialParallel(t *testing.T) {
}
}
-func lookupSlowFast(fn func(string) ([]IPAddr, error), host string) ([]IPAddr, error) {
+func lookupSlowFast(ctx context.Context, fn func(context.Context, string) ([]IPAddr, error), host string) ([]IPAddr, error) {
switch host {
case "slow6loopback4":
// Returns a slow IPv6 address, and a local IPv4 address.
@@ -413,14 +405,13 @@ func lookupSlowFast(fn func(string) ([]IPAddr, error), host string) ([]IPAddr, e
{IP: ParseIP("127.0.0.1")},
}, nil
default:
- return fn(host)
+ return fn(ctx, host)
}
}
func TestDialerFallbackDelay(t *testing.T) {
- if testing.Short() || !*testExternal {
- t.Skip("avoid external network")
- }
+ testenv.MustHaveExternalNetwork(t)
+
if !supportsIPv4 || !supportsIPv6 {
t.Skip("both IPv4 and IPv6 are required")
}
@@ -492,9 +483,6 @@ func TestDialParallelSpuriousConnection(t *testing.T) {
if !supportsIPv4 || !supportsIPv6 {
t.Skip("both IPv4 and IPv6 are required")
}
- if runtime.GOOS == "plan9" {
- t.Skip("skipping on plan9; cannot cancel dialTCP, golang.org/issue/11225")
- }
var wg sync.WaitGroup
wg.Add(2)
@@ -529,22 +517,24 @@ func TestDialParallelSpuriousConnection(t *testing.T) {
origTestHookDialTCP := testHookDialTCP
defer func() { testHookDialTCP = origTestHookDialTCP }()
- testHookDialTCP = func(net string, laddr, raddr *TCPAddr, deadline time.Time, cancel <-chan struct{}) (*TCPConn, error) {
+ testHookDialTCP = func(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
// Sleep long enough for Happy Eyeballs to kick in, and inhibit cancelation.
// This forces dialParallel to juggle two successful connections.
time.Sleep(fallbackDelay * 2)
- cancel = nil
- return dialTCP(net, laddr, raddr, deadline, cancel)
+
+ // Now ignore the provided context (which will be canceled) and use a
+ // different one to make sure this completes with a valid connection,
+ // which we hope to be closed below:
+ return doDialTCP(context.Background(), net, laddr, raddr)
}
d := Dialer{
FallbackDelay: fallbackDelay,
}
- ctx := &dialContext{
- Dialer: d,
- network: "tcp",
- address: "?",
- finalDeadline: d.deadline(time.Now()),
+ dp := &dialParam{
+ Dialer: d,
+ network: "tcp",
+ address: "?",
}
makeAddr := func(ip string) addrList {
@@ -556,7 +546,7 @@ func TestDialParallelSpuriousConnection(t *testing.T) {
}
// dialParallel returns one connection (and closes the other.)
- c, err := dialParallel(ctx, makeAddr("127.0.0.1"), makeAddr("::1"), nil)
+ c, err := dialParallel(context.Background(), dp, makeAddr("127.0.0.1"), makeAddr("::1"))
if err != nil {
t.Fatal(err)
}
@@ -810,14 +800,16 @@ func TestDialerKeepAlive(t *testing.T) {
}
func TestDialCancel(t *testing.T) {
- if runtime.GOOS == "plan9" || runtime.GOOS == "nacl" {
- // plan9 is not implemented and nacl doesn't have
- // external network access.
- t.Skipf("skipping on %s", runtime.GOOS)
+ switch testenv.Builder() {
+ case "linux-arm64-buildlet":
+ t.Skip("skipping on linux-arm64-buildlet; incompatible network config? issue 15191")
+ case "":
+ testenv.MustHaveExternalNetwork(t)
}
- onGoBuildFarm := testenv.Builder() != ""
- if testing.Short() && !onGoBuildFarm {
- t.Skip("skipping in short mode")
+
+ if runtime.GOOS == "nacl" {
+ // nacl doesn't have external network access.
+ t.Skipf("skipping on %s", runtime.GOOS)
}
blackholeIPPort := JoinHostPort(slowDst4, "1234")
diff --git a/src/net/dnsclient_unix.go b/src/net/dnsclient_unix.go
index cf0b2680db..3e31056a93 100644
--- a/src/net/dnsclient_unix.go
+++ b/src/net/dnsclient_unix.go
@@ -16,6 +16,7 @@
package net
import (
+ "context"
"errors"
"io"
"math/rand"
@@ -26,10 +27,10 @@ import (
// A dnsDialer provides dialing suitable for DNS queries.
type dnsDialer interface {
- dialDNS(string, string) (dnsConn, error)
+ dialDNS(ctx context.Context, network, addr string) (dnsConn, error)
}
-var testHookDNSDialer = func(d time.Duration) dnsDialer { return &Dialer{Timeout: d} }
+var testHookDNSDialer = func() dnsDialer { return &Dialer{} }
// A dnsConn represents a DNS transport endpoint.
type dnsConn interface {
@@ -37,46 +38,67 @@ type dnsConn interface {
SetDeadline(time.Time) error
- // readDNSResponse reads a DNS response message from the DNS
- // transport endpoint and returns the received DNS response
- // message.
- readDNSResponse() (*dnsMsg, error)
+ // dnsRoundTrip executes a single DNS transaction, returning a
+ // DNS response message for the provided DNS query message.
+ dnsRoundTrip(query *dnsMsg) (*dnsMsg, error)
+}
- // writeDNSQuery writes a DNS query message to the DNS
- // connection endpoint.
- writeDNSQuery(*dnsMsg) error
+func (c *UDPConn) dnsRoundTrip(query *dnsMsg) (*dnsMsg, error) {
+ return dnsRoundTripUDP(c, query)
}
-func (c *UDPConn) readDNSResponse() (*dnsMsg, error) {
- b := make([]byte, 512) // see RFC 1035
- n, err := c.Read(b)
- if err != nil {
+// dnsRoundTripUDP implements the dnsRoundTrip interface for RFC 1035's
+// "UDP usage" transport mechanism. c should be a packet-oriented connection,
+// such as a *UDPConn.
+func dnsRoundTripUDP(c io.ReadWriter, query *dnsMsg) (*dnsMsg, error) {
+ b, ok := query.Pack()
+ if !ok {
+ return nil, errors.New("cannot marshal DNS message")
+ }
+ if _, err := c.Write(b); err != nil {
return nil, err
}
- msg := &dnsMsg{}
- if !msg.Unpack(b[:n]) {
- return nil, errors.New("cannot unmarshal DNS message")
+
+ b = make([]byte, 512) // see RFC 1035
+ for {
+ n, err := c.Read(b)
+ if err != nil {
+ return nil, err
+ }
+ resp := &dnsMsg{}
+ if !resp.Unpack(b[:n]) || !resp.IsResponseTo(query) {
+ // Ignore invalid responses as they may be malicious
+ // forgery attempts. Instead continue waiting until
+ // timeout. See golang.org/issue/13281.
+ continue
+ }
+ return resp, nil
}
- return msg, nil
}
-func (c *UDPConn) writeDNSQuery(msg *dnsMsg) error {
- b, ok := msg.Pack()
+func (c *TCPConn) dnsRoundTrip(out *dnsMsg) (*dnsMsg, error) {
+ return dnsRoundTripTCP(c, out)
+}
+
+// dnsRoundTripTCP implements the dnsRoundTrip interface for RFC 1035's
+// "TCP usage" transport mechanism. c should be a stream-oriented connection,
+// such as a *TCPConn.
+func dnsRoundTripTCP(c io.ReadWriter, query *dnsMsg) (*dnsMsg, error) {
+ b, ok := query.Pack()
if !ok {
- return errors.New("cannot marshal DNS message")
+ return nil, errors.New("cannot marshal DNS message")
}
+ l := len(b)
+ b = append([]byte{byte(l >> 8), byte(l)}, b...)
if _, err := c.Write(b); err != nil {
- return err
+ return nil, err
}
- return nil
-}
-func (c *TCPConn) readDNSResponse() (*dnsMsg, error) {
- b := make([]byte, 1280) // 1280 is a reasonable initial size for IP over Ethernet, see RFC 4035
+ b = make([]byte, 1280) // 1280 is a reasonable initial size for IP over Ethernet, see RFC 4035
if _, err := io.ReadFull(c, b[:2]); err != nil {
return nil, err
}
- l := int(b[0])<<8 | int(b[1])
+ l = int(b[0])<<8 | int(b[1])
if l > len(b) {
b = make([]byte, l)
}
@@ -84,27 +106,17 @@ func (c *TCPConn) readDNSResponse() (*dnsMsg, error) {
if err != nil {
return nil, err
}
- msg := &dnsMsg{}
- if !msg.Unpack(b[:n]) {
+ resp := &dnsMsg{}
+ if !resp.Unpack(b[:n]) {
return nil, errors.New("cannot unmarshal DNS message")
}
- return msg, nil
-}
-
-func (c *TCPConn) writeDNSQuery(msg *dnsMsg) error {
- b, ok := msg.Pack()
- if !ok {
- return errors.New("cannot marshal DNS message")
- }
- l := uint16(len(b))
- b = append([]byte{byte(l >> 8), byte(l)}, b...)
- if _, err := c.Write(b); err != nil {
- return err
+ if !resp.IsResponseTo(query) {
+ return nil, errors.New("invalid DNS response")
}
- return nil
+ return resp, nil
}
-func (d *Dialer) dialDNS(network, server string) (dnsConn, error) {
+func (d *Dialer) dialDNS(ctx context.Context, network, server string) (dnsConn, error) {
switch network {
case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6":
default:
@@ -115,9 +127,9 @@ func (d *Dialer) dialDNS(network, server string) (dnsConn, error) {
// call back here to translate it. The DNS config parser has
// already checked that all the cfg.servers[i] are IP
// addresses, which Dial will use without a DNS lookup.
- c, err := d.Dial(network, server)
+ c, err := d.DialContext(ctx, network, server)
if err != nil {
- return nil, err
+ return nil, mapErr(err)
}
switch network {
case "tcp", "tcp4", "tcp6":
@@ -129,8 +141,8 @@ func (d *Dialer) dialDNS(network, server string) (dnsConn, error) {
}
// exchange sends a query on the connection and hopes for a response.
-func exchange(server, name string, qtype uint16, timeout time.Duration) (*dnsMsg, error) {
- d := testHookDNSDialer(timeout)
+func exchange(ctx context.Context, server, name string, qtype uint16) (*dnsMsg, error) {
+ d := testHookDNSDialer()
out := dnsMsg{
dnsMsgHdr: dnsMsgHdr{
recursion_desired: true,
@@ -140,24 +152,18 @@ func exchange(server, name string, qtype uint16, timeout time.Duration) (*dnsMsg
},
}
for _, network := range []string{"udp", "tcp"} {
- c, err := d.dialDNS(network, server)
+ c, err := d.dialDNS(ctx, network, server)
if err != nil {
return nil, err
}
defer c.Close()
- if timeout > 0 {
- c.SetDeadline(time.Now().Add(timeout))
+ if d, ok := ctx.Deadline(); ok && !d.IsZero() {
+ c.SetDeadline(d)
}
out.id = uint16(rand.Int()) ^ uint16(time.Now().UnixNano())
- if err := c.writeDNSQuery(&out); err != nil {
- return nil, err
- }
- in, err := c.readDNSResponse()
+ in, err := c.dnsRoundTrip(&out)
if err != nil {
- return nil, err
- }
- if in.id != out.id {
- return nil, errors.New("DNS message ID mismatch")
+ return nil, mapErr(err)
}
if in.truncated { // see RFC 5966
continue
@@ -169,16 +175,24 @@ func exchange(server, name string, qtype uint16, timeout time.Duration) (*dnsMsg
// Do a lookup for a single name, which must be rooted
// (otherwise answer will not find the answers).
-func tryOneName(cfg *dnsConfig, name string, qtype uint16) (string, []dnsRR, error) {
+func tryOneName(ctx context.Context, cfg *dnsConfig, name string, qtype uint16) (string, []dnsRR, error) {
if len(cfg.servers) == 0 {
return "", nil, &DNSError{Err: "no DNS servers", Name: name}
}
+
timeout := time.Duration(cfg.timeout) * time.Second
+ deadline := time.Now().Add(timeout)
+ if old, ok := ctx.Deadline(); !ok || deadline.Before(old) {
+ var cancel context.CancelFunc
+ ctx, cancel = context.WithDeadline(ctx, deadline)
+ defer cancel()
+ }
+
var lastErr error
for i := 0; i < cfg.attempts; i++ {
for _, server := range cfg.servers {
server = JoinHostPort(server, "53")
- msg, err := exchange(server, name, qtype, timeout)
+ msg, err := exchange(ctx, server, name, qtype)
if err != nil {
lastErr = &DNSError{
Err: err.Error(),
@@ -190,6 +204,12 @@ func tryOneName(cfg *dnsConfig, name string, qtype uint16) (string, []dnsRR, err
}
continue
}
+ // libresolv continues to the next server when it receives
+ // an invalid referral response. See golang.org/issue/15434.
+ if msg.rcode == dnsRcodeSuccess && !msg.authoritative && !msg.recursion_available && len(msg.answer) == 0 && len(msg.extra) == 0 {
+ lastErr = &DNSError{Err: "lame referral", Name: name, Server: server}
+ continue
+ }
cname, rrs, err := answer(name, server, msg, qtype)
// If answer errored for rcodes dnsRcodeSuccess or dnsRcodeNameError,
// it means the response in msg was not useful and trying another
@@ -296,7 +316,7 @@ func (conf *resolverConfig) releaseSema() {
<-conf.ch
}
-func lookup(name string, qtype uint16) (cname string, rrs []dnsRR, err error) {
+func lookup(ctx context.Context, name string, qtype uint16) (cname string, rrs []dnsRR, err error) {
if !isDomainName(name) {
return "", nil, &DNSError{Err: "invalid domain name", Name: name}
}
@@ -305,7 +325,7 @@ func lookup(name string, qtype uint16) (cname string, rrs []dnsRR, err error) {
conf := resolvConf.dnsConfig
resolvConf.mu.RUnlock()
for _, fqdn := range conf.nameList(name) {
- cname, rrs, err = tryOneName(conf, fqdn, qtype)
+ cname, rrs, err = tryOneName(ctx, conf, fqdn, qtype)
if err == nil {
break
}
@@ -399,11 +419,11 @@ func (o hostLookupOrder) String() string {
// Normally we let cgo use the C library resolver instead of
// depending on our lookup code, so that Go and C get the same
// answers.
-func goLookupHost(name string) (addrs []string, err error) {
- return goLookupHostOrder(name, hostLookupFilesDNS)
+func goLookupHost(ctx context.Context, name string) (addrs []string, err error) {
+ return goLookupHostOrder(ctx, name, hostLookupFilesDNS)
}
-func goLookupHostOrder(name string, order hostLookupOrder) (addrs []string, err error) {
+func goLookupHostOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []string, err error) {
if order == hostLookupFilesDNS || order == hostLookupFiles {
// Use entries from /etc/hosts if they match.
addrs = lookupStaticHost(name)
@@ -411,7 +431,7 @@ func goLookupHostOrder(name string, order hostLookupOrder) (addrs []string, err
return
}
}
- ips, err := goLookupIPOrder(name, order)
+ ips, err := goLookupIPOrder(ctx, name, order)
if err != nil {
return
}
@@ -437,11 +457,11 @@ func goLookupIPFiles(name string) (addrs []IPAddr) {
// goLookupIP is the native Go implementation of LookupIP.
// The libc versions are in cgo_*.go.
-func goLookupIP(name string) (addrs []IPAddr, err error) {
- return goLookupIPOrder(name, hostLookupFilesDNS)
+func goLookupIP(ctx context.Context, name string) (addrs []IPAddr, err error) {
+ return goLookupIPOrder(ctx, name, hostLookupFilesDNS)
}
-func goLookupIPOrder(name string, order hostLookupOrder) (addrs []IPAddr, err error) {
+func goLookupIPOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []IPAddr, err error) {
if order == hostLookupFilesDNS || order == hostLookupFiles {
addrs = goLookupIPFiles(name)
if len(addrs) > 0 || order == hostLookupFiles {
@@ -466,7 +486,7 @@ func goLookupIPOrder(name string, order hostLookupOrder) (addrs []IPAddr, err er
for _, fqdn := range conf.nameList(name) {
for _, qtype := range qtypes {
go func(qtype uint16) {
- _, rrs, err := tryOneName(conf, fqdn, qtype)
+ _, rrs, err := tryOneName(ctx, conf, fqdn, qtype)
lane <- racer{fqdn, rrs, err}
}(qtype)
}
@@ -509,8 +529,8 @@ func goLookupIPOrder(name string, order hostLookupOrder) (addrs []IPAddr, err er
// Normally we let cgo use the C library resolver instead of
// depending on our lookup code, so that Go and C get the same
// answers.
-func goLookupCNAME(name string) (cname string, err error) {
- _, rrs, err := lookup(name, dnsTypeCNAME)
+func goLookupCNAME(ctx context.Context, name string) (cname string, err error) {
+ _, rrs, err := lookup(ctx, name, dnsTypeCNAME)
if err != nil {
return
}
@@ -523,7 +543,7 @@ func goLookupCNAME(name string) (cname string, err error) {
// only if cgoLookupPTR is the stub in cgo_stub.go).
// Normally we let cgo use the C library resolver instead of depending
// on our lookup code, so that Go and C get the same answers.
-func goLookupPTR(addr string) ([]string, error) {
+func goLookupPTR(ctx context.Context, addr string) ([]string, error) {
names := lookupStaticAddr(addr)
if len(names) > 0 {
return names, nil
@@ -532,7 +552,7 @@ func goLookupPTR(addr string) ([]string, error) {
if err != nil {
return nil, err
}
- _, rrs, err := lookup(arpa, dnsTypePTR)
+ _, rrs, err := lookup(ctx, arpa, dnsTypePTR)
if err != nil {
return nil, err
}
diff --git a/src/net/dnsclient_unix_test.go b/src/net/dnsclient_unix_test.go
index 6845481e17..b4aacef54f 100644
--- a/src/net/dnsclient_unix_test.go
+++ b/src/net/dnsclient_unix_test.go
@@ -7,7 +7,9 @@
package net
import (
+ "context"
"fmt"
+ "internal/testenv"
"io/ioutil"
"os"
"path"
@@ -18,6 +20,9 @@ import (
"time"
)
+// Test address from 192.0.2.0/24 block, reserved by RFC 5737 for documentation.
+const TestAddr uint32 = 0xc0000201
+
var dnsTransportFallbackTests = []struct {
server string
name string
@@ -32,13 +37,12 @@ var dnsTransportFallbackTests = []struct {
}
func TestDNSTransportFallback(t *testing.T) {
- if testing.Short() || !*testExternal {
- t.Skip("avoid external network")
- }
+ testenv.MustHaveExternalNetwork(t)
for _, tt := range dnsTransportFallbackTests {
- timeout := time.Duration(tt.timeout) * time.Second
- msg, err := exchange(tt.server, tt.name, tt.qtype, timeout)
+ ctx, cancel := context.WithTimeout(context.Background(), time.Duration(tt.timeout)*time.Second)
+ defer cancel()
+ msg, err := exchange(ctx, tt.server, tt.name, tt.qtype)
if err != nil {
t.Error(err)
continue
@@ -74,13 +78,13 @@ var specialDomainNameTests = []struct {
}
func TestSpecialDomainName(t *testing.T) {
- if testing.Short() || !*testExternal {
- t.Skip("avoid external network")
- }
+ testenv.MustHaveExternalNetwork(t)
server := "8.8.8.8:53"
for _, tt := range specialDomainNameTests {
- msg, err := exchange(server, tt.name, tt.qtype, 3*time.Second)
+ ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
+ defer cancel()
+ msg, err := exchange(ctx, server, tt.name, tt.qtype)
if err != nil {
t.Error(err)
continue
@@ -136,7 +140,7 @@ func TestAvoidDNSName(t *testing.T) {
// Issue 13705: don't try to resolve onion addresses, etc
func TestLookupTorOnion(t *testing.T) {
- addrs, err := goLookupIP("foo.onion")
+ addrs, err := goLookupIP(context.Background(), "foo.onion")
if len(addrs) > 0 {
t.Errorf("unexpected addresses: %v", addrs)
}
@@ -232,9 +236,7 @@ var updateResolvConfTests = []struct {
}
func TestUpdateResolvConf(t *testing.T) {
- if testing.Short() || !*testExternal {
- t.Skip("avoid external network")
- }
+ testenv.MustHaveExternalNetwork(t)
conf, err := newResolvConfTest()
if err != nil {
@@ -254,7 +256,7 @@ func TestUpdateResolvConf(t *testing.T) {
for j := 0; j < N; j++ {
go func(name string) {
defer wg.Done()
- ips, err := goLookupIP(name)
+ ips, err := goLookupIP(context.Background(), name)
if err != nil {
t.Error(err)
return
@@ -389,9 +391,7 @@ var goLookupIPWithResolverConfigTests = []struct {
}
func TestGoLookupIPWithResolverConfig(t *testing.T) {
- if testing.Short() || !*testExternal {
- t.Skip("avoid external network")
- }
+ testenv.MustHaveExternalNetwork(t)
conf, err := newResolvConfTest()
if err != nil {
@@ -404,7 +404,7 @@ func TestGoLookupIPWithResolverConfig(t *testing.T) {
t.Error(err)
continue
}
- addrs, err := goLookupIP(tt.name)
+ addrs, err := goLookupIP(context.Background(), tt.name)
if err != nil {
// This test uses external network connectivity.
// We need to take care with errors on both
@@ -436,9 +436,7 @@ func TestGoLookupIPWithResolverConfig(t *testing.T) {
// Test that goLookupIPOrder falls back to the host file when no DNS servers are available.
func TestGoLookupIPOrderFallbackToFile(t *testing.T) {
- if testing.Short() || !*testExternal {
- t.Skip("avoid external network")
- }
+ testenv.MustHaveExternalNetwork(t)
// Add a config that simulates no dns servers being available.
conf, err := newResolvConfTest()
@@ -456,14 +454,14 @@ func TestGoLookupIPOrderFallbackToFile(t *testing.T) {
name := fmt.Sprintf("order %v", order)
// First ensure that we get an error when contacting a non-existent host.
- _, err := goLookupIPOrder("notarealhost", order)
+ _, err := goLookupIPOrder(context.Background(), "notarealhost", order)
if err == nil {
t.Errorf("%s: expected error while looking up name not in hosts file", name)
continue
}
// Now check that we get an address when the name appears in the hosts file.
- addrs, err := goLookupIPOrder("thor", order) // entry is in "testdata/hosts"
+ addrs, err := goLookupIPOrder(context.Background(), "thor", order) // entry is in "testdata/hosts"
if err != nil {
t.Errorf("%s: expected to successfully lookup host entry", name)
continue
@@ -499,10 +497,10 @@ func TestErrorForOriginalNameWhenSearching(t *testing.T) {
t.Fatal(err)
}
- d := &fakeDNSConn{}
- testHookDNSDialer = func(time.Duration) dnsDialer { return d }
+ d := &fakeDNSDialer{}
+ testHookDNSDialer = func() dnsDialer { return d }
- d.rh = func(q *dnsMsg) (*dnsMsg, error) {
+ d.rh = func(s string, q *dnsMsg) (*dnsMsg, error) {
r := &dnsMsg{
dnsMsgHdr: dnsMsgHdr{
id: q.id,
@@ -519,7 +517,7 @@ func TestErrorForOriginalNameWhenSearching(t *testing.T) {
return r, nil
}
- _, err = goLookupIP(fqdn)
+ _, err = goLookupIP(context.Background(), fqdn)
if err == nil {
t.Fatal("expected an error")
}
@@ -530,19 +528,83 @@ func TestErrorForOriginalNameWhenSearching(t *testing.T) {
}
}
+// Issue 15434. If a name server gives a lame referral, continue to the next.
+func TestIgnoreLameReferrals(t *testing.T) {
+ origTestHookDNSDialer := testHookDNSDialer
+ defer func() { testHookDNSDialer = origTestHookDNSDialer }()
+
+ conf, err := newResolvConfTest()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer conf.teardown()
+
+ if err := conf.writeAndUpdate([]string{"nameserver 192.0.2.1", "nameserver 192.0.2.2"}); err != nil {
+ t.Fatal(err)
+ }
+
+ d := &fakeDNSDialer{}
+ testHookDNSDialer = func() dnsDialer { return d }
+
+ d.rh = func(s string, q *dnsMsg) (*dnsMsg, error) {
+ t.Log(s, q)
+ r := &dnsMsg{
+ dnsMsgHdr: dnsMsgHdr{
+ id: q.id,
+ response: true,
+ },
+ question: q.question,
+ }
+
+ if s == "192.0.2.2:53" {
+ r.recursion_available = true
+ if q.question[0].Qtype == dnsTypeA {
+ r.answer = []dnsRR{
+ &dnsRR_A{
+ Hdr: dnsRR_Header{
+ Name: q.question[0].Name,
+ Rrtype: dnsTypeA,
+ Class: dnsClassINET,
+ Rdlength: 4,
+ },
+ A: TestAddr,
+ },
+ }
+ }
+ }
+
+ return r, nil
+ }
+
+ addrs, err := goLookupIP(context.Background(), "www.golang.org")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if got := len(addrs); got != 1 {
+ t.Fatal("got %d addresses, want 1", got)
+ }
+
+ if got, want := addrs[0].String(), "192.0.2.1"; got != want {
+ t.Fatal("got address %v, want %v", got, want)
+ }
+}
+
func BenchmarkGoLookupIP(b *testing.B) {
testHookUninstaller.Do(uninstallTestHooks)
+ ctx := context.Background()
for i := 0; i < b.N; i++ {
- goLookupIP("www.example.com")
+ goLookupIP(ctx, "www.example.com")
}
}
func BenchmarkGoLookupIPNoSuchHost(b *testing.B) {
testHookUninstaller.Do(uninstallTestHooks)
+ ctx := context.Background()
for i := 0; i < b.N; i++ {
- goLookupIP("some.nonexistent")
+ goLookupIP(ctx, "some.nonexistent")
}
}
@@ -562,22 +624,25 @@ func BenchmarkGoLookupIPWithBrokenNameServer(b *testing.B) {
if err := conf.writeAndUpdate(lines); err != nil {
b.Fatal(err)
}
+ ctx := context.Background()
for i := 0; i < b.N; i++ {
- goLookupIP("www.example.com")
+ goLookupIP(ctx, "www.example.com")
}
}
-type fakeDNSConn struct {
- // last query
- qmu sync.Mutex // guards q
- q *dnsMsg
+type fakeDNSDialer struct {
// reply handler
- rh func(*dnsMsg) (*dnsMsg, error)
+ rh func(s string, q *dnsMsg) (*dnsMsg, error)
}
-func (f *fakeDNSConn) dialDNS(n, s string) (dnsConn, error) {
- return f, nil
+func (f *fakeDNSDialer) dialDNS(_ context.Context, n, s string) (dnsConn, error) {
+ return &fakeDNSConn{f.rh, s}, nil
+}
+
+type fakeDNSConn struct {
+ rh func(s string, q *dnsMsg) (*dnsMsg, error)
+ s string
}
func (f *fakeDNSConn) Close() error {
@@ -588,16 +653,74 @@ func (f *fakeDNSConn) SetDeadline(time.Time) error {
return nil
}
-func (f *fakeDNSConn) writeDNSQuery(q *dnsMsg) error {
- f.qmu.Lock()
- defer f.qmu.Unlock()
- f.q = q
- return nil
+func (f *fakeDNSConn) dnsRoundTrip(q *dnsMsg) (*dnsMsg, error) {
+ return f.rh(f.s, q)
}
-func (f *fakeDNSConn) readDNSResponse() (*dnsMsg, error) {
- f.qmu.Lock()
- q := f.q
- f.qmu.Unlock()
- return f.rh(q)
+// UDP round-tripper algorithm should ignore invalid DNS responses (issue 13281).
+func TestIgnoreDNSForgeries(t *testing.T) {
+ c, s := Pipe()
+ go func() {
+ b := make([]byte, 512)
+ n, err := s.Read(b)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ msg := &dnsMsg{}
+ if !msg.Unpack(b[:n]) {
+ t.Fatal("invalid DNS query")
+ }
+
+ s.Write([]byte("garbage DNS response packet"))
+
+ msg.response = true
+ msg.id++ // make invalid ID
+ b, ok := msg.Pack()
+ if !ok {
+ t.Fatal("failed to pack DNS response")
+ }
+ s.Write(b)
+
+ msg.id-- // restore original ID
+ msg.answer = []dnsRR{
+ &dnsRR_A{
+ Hdr: dnsRR_Header{
+ Name: "www.example.com.",
+ Rrtype: dnsTypeA,
+ Class: dnsClassINET,
+ Rdlength: 4,
+ },
+ A: TestAddr,
+ },
+ }
+
+ b, ok = msg.Pack()
+ if !ok {
+ t.Fatal("failed to pack DNS response")
+ }
+ s.Write(b)
+ }()
+
+ msg := &dnsMsg{
+ dnsMsgHdr: dnsMsgHdr{
+ id: 42,
+ },
+ question: []dnsQuestion{
+ {
+ Name: "www.example.com.",
+ Qtype: dnsTypeA,
+ Qclass: dnsClassINET,
+ },
+ },
+ }
+
+ resp, err := dnsRoundTripUDP(c, msg)
+ if err != nil {
+ t.Fatalf("dnsRoundTripUDP failed: %v", err)
+ }
+
+ if got := resp.answer[0].(*dnsRR_A).A; got != TestAddr {
+ t.Error("got address %v, want %v", got, TestAddr)
+ }
}
diff --git a/src/net/dnsconfig_unix.go b/src/net/dnsconfig_unix.go
index 181d47b36d..9893cb7e63 100644
--- a/src/net/dnsconfig_unix.go
+++ b/src/net/dnsconfig_unix.go
@@ -8,9 +8,15 @@
package net
-import "time"
+import (
+ "os"
+ "time"
+)
-var defaultNS = []string{"127.0.0.1", "::1"}
+var (
+ defaultNS = []string{"127.0.0.1", "::1"}
+ getHostname = os.Hostname // variable for testing
+)
type dnsConfig struct {
servers []string // servers to use
@@ -26,8 +32,6 @@ type dnsConfig struct {
}
// See resolv.conf(5) on a Linux machine.
-// TODO(rsc): Supposed to call uname() and chop the beginning
-// of the host name to get the default search domain.
func dnsReadConfig(filename string) *dnsConfig {
conf := &dnsConfig{
ndots: 1,
@@ -37,6 +41,7 @@ func dnsReadConfig(filename string) *dnsConfig {
file, err := open(filename)
if err != nil {
conf.servers = defaultNS
+ conf.search = dnsDefaultSearch()
conf.err = err
return conf
}
@@ -45,6 +50,7 @@ func dnsReadConfig(filename string) *dnsConfig {
conf.mtime = fi.ModTime()
} else {
conf.servers = defaultNS
+ conf.search = dnsDefaultSearch()
conf.err = err
return conf
}
@@ -122,9 +128,24 @@ func dnsReadConfig(filename string) *dnsConfig {
if len(conf.servers) == 0 {
conf.servers = defaultNS
}
+ if len(conf.search) == 0 {
+ conf.search = dnsDefaultSearch()
+ }
return conf
}
+func dnsDefaultSearch() []string {
+ hn, err := getHostname()
+ if err != nil {
+ // best effort
+ return nil
+ }
+ if i := byteIndex(hn, '.'); i >= 0 && i < len(hn)-1 {
+ return []string{hn[i+1:]}
+ }
+ return nil
+}
+
func hasPrefix(s, prefix string) bool {
return len(s) >= len(prefix) && s[:len(prefix)] == prefix
}
diff --git a/src/net/dnsconfig_unix_test.go b/src/net/dnsconfig_unix_test.go
index 849c0da93b..f9ef79cba8 100644
--- a/src/net/dnsconfig_unix_test.go
+++ b/src/net/dnsconfig_unix_test.go
@@ -7,6 +7,7 @@
package net
import (
+ "errors"
"os"
"reflect"
"testing"
@@ -56,6 +57,7 @@ var dnsReadConfigTests = []struct {
ndots: 1,
timeout: 5,
attempts: 2,
+ search: []string{"domain.local"},
},
},
{
@@ -72,6 +74,10 @@ var dnsReadConfigTests = []struct {
}
func TestDNSReadConfig(t *testing.T) {
+ origGetHostname := getHostname
+ defer func() { getHostname = origGetHostname }()
+ getHostname = func() (string, error) { return "host.domain.local", nil }
+
for _, tt := range dnsReadConfigTests {
conf := dnsReadConfig(tt.name)
if conf.err != nil {
@@ -85,6 +91,10 @@ func TestDNSReadConfig(t *testing.T) {
}
func TestDNSReadMissingFile(t *testing.T) {
+ origGetHostname := getHostname
+ defer func() { getHostname = origGetHostname }()
+ getHostname = func() (string, error) { return "host.domain.local", nil }
+
conf := dnsReadConfig("a-nonexistent-file")
if !os.IsNotExist(conf.err) {
t.Errorf("missing resolv.conf:\ngot: %v\nwant: %v", conf.err, os.ErrNotExist)
@@ -95,8 +105,52 @@ func TestDNSReadMissingFile(t *testing.T) {
ndots: 1,
timeout: 5,
attempts: 2,
+ search: []string{"domain.local"},
}
if !reflect.DeepEqual(conf, want) {
t.Errorf("missing resolv.conf:\ngot: %+v\nwant: %+v", conf, want)
}
}
+
+var dnsDefaultSearchTests = []struct {
+ name string
+ err error
+ want []string
+}{
+ {
+ name: "host.long.domain.local",
+ want: []string{"long.domain.local"},
+ },
+ {
+ name: "host.local",
+ want: []string{"local"},
+ },
+ {
+ name: "host",
+ want: nil,
+ },
+ {
+ name: "host.domain.local",
+ err: errors.New("errored"),
+ want: nil,
+ },
+ {
+ // ensures we don't return []string{""}
+ // which causes duplicate lookups
+ name: "foo.",
+ want: nil,
+ },
+}
+
+func TestDNSDefaultSearch(t *testing.T) {
+ origGetHostname := getHostname
+ defer func() { getHostname = origGetHostname }()
+
+ for _, tt := range dnsDefaultSearchTests {
+ getHostname = func() (string, error) { return tt.name, tt.err }
+ got := dnsDefaultSearch()
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("dnsDefaultSearch with hostname %q and error %+v = %q, wanted %q", tt.name, tt.err, got, tt.want)
+ }
+ }
+}
diff --git a/src/net/dnsmsg.go b/src/net/dnsmsg.go
index c01381f190..5e339c5fbf 100644
--- a/src/net/dnsmsg.go
+++ b/src/net/dnsmsg.go
@@ -934,3 +934,23 @@ func (dns *dnsMsg) String() string {
}
return s
}
+
+// IsResponseTo reports whether m is an acceptable response to query.
+func (m *dnsMsg) IsResponseTo(query *dnsMsg) bool {
+ if !m.response {
+ return false
+ }
+ if m.id != query.id {
+ return false
+ }
+ if len(m.question) != len(query.question) {
+ return false
+ }
+ for i, q := range m.question {
+ q2 := query.question[i]
+ if !equalASCIILabel(q.Name, q2.Name) || q.Qtype != q2.Qtype || q.Qclass != q2.Qclass {
+ return false
+ }
+ }
+ return true
+}
diff --git a/src/net/dnsmsg_test.go b/src/net/dnsmsg_test.go
index 841c32fa84..25bd98cff7 100644
--- a/src/net/dnsmsg_test.go
+++ b/src/net/dnsmsg_test.go
@@ -280,6 +280,124 @@ func TestDNSParseTXTCorruptTXTLengthReply(t *testing.T) {
}
}
+func TestIsResponseTo(t *testing.T) {
+ // Sample DNS query.
+ query := dnsMsg{
+ dnsMsgHdr: dnsMsgHdr{
+ id: 42,
+ },
+ question: []dnsQuestion{
+ {
+ Name: "www.example.com.",
+ Qtype: dnsTypeA,
+ Qclass: dnsClassINET,
+ },
+ },
+ }
+
+ resp := query
+ resp.response = true
+ if !resp.IsResponseTo(&query) {
+ t.Error("got false, want true")
+ }
+
+ badResponses := []dnsMsg{
+ // Different ID.
+ {
+ dnsMsgHdr: dnsMsgHdr{
+ id: 43,
+ response: true,
+ },
+ question: []dnsQuestion{
+ {
+ Name: "www.example.com.",
+ Qtype: dnsTypeA,
+ Qclass: dnsClassINET,
+ },
+ },
+ },
+
+ // Different query name.
+ {
+ dnsMsgHdr: dnsMsgHdr{
+ id: 42,
+ response: true,
+ },
+ question: []dnsQuestion{
+ {
+ Name: "www.google.com.",
+ Qtype: dnsTypeA,
+ Qclass: dnsClassINET,
+ },
+ },
+ },
+
+ // Different query type.
+ {
+ dnsMsgHdr: dnsMsgHdr{
+ id: 42,
+ response: true,
+ },
+ question: []dnsQuestion{
+ {
+ Name: "www.example.com.",
+ Qtype: dnsTypeAAAA,
+ Qclass: dnsClassINET,
+ },
+ },
+ },
+
+ // Different query class.
+ {
+ dnsMsgHdr: dnsMsgHdr{
+ id: 42,
+ response: true,
+ },
+ question: []dnsQuestion{
+ {
+ Name: "www.example.com.",
+ Qtype: dnsTypeA,
+ Qclass: dnsClassCSNET,
+ },
+ },
+ },
+
+ // No questions.
+ {
+ dnsMsgHdr: dnsMsgHdr{
+ id: 42,
+ response: true,
+ },
+ },
+
+ // Extra questions.
+ {
+ dnsMsgHdr: dnsMsgHdr{
+ id: 42,
+ response: true,
+ },
+ question: []dnsQuestion{
+ {
+ Name: "www.example.com.",
+ Qtype: dnsTypeA,
+ Qclass: dnsClassINET,
+ },
+ {
+ Name: "www.golang.org.",
+ Qtype: dnsTypeAAAA,
+ Qclass: dnsClassINET,
+ },
+ },
+ },
+ }
+
+ for i := range badResponses {
+ if badResponses[i].IsResponseTo(&query) {
+ t.Error("%v: got true, want false", i)
+ }
+ }
+}
+
// Valid DNS SRV reply
const dnsSRVReply = "0901818000010005000000000c5f786d70702d736572766572045f74637006676f6f67" +
"6c6503636f6d0000210001c00c002100010000012c00210014000014950c786d70702d" +
diff --git a/src/net/dnsname_test.go b/src/net/dnsname_test.go
index be07dc6a16..bc777b855e 100644
--- a/src/net/dnsname_test.go
+++ b/src/net/dnsname_test.go
@@ -15,7 +15,7 @@ type dnsNameTest struct {
}
var dnsNameTests = []dnsNameTest{
- // RFC2181, section 11.
+ // RFC 2181, section 11.
{"_xmpp-server._tcp.google.com", true},
{"foo.com", true},
{"1foo.com", true},
diff --git a/src/net/error_test.go b/src/net/error_test.go
index c3a4d32382..9f496d7d2d 100644
--- a/src/net/error_test.go
+++ b/src/net/error_test.go
@@ -5,6 +5,7 @@
package net
import (
+ "context"
"fmt"
"io"
"io/ioutil"
@@ -138,7 +139,7 @@ func TestDialError(t *testing.T) {
origTestHookLookupIP := testHookLookupIP
defer func() { testHookLookupIP = origTestHookLookupIP }()
- testHookLookupIP = func(fn func(string) ([]IPAddr, error), host string) ([]IPAddr, error) {
+ testHookLookupIP = func(ctx context.Context, fn func(context.Context, string) ([]IPAddr, error), host string) ([]IPAddr, error) {
return nil, &DNSError{Err: "dial error test", Name: "name", Server: "server", IsTimeout: true}
}
sw.Set(socktest.FilterConnect, func(so *socktest.Status) (socktest.AfterFilter, error) {
@@ -206,6 +207,58 @@ func TestProtocolDialError(t *testing.T) {
}
}
+func TestDialAddrError(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if !supportsIPv4 || !supportsIPv6 {
+ t.Skip("both IPv4 and IPv6 are required")
+ }
+
+ for _, tt := range []struct {
+ network string
+ lit string
+ addr *TCPAddr
+ }{
+ {"tcp4", "::1", nil},
+ {"tcp4", "", &TCPAddr{IP: IPv6loopback}},
+ // We don't test the {"tcp6", "byte sequence", nil}
+ // case for now because there is no easy way to
+ // control name resolution.
+ {"tcp6", "", &TCPAddr{IP: IP{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}}},
+ } {
+ var err error
+ var c Conn
+ if tt.lit != "" {
+ c, err = Dial(tt.network, JoinHostPort(tt.lit, "0"))
+ } else {
+ c, err = DialTCP(tt.network, nil, tt.addr)
+ }
+ if err == nil {
+ c.Close()
+ t.Errorf("%s %q/%v: should fail", tt.network, tt.lit, tt.addr)
+ continue
+ }
+ if perr := parseDialError(err); perr != nil {
+ t.Error(perr)
+ continue
+ }
+ aerr, ok := err.(*OpError).Err.(*AddrError)
+ if !ok {
+ t.Errorf("%s %q/%v: should be AddrError: %v", tt.network, tt.lit, tt.addr, err)
+ continue
+ }
+ want := tt.lit
+ if tt.lit == "" {
+ want = tt.addr.IP.String()
+ }
+ if aerr.Addr != want {
+ t.Fatalf("%s: got %q; want %q", tt.network, aerr.Addr, want)
+ }
+ }
+}
+
var listenErrorTests = []struct {
network, address string
}{
@@ -231,7 +284,7 @@ func TestListenError(t *testing.T) {
origTestHookLookupIP := testHookLookupIP
defer func() { testHookLookupIP = origTestHookLookupIP }()
- testHookLookupIP = func(fn func(string) ([]IPAddr, error), host string) ([]IPAddr, error) {
+ testHookLookupIP = func(_ context.Context, fn func(context.Context, string) ([]IPAddr, error), host string) ([]IPAddr, error) {
return nil, &DNSError{Err: "listen error test", Name: "name", Server: "server", IsTimeout: true}
}
sw.Set(socktest.FilterListen, func(so *socktest.Status) (socktest.AfterFilter, error) {
@@ -291,7 +344,7 @@ func TestListenPacketError(t *testing.T) {
origTestHookLookupIP := testHookLookupIP
defer func() { testHookLookupIP = origTestHookLookupIP }()
- testHookLookupIP = func(fn func(string) ([]IPAddr, error), host string) ([]IPAddr, error) {
+ testHookLookupIP = func(_ context.Context, fn func(context.Context, string) ([]IPAddr, error), host string) ([]IPAddr, error) {
return nil, &DNSError{Err: "listen error test", Name: "name", Server: "server", IsTimeout: true}
}
diff --git a/src/net/external_test.go b/src/net/external_test.go
index d5ff2be20a..e18b547cac 100644
--- a/src/net/external_test.go
+++ b/src/net/external_test.go
@@ -6,15 +6,15 @@ package net
import (
"fmt"
+ "internal/testenv"
"io"
"strings"
"testing"
)
func TestResolveGoogle(t *testing.T) {
- if testing.Short() || !*testExternal {
- t.Skip("avoid external network")
- }
+ testenv.MustHaveExternalNetwork(t)
+
if !supportsIPv4 || !supportsIPv6 || !*testIPv4 || !*testIPv6 {
t.Skip("both IPv4 and IPv6 are required")
}
@@ -60,9 +60,8 @@ var dialGoogleTests = []struct {
}
func TestDialGoogle(t *testing.T) {
- if testing.Short() || !*testExternal {
- t.Skip("avoid external network")
- }
+ testenv.MustHaveExternalNetwork(t)
+
if !supportsIPv4 || !supportsIPv6 || !*testIPv4 || !*testIPv6 {
t.Skip("both IPv4 and IPv6 are required")
}
diff --git a/src/net/fd_plan9.go b/src/net/fd_plan9.go
index d0e9c53fca..329d6152b2 100644
--- a/src/net/fd_plan9.go
+++ b/src/net/fd_plan9.go
@@ -32,12 +32,6 @@ func sysInit() {
netdir = "/net"
}
-func dial(net string, ra Addr, dialer func(time.Time) (Conn, error), deadline time.Time) (Conn, error) {
- // On plan9, use the relatively inefficient
- // goroutine-racing implementation.
- return dialChannel(net, ra, dialer, deadline)
-}
-
func newFD(net, name string, ctl, data *os.File, laddr, raddr Addr) (*netFD, error) {
return &netFD{net: net, n: name, dir: netdir + "/" + net + "/" + name, ctl: ctl, data: data, laddr: laddr, raddr: raddr}, nil
}
@@ -83,6 +77,9 @@ func (fd *netFD) Read(b []byte) (n int, err error) {
}
defer fd.readUnlock()
n, err = fd.data.Read(b)
+ if isHangup(err) {
+ err = io.EOF
+ }
if fd.net == "udp" && err == io.EOF {
n = 0
err = nil
@@ -185,3 +182,7 @@ func setReadBuffer(fd *netFD, bytes int) error {
func setWriteBuffer(fd *netFD, bytes int) error {
return syscall.EPLAN9
}
+
+func isHangup(err error) bool {
+ return err != nil && stringsHasSuffix(err.Error(), "Hangup")
+}
diff --git a/src/net/fd_unix.go b/src/net/fd_unix.go
index d47b4bef99..7ef10702ed 100644
--- a/src/net/fd_unix.go
+++ b/src/net/fd_unix.go
@@ -7,12 +7,12 @@
package net
import (
+ "context"
"io"
"os"
"runtime"
"sync/atomic"
"syscall"
- "time"
)
// Network file descriptor.
@@ -36,10 +36,6 @@ type netFD struct {
func sysInit() {
}
-func dial(network string, ra Addr, dialer func(time.Time) (Conn, error), deadline time.Time) (Conn, error) {
- return dialer(deadline)
-}
-
func newFD(sysfd, family, sotype int, net string) (*netFD, error) {
return &netFD{sysfd: sysfd, family: family, sotype: sotype, net: net}, nil
}
@@ -68,15 +64,17 @@ func (fd *netFD) name() string {
return fd.net + ":" + ls + "->" + rs
}
-func (fd *netFD) connect(la, ra syscall.Sockaddr, deadline time.Time, cancel <-chan struct{}) error {
+func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) error {
// Do not need to call fd.writeLock here,
// because fd is not yet accessible to user,
// so no concurrent operations are possible.
switch err := connectFunc(fd.sysfd, ra); err {
case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
case nil, syscall.EISCONN:
- if !deadline.IsZero() && deadline.Before(time.Now()) {
- return errTimeout
+ select {
+ case <-ctx.Done():
+ return mapErr(ctx.Err())
+ default:
}
if err := fd.init(); err != nil {
return err
@@ -98,27 +96,27 @@ func (fd *netFD) connect(la, ra syscall.Sockaddr, deadline time.Time, cancel <-c
if err := fd.init(); err != nil {
return err
}
- if !deadline.IsZero() {
+ if deadline, _ := ctx.Deadline(); !deadline.IsZero() {
fd.setWriteDeadline(deadline)
defer fd.setWriteDeadline(noDeadline)
}
- if cancel != nil {
- done := make(chan bool)
- defer func() {
- // This is unbuffered; wait for the goroutine before returning.
- done <- true
- }()
- go func() {
- select {
- case <-cancel:
- // Force the runtime's poller to immediately give
- // up waiting for writability.
- fd.setWriteDeadline(aLongTimeAgo)
- <-done
- case <-done:
- }
- }()
- }
+
+ // Wait for the goroutine converting context.Done into a write timeout
+ // to exist, otherwise our caller might cancel the context and
+ // cause fd.setWriteDeadline(aLongTimeAgo) to cancel a successful dial.
+ done := make(chan bool) // must be unbuffered
+ defer func() { done <- true }()
+ go func() {
+ select {
+ case <-ctx.Done():
+ // Force the runtime's poller to immediately give
+ // up waiting for writability.
+ fd.setWriteDeadline(aLongTimeAgo)
+ <-done
+ case <-done:
+ }
+ }()
+
for {
// Performing multiple connect system calls on a
// non-blocking socket under Unix variants does not
@@ -130,8 +128,8 @@ func (fd *netFD) connect(la, ra syscall.Sockaddr, deadline time.Time, cancel <-c
// details.
if err := fd.pd.waitWrite(); err != nil {
select {
- case <-cancel:
- return errCanceled
+ case <-ctx.Done():
+ return mapErr(ctx.Err())
default:
}
return err
diff --git a/src/net/fd_windows.go b/src/net/fd_windows.go
index 100994525e..49e79d6a95 100644
--- a/src/net/fd_windows.go
+++ b/src/net/fd_windows.go
@@ -5,12 +5,12 @@
package net
import (
+ "context"
"internal/race"
"os"
"runtime"
"sync"
"syscall"
- "time"
"unsafe"
)
@@ -42,11 +42,6 @@ func sysInit() {
initErr = os.NewSyscallError("wsastartup", e)
}
canCancelIO = syscall.LoadCancelIoEx() == nil
- if syscall.LoadGetAddrInfo() == nil {
- lookupPort = newLookupPort
- lookupIP = newLookupIP
- }
-
hasLoadSetFileCompletionNotificationModes = syscall.LoadSetFileCompletionNotificationModes() == nil
if hasLoadSetFileCompletionNotificationModes {
// It's not safe to use FILE_SKIP_COMPLETION_PORT_ON_SUCCESS if non IFS providers are installed:
@@ -69,22 +64,15 @@ func sysInit() {
}
}
+// canUseConnectEx reports whether we can use the ConnectEx Windows API call
+// for the given network type.
func canUseConnectEx(net string) bool {
switch net {
- case "udp", "udp4", "udp6", "ip", "ip4", "ip6":
- // ConnectEx windows API does not support connectionless sockets.
- return false
- }
- return syscall.LoadConnectEx() == nil
-}
-
-func dial(net string, ra Addr, dialer func(time.Time) (Conn, error), deadline time.Time) (Conn, error) {
- if !canUseConnectEx(net) {
- // Use the relatively inefficient goroutine-racing
- // implementation of DialTimeout.
- return dialChannel(net, ra, dialer, deadline)
+ case "tcp", "tcp4", "tcp6":
+ return true
}
- return dialer(deadline)
+ // ConnectEx windows API does not support connectionless sockets.
+ return false
}
// operation contains superset of data necessary to perform all async IO.
@@ -320,19 +308,20 @@ func (fd *netFD) setAddr(laddr, raddr Addr) {
runtime.SetFinalizer(fd, (*netFD).Close)
}
-func (fd *netFD) connect(la, ra syscall.Sockaddr, deadline time.Time, cancel <-chan struct{}) error {
+func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) error {
// Do not need to call fd.writeLock here,
// because fd is not yet accessible to user,
// so no concurrent operations are possible.
if err := fd.init(); err != nil {
return err
}
- if !deadline.IsZero() {
+ if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() {
fd.setWriteDeadline(deadline)
defer fd.setWriteDeadline(noDeadline)
}
if !canUseConnectEx(fd.net) {
- return os.NewSyscallError("connect", connectFunc(fd.sysfd, ra))
+ err := connectFunc(fd.sysfd, ra)
+ return os.NewSyscallError("connect", err)
}
// ConnectEx windows API requires an unconnected, previously bound socket.
if la == nil {
@@ -351,30 +340,30 @@ func (fd *netFD) connect(la, ra syscall.Sockaddr, deadline time.Time, cancel <-c
// Call ConnectEx API.
o := &fd.wop
o.sa = ra
- if cancel != nil {
- done := make(chan bool)
- defer func() {
- // This is unbuffered; wait for the goroutine before returning.
- done <- true
- }()
- go func() {
- select {
- case <-cancel:
- // Force the runtime's poller to immediately give
- // up waiting for writability.
- fd.setWriteDeadline(aLongTimeAgo)
- <-done
- case <-done:
- }
- }()
- }
+
+ // Wait for the goroutine converting context.Done into a write timeout
+ // to exist, otherwise our caller might cancel the context and
+ // cause fd.setWriteDeadline(aLongTimeAgo) to cancel a successful dial.
+ done := make(chan bool) // must be unbuffered
+ defer func() { done <- true }()
+ go func() {
+ select {
+ case <-ctx.Done():
+ // Force the runtime's poller to immediately give
+ // up waiting for writability.
+ fd.setWriteDeadline(aLongTimeAgo)
+ <-done
+ case <-done:
+ }
+ }()
+
_, err := wsrv.ExecIO(o, "ConnectEx", func(o *operation) error {
return connectExFunc(o.fd.sysfd, o.sa, nil, 0, nil, &o.o)
})
if err != nil {
select {
- case <-cancel:
- return errCanceled
+ case <-ctx.Done():
+ return mapErr(ctx.Err())
default:
if _, ok := err.(syscall.Errno); ok {
err = os.NewSyscallError("connectex", err)
diff --git a/src/net/hook.go b/src/net/hook.go
index 9ab34c0e36..d7316ea438 100644
--- a/src/net/hook.go
+++ b/src/net/hook.go
@@ -4,9 +4,19 @@
package net
+import "context"
+
var (
- testHookDialTCP = dialTCP
- testHookHostsPath = "/etc/hosts"
- testHookLookupIP = func(fn func(string) ([]IPAddr, error), host string) ([]IPAddr, error) { return fn(host) }
+ // if non-nil, overrides dialTCP.
+ testHookDialTCP func(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error)
+
+ testHookHostsPath = "/etc/hosts"
+ testHookLookupIP = func(
+ ctx context.Context,
+ fn func(context.Context, string) ([]IPAddr, error),
+ host string,
+ ) ([]IPAddr, error) {
+ return fn(ctx, host)
+ }
testHookSetKeepAlive = func() {}
)
diff --git a/src/net/http/client.go b/src/net/http/client.go
index 10f5684a79..f8ab675a3d 100644
--- a/src/net/http/client.go
+++ b/src/net/http/client.go
@@ -110,10 +110,6 @@ type RoundTripper interface {
RoundTrip(*Request) (*Response, error)
}
-// Given a string of the form "host", "host:port", or "[ipv6::address]:port",
-// return true if the string includes a port.
-func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }
-
// refererForURL returns a referer without any authentication info or
// an empty string if lastReq scheme is https and newReq scheme is http.
func refererForURL(lastReq, newReq *url.URL) string {
@@ -475,6 +471,7 @@ func (c *Client) doFollowingRedirects(req *Request, shouldRedirect func(int) boo
URL: u,
Header: make(Header),
Cancel: ireq.Cancel,
+ ctx: ireq.ctx,
}
if ireq.Method == "POST" || ireq.Method == "PUT" {
req.Method = "GET"
diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go
index e4fed26803..a9b30b1bf5 100644
--- a/src/net/http/client_test.go
+++ b/src/net/http/client_test.go
@@ -8,6 +8,7 @@ package http_test
import (
"bytes"
+ "context"
"crypto/tls"
"crypto/x509"
"encoding/base64"
@@ -290,6 +291,33 @@ func TestClientRedirects(t *testing.T) {
}
}
+func TestClientRedirectContext(t *testing.T) {
+ defer afterTest(t)
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ Redirect(w, r, "/", StatusFound)
+ }))
+ defer ts.Close()
+
+ ctx, cancel := context.WithCancel(context.Background())
+ c := &Client{CheckRedirect: func(req *Request, via []*Request) error {
+ cancel()
+ if len(via) > 2 {
+ return errors.New("too many redirects")
+ }
+ return nil
+ }}
+ req, _ := NewRequest("GET", ts.URL, nil)
+ req = req.WithContext(ctx)
+ _, err := c.Do(req)
+ ue, ok := err.(*url.Error)
+ if !ok {
+ t.Fatalf("got error %T; want *url.Error", err)
+ }
+ if ue.Err != ExportErrRequestCanceled && ue.Err != ExportErrRequestCanceledConn {
+ t.Errorf("url.Error.Err = %v; want errRequestCanceled or errRequestCanceledConn", ue.Err)
+ }
+}
+
func TestPostRedirects(t *testing.T) {
defer afterTest(t)
var log struct {
diff --git a/src/net/http/clientserver_test.go b/src/net/http/clientserver_test.go
index c2bab378e3..f721382365 100644
--- a/src/net/http/clientserver_test.go
+++ b/src/net/http/clientserver_test.go
@@ -1123,6 +1123,34 @@ func testBogusStatusWorks(t *testing.T, h2 bool) {
}
}
+func TestInterruptWithPanic_h1(t *testing.T) { testInterruptWithPanic(t, h1Mode) }
+func TestInterruptWithPanic_h2(t *testing.T) { testInterruptWithPanic(t, h2Mode) }
+func testInterruptWithPanic(t *testing.T, h2 bool) {
+ log.SetOutput(ioutil.Discard) // is noisy otherwise
+ defer log.SetOutput(os.Stderr)
+
+ const msg = "hello"
+ defer afterTest(t)
+ cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
+ io.WriteString(w, msg)
+ w.(Flusher).Flush()
+ panic("no more")
+ }))
+ defer cst.close()
+ res, err := cst.c.Get(cst.ts.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer res.Body.Close()
+ slurp, err := ioutil.ReadAll(res.Body)
+ if string(slurp) != msg {
+ t.Errorf("client read %q; want %q", slurp, msg)
+ }
+ if err == nil {
+ t.Errorf("client read all successfully; want some error")
+ }
+}
+
type noteCloseConn struct {
net.Conn
closeFunc func()
diff --git a/src/net/http/cookiejar/punycode.go b/src/net/http/cookiejar/punycode.go
index ea7ceb5ef3..a9cc666e8c 100644
--- a/src/net/http/cookiejar/punycode.go
+++ b/src/net/http/cookiejar/punycode.go
@@ -37,7 +37,7 @@ func encode(prefix, s string) (string, error) {
delta, n, bias := int32(0), initialN, initialBias
b, remaining := int32(0), int32(0)
for _, r := range s {
- if r < 0x80 {
+ if r < utf8.RuneSelf {
b++
output = append(output, byte(r))
} else {
diff --git a/src/net/http/fs.go b/src/net/http/fs.go
index 5546d37516..c7a58a61df 100644
--- a/src/net/http/fs.go
+++ b/src/net/http/fs.go
@@ -121,11 +121,11 @@ func dirList(w ResponseWriter, f File) {
// Note that *os.File implements the io.ReadSeeker interface.
func ServeContent(w ResponseWriter, req *Request, name string, modtime time.Time, content io.ReadSeeker) {
sizeFunc := func() (int64, error) {
- size, err := content.Seek(0, os.SEEK_END)
+ size, err := content.Seek(0, io.SeekEnd)
if err != nil {
return 0, errSeeker
}
- _, err = content.Seek(0, os.SEEK_SET)
+ _, err = content.Seek(0, io.SeekStart)
if err != nil {
return 0, errSeeker
}
@@ -166,7 +166,7 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time,
var buf [sniffLen]byte
n, _ := io.ReadFull(content, buf[:])
ctype = DetectContentType(buf[:n])
- _, err := content.Seek(0, os.SEEK_SET) // rewind to output whole file
+ _, err := content.Seek(0, io.SeekStart) // rewind to output whole file
if err != nil {
Error(w, "seeker can't seek", StatusInternalServerError)
return
@@ -213,7 +213,7 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time,
// A response to a request for a single range MUST NOT
// be sent using the multipart/byteranges media type."
ra := ranges[0]
- if _, err := content.Seek(ra.start, os.SEEK_SET); err != nil {
+ if _, err := content.Seek(ra.start, io.SeekStart); err != nil {
Error(w, err.Error(), StatusRequestedRangeNotSatisfiable)
return
}
@@ -236,7 +236,7 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time,
pw.CloseWithError(err)
return
}
- if _, err := content.Seek(ra.start, os.SEEK_SET); err != nil {
+ if _, err := content.Seek(ra.start, io.SeekStart); err != nil {
pw.CloseWithError(err)
return
}
diff --git a/src/net/http/fs_test.go b/src/net/http/fs_test.go
index 9253ebe43a..c811891e87 100644
--- a/src/net/http/fs_test.go
+++ b/src/net/http/fs_test.go
@@ -978,9 +978,9 @@ func TestLinuxSendfile(t *testing.T) {
syscalls := "sendfile,sendfile64"
switch runtime.GOARCH {
- case "mips64", "mips64le":
- // mips64 strace doesn't support sendfile64 and will error out
- // if we specify that with `-e trace='.
+ case "mips64", "mips64le", "s390x":
+ // strace on the above platforms doesn't support sendfile64
+ // and will error out if we specify that with `-e trace='.
syscalls = "sendfile"
}
diff --git a/src/net/http/http.go b/src/net/http/http.go
index a40b23dfdb..a121628632 100644
--- a/src/net/http/http.go
+++ b/src/net/http/http.go
@@ -4,9 +4,34 @@
package http
+import (
+ "strings"
+)
+
// maxInt64 is the effective "infinite" value for the Server and
// Transport's byte-limiting readers.
const maxInt64 = 1<<63 - 1
// TODO(bradfitz): move common stuff here. The other files have accumulated
// generic http stuff in random places.
+
+// contextKey is a value for use with context.WithValue. It's used as
+// a pointer so it fits in an interface{} without allocation.
+type contextKey struct {
+ name string
+}
+
+func (k *contextKey) String() string { return "net/http context value " + k.name }
+
+// Given a string of the form "host", "host:port", or "[ipv6::address]:port",
+// return true if the string includes a port.
+func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }
+
+// removeEmptyPort strips the empty port in ":port" to ""
+// as mandated by RFC 3986 Section 6.2.3.
+func removeEmptyPort(host string) string {
+ if hasPort(host) {
+ return strings.TrimSuffix(host, ":")
+ }
+ return host
+}
diff --git a/src/net/http/httptest/httptest_test.go b/src/net/http/httptest/httptest_test.go
index 18ba73880e..4f9ecbd8bb 100644
--- a/src/net/http/httptest/httptest_test.go
+++ b/src/net/http/httptest/httptest_test.go
@@ -155,10 +155,10 @@ func TestNewRequest(t *testing.T) {
got := NewRequest(tt.method, tt.uri, tt.body)
slurp, err := ioutil.ReadAll(got.Body)
if err != nil {
- t.Errorf("%i. ReadAll: %v", i, err)
+ t.Errorf("%d. ReadAll: %v", i, err)
}
if string(slurp) != tt.wantBody {
- t.Errorf("%i. Body = %q; want %q", i, slurp, tt.wantBody)
+ t.Errorf("%d. Body = %q; want %q", i, slurp, tt.wantBody)
}
got.Body = nil // before DeepEqual
if !reflect.DeepEqual(got.URL, tt.want.URL) {
diff --git a/src/net/http/httputil/dump.go b/src/net/http/httputil/dump.go
index ddde11a0e4..692ab62c9b 100644
--- a/src/net/http/httputil/dump.go
+++ b/src/net/http/httputil/dump.go
@@ -163,7 +163,6 @@ func valueOrDefault(value, def string) string {
var reqWriteExcludeHeaderDump = map[string]bool{
"Host": true, // not in Header map anyway
- "Content-Length": true,
"Transfer-Encoding": true,
"Trailer": true,
}
diff --git a/src/net/http/httputil/dump_test.go b/src/net/http/httputil/dump_test.go
index fc884347a6..2e980d39f8 100644
--- a/src/net/http/httputil/dump_test.go
+++ b/src/net/http/httputil/dump_test.go
@@ -122,6 +122,10 @@ var dumpTests = []dumpTest{
Host: "post.tld",
Path: "/",
},
+ Header: http.Header{
+ "Content-Length": []string{"8193"},
+ },
+
ContentLength: 8193,
ProtoMajor: 1,
ProtoMinor: 1,
@@ -135,6 +139,10 @@ var dumpTests = []dumpTest{
"Content-Length: 8193\r\n" +
"Accept-Encoding: gzip\r\n\r\n" +
strings.Repeat("a", 8193),
+ WantDump: "POST / HTTP/1.1\r\n" +
+ "Host: post.tld\r\n" +
+ "Content-Length: 8193\r\n\r\n" +
+ strings.Repeat("a", 8193),
},
{
@@ -144,6 +152,38 @@ var dumpTests = []dumpTest{
WantDump: "GET http://foo.com/ HTTP/1.1\r\n" +
"User-Agent: blah\r\n\r\n",
},
+
+ // Issue #7215. DumpRequest should return the "Content-Length" when set
+ {
+ Req: *mustReadRequest("POST /v2/api/?login HTTP/1.1\r\n" +
+ "Host: passport.myhost.com\r\n" +
+ "Content-Length: 3\r\n" +
+ "\r\nkey1=name1&key2=name2"),
+ WantDump: "POST /v2/api/?login HTTP/1.1\r\n" +
+ "Host: passport.myhost.com\r\n" +
+ "Content-Length: 3\r\n" +
+ "\r\nkey",
+ },
+
+ // Issue #7215. DumpRequest should return the "Content-Length" in ReadRequest
+ {
+ Req: *mustReadRequest("POST /v2/api/?login HTTP/1.1\r\n" +
+ "Host: passport.myhost.com\r\n" +
+ "Content-Length: 0\r\n" +
+ "\r\nkey1=name1&key2=name2"),
+ WantDump: "POST /v2/api/?login HTTP/1.1\r\n" +
+ "Host: passport.myhost.com\r\n" +
+ "Content-Length: 0\r\n\r\n",
+ },
+
+ // Issue #7215. DumpRequest should not return the "Content-Length" if unset
+ {
+ Req: *mustReadRequest("POST /v2/api/?login HTTP/1.1\r\n" +
+ "Host: passport.myhost.com\r\n" +
+ "\r\nkey1=name1&key2=name2"),
+ WantDump: "POST /v2/api/?login HTTP/1.1\r\n" +
+ "Host: passport.myhost.com\r\n\r\n",
+ },
}
func TestDumpRequest(t *testing.T) {
diff --git a/src/net/http/httputil/example_test.go b/src/net/http/httputil/example_test.go
index f856135742..6191603674 100644
--- a/src/net/http/httputil/example_test.go
+++ b/src/net/http/httputil/example_test.go
@@ -47,7 +47,7 @@ func ExampleDumpRequest() {
fmt.Printf("%s", b)
// Output:
- // "POST / HTTP/1.1\r\nHost: www.example.org\r\nAccept-Encoding: gzip\r\nUser-Agent: Go-http-client/1.1\r\n\r\nGo is a general-purpose language designed with systems programming in mind."
+ // "POST / HTTP/1.1\r\nHost: www.example.org\r\nAccept-Encoding: gzip\r\nContent-Length: 75\r\nUser-Agent: Go-http-client/1.1\r\n\r\nGo is a general-purpose language designed with systems programming in mind."
}
func ExampleDumpRequestOut() {
diff --git a/src/net/http/httputil/persist.go b/src/net/http/httputil/persist.go
index 7874da3bec..51486e78e2 100644
--- a/src/net/http/httputil/persist.go
+++ b/src/net/http/httputil/persist.go
@@ -28,7 +28,7 @@ var errClosed = errors.New("i/o operation on closed connection")
// Is is low-level, old, and unused by Go's current HTTP stack.
// We should have deleted it before Go 1.
//
-// Deprecated: use the Server in package net/http instead.
+// Deprecated: Use the Server in package net/http instead.
type ServerConn struct {
mu sync.Mutex // read-write protects the following fields
c net.Conn
@@ -45,7 +45,7 @@ type ServerConn struct {
// Is is low-level, old, and unused by Go's current HTTP stack.
// We should have deleted it before Go 1.
//
-// Deprecated: use the Server in package net/http instead.
+// Deprecated: Use the Server in package net/http instead.
func NewServerConn(c net.Conn, r *bufio.Reader) *ServerConn {
if r == nil {
r = bufio.NewReader(c)
@@ -221,7 +221,7 @@ func (sc *ServerConn) Write(req *http.Request, resp *http.Response) error {
// Is is low-level, old, and unused by Go's current HTTP stack.
// We should have deleted it before Go 1.
//
-// Deprecated: use Client or Transport in package net/http instead.
+// Deprecated: Use Client or Transport in package net/http instead.
type ClientConn struct {
mu sync.Mutex // read-write protects the following fields
c net.Conn
@@ -239,7 +239,7 @@ type ClientConn struct {
// Is is low-level, old, and unused by Go's current HTTP stack.
// We should have deleted it before Go 1.
//
-// Deprecated: use the Client or Transport in package net/http instead.
+// Deprecated: Use the Client or Transport in package net/http instead.
func NewClientConn(c net.Conn, r *bufio.Reader) *ClientConn {
if r == nil {
r = bufio.NewReader(c)
@@ -256,7 +256,7 @@ func NewClientConn(c net.Conn, r *bufio.Reader) *ClientConn {
// Is is low-level, old, and unused by Go's current HTTP stack.
// We should have deleted it before Go 1.
//
-// Deprecated: use the Client or Transport in package net/http instead.
+// Deprecated: Use the Client or Transport in package net/http instead.
func NewProxyClientConn(c net.Conn, r *bufio.Reader) *ClientConn {
cc := NewClientConn(c, r)
cc.writeReq = (*http.Request).WriteProxy
diff --git a/src/net/http/main_test.go b/src/net/http/main_test.go
index 299cd7b2d2..d10fd89b54 100644
--- a/src/net/http/main_test.go
+++ b/src/net/http/main_test.go
@@ -5,7 +5,6 @@
package http_test
import (
- "flag"
"fmt"
"net/http"
"os"
@@ -16,8 +15,6 @@ import (
"time"
)
-var flaky = flag.Bool("flaky", false, "run known-flaky tests too")
-
func TestMain(m *testing.M) {
v := m.Run()
if v == 0 && goroutineLeaked() {
@@ -91,12 +88,6 @@ func setParallel(t *testing.T) {
}
}
-func setFlaky(t *testing.T, issue int) {
- if !*flaky {
- t.Skipf("skipping known flaky test; see golang.org/issue/%d", issue)
- }
-}
-
func afterTest(t testing.TB) {
http.DefaultTransport.(*http.Transport).CloseIdleConnections()
if testing.Short() {
@@ -129,3 +120,17 @@ func afterTest(t testing.TB) {
}
t.Errorf("Test appears to have leaked %s:\n%s", bad, stacks)
}
+
+// waitCondition reports whether fn eventually returned true,
+// checking immediately and then every checkEvery amount,
+// until waitFor has elpased, at which point it returns false.
+func waitCondition(waitFor, checkEvery time.Duration, fn func() bool) bool {
+ deadline := time.Now().Add(waitFor)
+ for time.Now().Before(deadline) {
+ if fn() {
+ return true
+ }
+ time.Sleep(checkEvery)
+ }
+ return false
+}
diff --git a/src/net/http/pprof/pprof.go b/src/net/http/pprof/pprof.go
index 2357d8ed1e..cb4086b963 100644
--- a/src/net/http/pprof/pprof.go
+++ b/src/net/http/pprof/pprof.go
@@ -30,7 +30,8 @@
//
// go tool pprof http://localhost:6060/debug/pprof/profile
//
-// Or to look at the goroutine blocking profile:
+// Or to look at the goroutine blocking profile, after calling
+// runtime.SetBlockProfileRate in your program:
//
// go tool pprof http://localhost:6060/debug/pprof/block
//
@@ -119,8 +120,8 @@ func Profile(w http.ResponseWriter, r *http.Request) {
// Tracing lasts for duration specified in seconds GET parameter, or for 1 second if not specified.
// The package initialization registers it as /debug/pprof/trace.
func Trace(w http.ResponseWriter, r *http.Request) {
- sec, _ := strconv.ParseInt(r.FormValue("seconds"), 10, 64)
- if sec == 0 {
+ sec, err := strconv.ParseFloat(r.FormValue("seconds"), 64)
+ if sec <= 0 || err != nil {
sec = 1
}
@@ -135,7 +136,7 @@ func Trace(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Could not enable tracing: %s\n", err)
return
}
- sleep(w, time.Duration(sec)*time.Second)
+ sleep(w, time.Duration(sec*float64(time.Second)))
trace.Stop()
}
diff --git a/src/net/http/request.go b/src/net/http/request.go
index 371d36b097..a49ab36964 100644
--- a/src/net/http/request.go
+++ b/src/net/http/request.go
@@ -249,7 +249,7 @@ type Request struct {
//
// For server requests, this field is not applicable.
//
- // Deprecated: use the Context and WithContext methods
+ // Deprecated: Use the Context and WithContext methods
// instead. If a Request's Cancel field and context are both
// set, it is undefined whether Cancel is respected.
Cancel <-chan struct{}
@@ -266,9 +266,13 @@ type Request struct {
//
// The returned context is always non-nil; it defaults to the
// background context.
+//
+// For outgoing client requests, the context controls cancelation.
+//
+// For incoming server requests, the context is canceled when either
+// the client's connection closes, or when the ServeHTTP method
+// returns.
func (r *Request) Context() context.Context {
- // TODO(bradfitz): document above what Context means for server and client
- // requests, once implemented.
if r.ctx != nil {
return r.ctx
}
@@ -656,6 +660,8 @@ func NewRequest(method, urlStr string, body io.Reader) (*Request, error) {
if !ok && body != nil {
rc = ioutil.NopCloser(body)
}
+ // The host's colon:port should be normalized. See Issue 14836.
+ u.Host = removeEmptyPort(u.Host)
req := &Request{
Method: method,
URL: u,
@@ -813,7 +819,7 @@ func readRequest(b *bufio.Reader, deleteHostHeader bool) (req *Request, err erro
}
req.Header = Header(mimeHeader)
- // RFC2616: Must treat
+ // RFC 2616: Must treat
// GET /index.html HTTP/1.1
// Host: www.google.com
// and
diff --git a/src/net/http/request_test.go b/src/net/http/request_test.go
index ff4837f2fa..82c7af3cda 100644
--- a/src/net/http/request_test.go
+++ b/src/net/http/request_test.go
@@ -398,11 +398,13 @@ var newRequestHostTests = []struct {
{"http://192.168.0.1/", "192.168.0.1"},
{"http://192.168.0.1:8080/", "192.168.0.1:8080"},
+ {"http://192.168.0.1:/", "192.168.0.1"},
{"http://[fe80::1]/", "[fe80::1]"},
{"http://[fe80::1]:8080/", "[fe80::1]:8080"},
{"http://[fe80::1%25en0]/", "[fe80::1%en0]"},
{"http://[fe80::1%25en0]:8080/", "[fe80::1%en0]:8080"},
+ {"http://[fe80::1%25en0]:/", "[fe80::1%en0]"},
}
func TestNewRequestHost(t *testing.T) {
diff --git a/src/net/http/response.go b/src/net/http/response.go
index b49b77d8b9..91d4ffb7ec 100644
--- a/src/net/http/response.go
+++ b/src/net/http/response.go
@@ -185,7 +185,7 @@ func ReadResponse(r *bufio.Reader, req *Request) (*Response, error) {
return resp, nil
}
-// RFC2616: Should treat
+// RFC 2616: Should treat
// Pragma: no-cache
// like
// Cache-Control: no-cache
diff --git a/src/net/http/serve_test.go b/src/net/http/serve_test.go
index 638ba5f48f..5f206b1873 100644
--- a/src/net/http/serve_test.go
+++ b/src/net/http/serve_test.go
@@ -9,6 +9,7 @@ package http_test
import (
"bufio"
"bytes"
+ "context"
"crypto/tls"
"errors"
"fmt"
@@ -3989,6 +3990,89 @@ func TestServerValidatesHeaders(t *testing.T) {
}
}
+func TestServerRequestContextCancel_ServeHTTPDone(t *testing.T) {
+ defer afterTest(t)
+ ctxc := make(chan context.Context, 1)
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ ctx := r.Context()
+ select {
+ case <-ctx.Done():
+ t.Error("should not be Done in ServeHTTP")
+ default:
+ }
+ ctxc <- ctx
+ }))
+ defer ts.Close()
+ res, err := Get(ts.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ res.Body.Close()
+ ctx := <-ctxc
+ select {
+ case <-ctx.Done():
+ default:
+ t.Error("context should be done after ServeHTTP completes")
+ }
+}
+
+func TestServerRequestContextCancel_ConnClose(t *testing.T) {
+ // Currently the context is not canceled when the connection
+ // is closed because we're not reading from the connection
+ // until after ServeHTTP for the previous handler is done.
+ // Until the server code is modified to always be in a read
+ // (Issue 15224), this test doesn't work yet.
+ t.Skip("TODO(bradfitz): this test doesn't yet work; golang.org/issue/15224")
+ defer afterTest(t)
+ inHandler := make(chan struct{})
+ handlerDone := make(chan struct{})
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ close(inHandler)
+ select {
+ case <-r.Context().Done():
+ case <-time.After(3 * time.Second):
+ t.Errorf("timeout waiting for context to be done")
+ }
+ close(handlerDone)
+ }))
+ defer ts.Close()
+ c, err := net.Dial("tcp", ts.Listener.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+ io.WriteString(c, "GET / HTTP/1.1\r\nHost: foo\r\n\r\n")
+ select {
+ case <-inHandler:
+ case <-time.After(3 * time.Second):
+ t.Fatalf("timeout waiting to see ServeHTTP get called")
+ }
+ c.Close() // this should trigger the context being done
+
+ select {
+ case <-handlerDone:
+ case <-time.After(3 * time.Second):
+ t.Fatalf("timeout waiting to see ServeHTTP exit")
+ }
+}
+
+func TestServerContext_ServerContextKey(t *testing.T) {
+ defer afterTest(t)
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ ctx := r.Context()
+ got := ctx.Value(ServerContextKey)
+ if _, ok := got.(*Server); !ok {
+ t.Errorf("context value = %T; want *http.Server")
+ }
+ }))
+ defer ts.Close()
+ res, err := Get(ts.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ res.Body.Close()
+}
+
func BenchmarkClientServer(b *testing.B) {
b.ReportAllocs()
b.StopTimer()
@@ -4200,7 +4284,7 @@ func BenchmarkClient(b *testing.B) {
if err != nil {
b.Fatalf("ReadAll: %v", err)
}
- if bytes.Compare(body, data) != 0 {
+ if !bytes.Equal(body, data) {
b.Fatalf("Got body: %q", body)
}
}
diff --git a/src/net/http/server.go b/src/net/http/server.go
index a2ef0ddf20..da17fccbae 100644
--- a/src/net/http/server.go
+++ b/src/net/http/server.go
@@ -9,6 +9,7 @@ package http
import (
"bufio"
"bytes"
+ "context"
"crypto/tls"
"errors"
"fmt"
@@ -50,6 +51,9 @@ var (
// ResponseWriter. Cautious handlers should read the Request.Body
// first, and then reply.
//
+// Except for reading the body, handlers should not modify the
+// provided Request.
+//
// If ServeHTTP panics, the server (the caller of ServeHTTP) assumes
// that the effect of the panic was isolated to the active request.
// It recovers the panic, logs a stack trace to the server error log,
@@ -90,6 +94,10 @@ type ResponseWriter interface {
// The Flusher interface is implemented by ResponseWriters that allow
// an HTTP handler to flush buffered data to the client.
//
+// The default HTTP/1.x and HTTP/2 ResponseWriter implementations
+// support Flusher, but ResponseWriter wrappers may not. Handlers
+// should always test for this ability at runtime.
+//
// Note that even for ResponseWriters that support Flush,
// if the client is connected through an HTTP proxy,
// the buffered data may not reach the client until the response
@@ -101,6 +109,11 @@ type Flusher interface {
// The Hijacker interface is implemented by ResponseWriters that allow
// an HTTP handler to take over the connection.
+//
+// The default ResponseWriter for HTTP/1.x connections supports
+// Hijacker, but HTTP/2 connections intentionally do not.
+// ResponseWriter wrappers may also not support Hijacker. Handlers
+// should always test for this ability at runtime.
type Hijacker interface {
// Hijack lets the caller take over the connection.
// After a call to Hijack(), the HTTP server library
@@ -143,6 +156,14 @@ type CloseNotifier interface {
CloseNotify() <-chan bool
}
+var (
+ // ServerContextKey is a context key. It can be used in HTTP
+ // handlers with context.WithValue to access the server that
+ // started the handler. The associated value will be of
+ // type *Server.
+ ServerContextKey = &contextKey{"http-server"}
+)
+
// A conn represents the server side of an HTTP connection.
type conn struct {
// server is the server on which the connection arrived.
@@ -306,11 +327,14 @@ func (cw *chunkWriter) close() {
// A response represents the server side of an HTTP response.
type response struct {
- conn *conn
- req *Request // request for this response
- reqBody io.ReadCloser
- wroteHeader bool // reply header has been (logically) written
- wroteContinue bool // 100 Continue response was written
+ conn *conn
+ req *Request // request for this response
+ reqBody io.ReadCloser
+ cancelCtx context.CancelFunc // when ServeHTTP exits
+ wroteHeader bool // reply header has been (logically) written
+ wroteContinue bool // 100 Continue response was written
+ wants10KeepAlive bool // HTTP/1.0 w/ Connection "keep-alive"
+ wantsClose bool // HTTP request has Connection "close"
w *bufio.Writer // buffers output in chunks to chunkWriter
cw chunkWriter
@@ -681,7 +705,7 @@ func appendTime(b []byte, t time.Time) []byte {
var errTooLarge = errors.New("http: request too large")
// Read next request from connection.
-func (c *conn) readRequest() (w *response, err error) {
+func (c *conn) readRequest(ctx context.Context) (w *response, err error) {
if c.hijacked() {
return nil, ErrHijacked
}
@@ -710,6 +734,10 @@ func (c *conn) readRequest() (w *response, err error) {
}
return nil, err
}
+
+ ctx, cancelCtx := context.WithCancel(ctx)
+ req.ctx = ctx
+
c.lastMethod = req.Method
c.r.setInfiniteReadLimit()
@@ -744,10 +772,17 @@ func (c *conn) readRequest() (w *response, err error) {
w = &response{
conn: c,
+ cancelCtx: cancelCtx,
req: req,
reqBody: req.Body,
handlerHeader: make(Header),
contentLength: -1,
+
+ // We populate these ahead of time so we're not
+ // reading from req.Header after their Handler starts
+ // and maybe mutates it (Issue 14940)
+ wants10KeepAlive: req.wantsHttp10KeepAlive(),
+ wantsClose: req.wantsClose(),
}
if isH2Upgrade {
w.closeAfterReply = true
@@ -929,7 +964,7 @@ func (cw *chunkWriter) writeHeader(p []byte) {
// If this was an HTTP/1.0 request with keep-alive and we sent a
// Content-Length back, we can make this a keep-alive response ...
- if w.req.wantsHttp10KeepAlive() && keepAlivesEnabled {
+ if w.wants10KeepAlive && keepAlivesEnabled {
sentLength := header.get("Content-Length") != ""
if sentLength && header.get("Connection") == "keep-alive" {
w.closeAfterReply = false
@@ -939,12 +974,12 @@ func (cw *chunkWriter) writeHeader(p []byte) {
// Check for a explicit (and valid) Content-Length header.
hasCL := w.contentLength != -1
- if w.req.wantsHttp10KeepAlive() && (isHEAD || hasCL) {
+ if w.wants10KeepAlive && (isHEAD || hasCL) {
_, connectionHeaderSet := header["Connection"]
if !connectionHeaderSet {
setHeader.connection = "keep-alive"
}
- } else if !w.req.ProtoAtLeast(1, 1) || w.req.wantsClose() {
+ } else if !w.req.ProtoAtLeast(1, 1) || w.wantsClose {
w.closeAfterReply = true
}
@@ -1384,7 +1419,7 @@ type badRequestError string
func (e badRequestError) Error() string { return "Bad Request: " + string(e) }
// Serve a new connection.
-func (c *conn) serve() {
+func (c *conn) serve(ctx context.Context) {
c.remoteAddr = c.rwc.RemoteAddr().String()
defer func() {
if err := recover(); err != nil {
@@ -1421,12 +1456,17 @@ func (c *conn) serve() {
}
}
+ // HTTP/1.x from here on.
+
c.r = &connReader{r: c.rwc}
c.bufr = newBufioReader(c.r)
c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)
+ ctx, cancelCtx := context.WithCancel(ctx)
+ defer cancelCtx()
+
for {
- w, err := c.readRequest()
+ w, err := c.readRequest(ctx)
if c.r.remain != c.server.initialReadLimitSize() {
// If we read any bytes off the wire, we're active.
c.setState(c.rwc, StateActive)
@@ -1474,6 +1514,7 @@ func (c *conn) serve() {
// [*] Not strictly true: HTTP pipelining. We could let them all process
// in parallel even if their responses need to be serialized.
serverHandler{c.server}.ServeHTTP(w, w.req)
+ w.cancelCtx()
if c.hijacked() {
return
}
@@ -1625,6 +1666,8 @@ func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
// Helper handlers
// Error replies to the request with the specified error message and HTTP code.
+// It does not otherwise end the request; the caller should ensure no further
+// writes are done to w.
// The error message should be plain text.
func Error(w ResponseWriter, error string, code int) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
@@ -1713,7 +1756,7 @@ func Redirect(w ResponseWriter, r *Request, urlStr string, code int) {
w.Header().Set("Location", urlStr)
w.WriteHeader(code)
- // RFC2616 recommends that a short note "SHOULD" be included in the
+ // RFC 2616 recommends that a short note "SHOULD" be included in the
// response because older user agents may not understand 301/307.
// Shouldn't send the response for POST or HEAD; that leaves GET.
if r.Method == "GET" {
@@ -2122,6 +2165,10 @@ func (srv *Server) Serve(l net.Listener) error {
if err := srv.setupHTTP2(); err != nil {
return err
}
+ // TODO: allow changing base context? can't imagine concrete
+ // use cases yet.
+ baseCtx := context.Background()
+ ctx := context.WithValue(baseCtx, ServerContextKey, srv)
for {
rw, e := l.Accept()
if e != nil {
@@ -2143,7 +2190,7 @@ func (srv *Server) Serve(l net.Listener) error {
tempDelay = 0
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
- go c.serve()
+ go c.serve(ctx)
}
}
diff --git a/src/net/http/transfer.go b/src/net/http/transfer.go
index 4c130f0cc4..501e4be08c 100644
--- a/src/net/http/transfer.go
+++ b/src/net/http/transfer.go
@@ -276,7 +276,7 @@ func (t *transferReader) protoAtLeast(m, n int) bool {
}
// bodyAllowedForStatus reports whether a given response status code
-// permits a body. See RFC2616, section 4.4.
+// permits a body. See RFC 2616, section 4.4.
func bodyAllowedForStatus(status int) bool {
switch {
case status >= 100 && status <= 199:
@@ -368,7 +368,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
// If there is no Content-Length or chunked Transfer-Encoding on a *Response
// and the status is not 1xx, 204 or 304, then the body is unbounded.
- // See RFC2616, section 4.4.
+ // See RFC 2616, section 4.4.
switch msg.(type) {
case *Response:
if realLength == -1 &&
diff --git a/src/net/http/transport.go b/src/net/http/transport.go
index 7692abff47..3ccc6dd0df 100644
--- a/src/net/http/transport.go
+++ b/src/net/http/transport.go
@@ -12,6 +12,7 @@ package http
import (
"bufio"
"compress/gzip"
+ "context"
"crypto/tls"
"errors"
"fmt"
@@ -32,10 +33,10 @@ import (
// $no_proxy) environment variables.
var DefaultTransport RoundTripper = &Transport{
Proxy: ProxyFromEnvironment,
- Dial: (&net.Dialer{
+ Dialer: &net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
- }).Dial,
+ },
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
@@ -64,6 +65,7 @@ const DefaultMaxIdleConnsPerHost = 2
type Transport struct {
idleMu sync.Mutex
wantIdle bool // user has requested to close all idle conns
+ idleCount int
idleConn map[connectMethodKey][]*persistConn
idleConnCh map[connectMethodKey]chan *persistConn
@@ -80,10 +82,17 @@ type Transport struct {
Proxy func(*Request) (*url.URL, error)
// Dial specifies the dial function for creating unencrypted
- // TCP connections.
- // If Dial is nil, net.Dial is used.
+ // TCP connections. If Dial and Dialer are both nil, net.Dial
+ // is used.
+ //
+ // Deprecated: Use Dialer instead. If both are specified, Dialer
+ // takes precedence.
Dial func(network, addr string) (net.Conn, error)
+ // Dialer optionally specifies a dialer configuration to use
+ // for new connections.
+ Dialer *net.Dialer
+
// DialTLS specifies an optional dial function for creating
// TLS connections for non-proxied HTTPS requests.
//
@@ -158,7 +167,7 @@ type Transport struct {
nextProtoOnce sync.Once
h2transport *http2Transport // non-nil if http2 wired up
- // TODO: tunable on global max cached connections
+ // TODO: MaxIdleConns tunable for global max cached connections (Issue 15461)
// TODO: tunable on timeout on cached connections
// TODO: tunable on max per-host TCP dials in flight (Issue 13957)
}
@@ -605,6 +614,7 @@ func (t *Transport) tryPutIdleConn(pconn *persistConn) error {
}
}
t.idleConn[key] = append(t.idleConn[key], pconn)
+ t.idleCount++
return nil
}
@@ -630,13 +640,14 @@ func (t *Transport) getIdleConnCh(cm connectMethod) chan *persistConn {
return ch
}
-func (t *Transport) getIdleConn(cm connectMethod) (pconn *persistConn) {
+func (t *Transport) getIdleConn(cm connectMethod) *persistConn {
key := cm.key()
t.idleMu.Lock()
defer t.idleMu.Unlock()
if t.idleConn == nil {
return nil
}
+ var pconn *persistConn
for {
pconns, ok := t.idleConn[key]
if !ok {
@@ -651,8 +662,44 @@ func (t *Transport) getIdleConn(cm connectMethod) (pconn *persistConn) {
pconn = pconns[len(pconns)-1]
t.idleConn[key] = pconns[:len(pconns)-1]
}
- if !pconn.isBroken() {
- return
+ t.idleCount--
+ if pconn.isBroken() {
+ // There is a tiny window where this is
+ // possible, between the connecting dying and
+ // the persistConn readLoop calling
+ // Transport.removeIdleConn. Just skip it and
+ // carry on.
+ continue
+ }
+ return pconn
+ }
+}
+
+// removeIdleConn marks pconn as dead.
+func (t *Transport) removeIdleConn(pconn *persistConn) {
+ key := pconn.cacheKey
+ t.idleMu.Lock()
+ defer t.idleMu.Unlock()
+
+ pconns, _ := t.idleConn[key]
+ switch len(pconns) {
+ case 0:
+ // Nothing
+ case 1:
+ if pconns[0] == pconn {
+ t.idleCount--
+ delete(t.idleConn, key)
+ }
+ default:
+ // TODO(bradfitz): map into LRU element?
+ for i, v := range pconns {
+ if v != pconn {
+ continue
+ }
+ pconns[i] = pconns[len(pconns)-1]
+ t.idleConn[key] = pconns[:len(pconns)-1]
+ t.idleCount--
+ break
}
}
}
@@ -689,7 +736,10 @@ func (t *Transport) replaceReqCanceler(r *Request, fn func()) bool {
return true
}
-func (t *Transport) dial(network, addr string) (net.Conn, error) {
+func (t *Transport) dial(ctx context.Context, network, addr string) (net.Conn, error) {
+ if t.Dialer != nil {
+ return t.Dialer.DialContext(ctx, network, addr)
+ }
if t.Dial != nil {
c, err := t.Dial(network, addr)
if c == nil && err == nil {
@@ -705,6 +755,7 @@ func (t *Transport) dial(network, addr string) (net.Conn, error) {
// and/or setting up TLS. If this doesn't return an error, the persistConn
// is ready to write requests to.
func (t *Transport) getConn(req *Request, cm connectMethod) (*persistConn, error) {
+ ctx := req.Context()
if pc := t.getIdleConn(cm); pc != nil {
// set request canceler to some non-nil function so we
// can detect whether it was cleared between now and when
@@ -738,7 +789,7 @@ func (t *Transport) getConn(req *Request, cm connectMethod) (*persistConn, error
t.setReqCanceler(req, func() { close(cancelc) })
go func() {
- pc, err := t.dialConn(cm)
+ pc, err := t.dialConn(ctx, cm)
dialc <- dialRes{pc, err}
}()
@@ -767,7 +818,7 @@ func (t *Transport) getConn(req *Request, cm connectMethod) (*persistConn, error
}
}
-func (t *Transport) dialConn(cm connectMethod) (*persistConn, error) {
+func (t *Transport) dialConn(ctx context.Context, cm connectMethod) (*persistConn, error) {
pconn := &persistConn{
t: t,
cacheKey: cm.key(),
@@ -797,7 +848,7 @@ func (t *Transport) dialConn(cm connectMethod) (*persistConn, error) {
pconn.tlsState = &cs
}
} else {
- conn, err := t.dial("tcp", cm.addr())
+ conn, err := t.dial(ctx, "tcp", cm.addr())
if err != nil {
if cm.proxyURL != nil {
err = fmt.Errorf("http: error connecting to proxy %s: %v", cm.proxyURL, err)
@@ -1108,7 +1159,10 @@ func (pc *persistConn) cancelRequest() {
func (pc *persistConn) readLoop() {
closeErr := errReadLoopExiting // default value, if not changed below
- defer func() { pc.close(closeErr) }()
+ defer func() {
+ pc.close(closeErr)
+ pc.t.removeIdleConn(pc)
+ }()
tryPutIdleConn := func() bool {
if err := pc.t.tryPutIdleConn(pc); err != nil {
diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go
index 7a01dca394..2e27cc1850 100644
--- a/src/net/http/transport_test.go
+++ b/src/net/http/transport_test.go
@@ -18,6 +18,7 @@ import (
"crypto/tls"
"errors"
"fmt"
+ "internal/testenv"
"io"
"io/ioutil"
"log"
@@ -437,6 +438,54 @@ func TestTransportMaxPerHostIdleConns(t *testing.T) {
}
}
+func TestTransportRemovesDeadIdleConnections(t *testing.T) {
+ defer afterTest(t)
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ io.WriteString(w, r.RemoteAddr)
+ }))
+ defer ts.Close()
+
+ tr := &Transport{}
+ defer tr.CloseIdleConnections()
+ c := &Client{Transport: tr}
+
+ doReq := func(name string) string {
+ // Do a POST instead of a GET to prevent the Transport's
+ // idempotent request retry logic from kicking in...
+ res, err := c.Post(ts.URL, "", nil)
+ if err != nil {
+ t.Fatalf("%s: %v", name, err)
+ }
+ if res.StatusCode != 200 {
+ t.Fatalf("%s: %v", name, res.Status)
+ }
+ defer res.Body.Close()
+ slurp, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Fatalf("%s: %v", name, err)
+ }
+ return string(slurp)
+ }
+
+ first := doReq("first")
+ keys1 := tr.IdleConnKeysForTesting()
+
+ ts.CloseClientConnections()
+
+ var keys2 []string
+ if !waitCondition(3*time.Second, 50*time.Millisecond, func() bool {
+ keys2 = tr.IdleConnKeysForTesting()
+ return len(keys2) == 0
+ }) {
+ t.Fatalf("Transport didn't notice idle connection's death.\nbefore: %q\n after: %q\n", keys1, keys2)
+ }
+
+ second := doReq("second")
+ if first == second {
+ t.Errorf("expected a different connection between requests. got %q both times", first)
+ }
+}
+
func TestTransportServerClosingUnexpectedly(t *testing.T) {
setParallel(t)
defer afterTest(t)
@@ -2229,7 +2278,7 @@ func TestTransportTLSHandshakeTimeout(t *testing.T) {
// Trying to repro golang.org/issue/3514
func TestTLSServerClosesConnection(t *testing.T) {
defer afterTest(t)
- setFlaky(t, 7634)
+ testenv.SkipFlaky(t, 7634)
closedc := make(chan bool, 1)
ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
diff --git a/src/net/interface.go b/src/net/interface.go
index c99f8fd216..52b857c65f 100644
--- a/src/net/interface.go
+++ b/src/net/interface.go
@@ -4,7 +4,11 @@
package net
-import "errors"
+import (
+ "errors"
+ "sync"
+ "time"
+)
var (
errInvalidInterface = errors.New("invalid network interface")
@@ -88,9 +92,12 @@ func (ifi *Interface) MulticastAddrs() ([]Addr, error) {
func Interfaces() ([]Interface, error) {
ift, err := interfaceTable(0)
if err != nil {
- err = &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
+ return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
}
- return ift, err
+ if len(ift) != 0 {
+ zoneCache.update(ift)
+ }
+ return ift, nil
}
// InterfaceAddrs returns a list of the system's network interface
@@ -137,6 +144,9 @@ func InterfaceByName(name string) (*Interface, error) {
if err != nil {
return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
}
+ if len(ift) != 0 {
+ zoneCache.update(ift)
+ }
for _, ifi := range ift {
if name == ifi.Name {
return &ifi, nil
@@ -144,3 +154,68 @@ func InterfaceByName(name string) (*Interface, error) {
}
return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errNoSuchInterface}
}
+
+// An ipv6ZoneCache represents a cache holding partial network
+// interface information. It is used for reducing the cost of IPv6
+// addressing scope zone resolution.
+type ipv6ZoneCache struct {
+ sync.RWMutex // guard the following
+ lastFetched time.Time // last time routing information was fetched
+ toIndex map[string]int // interface name to its index
+ toName map[int]string // interface index to its name
+}
+
+var zoneCache = ipv6ZoneCache{
+ toIndex: make(map[string]int),
+ toName: make(map[int]string),
+}
+
+func (zc *ipv6ZoneCache) update(ift []Interface) {
+ zc.Lock()
+ defer zc.Unlock()
+ now := time.Now()
+ if zc.lastFetched.After(now.Add(-60 * time.Second)) {
+ return
+ }
+ zc.lastFetched = now
+ if len(ift) == 0 {
+ var err error
+ if ift, err = interfaceTable(0); err != nil {
+ return
+ }
+ }
+ zc.toIndex = make(map[string]int, len(ift))
+ zc.toName = make(map[int]string, len(ift))
+ for _, ifi := range ift {
+ zc.toIndex[ifi.Name] = ifi.Index
+ zc.toName[ifi.Index] = ifi.Name
+ }
+}
+
+func zoneToString(zone int) string {
+ if zone == 0 {
+ return ""
+ }
+ zoneCache.update(nil)
+ zoneCache.RLock()
+ defer zoneCache.RUnlock()
+ name, ok := zoneCache.toName[zone]
+ if !ok {
+ name = uitoa(uint(zone))
+ }
+ return name
+}
+
+func zoneToInt(zone string) int {
+ if zone == "" {
+ return 0
+ }
+ zoneCache.update(nil)
+ zoneCache.RLock()
+ defer zoneCache.RUnlock()
+ index, ok := zoneCache.toIndex[zone]
+ if !ok {
+ index, _, _ = dtoi(zone, 0)
+ }
+ return index
+}
diff --git a/src/net/interface_bsd.go b/src/net/interface_bsd.go
index b173fbcefc..98d19f2d33 100644
--- a/src/net/interface_bsd.go
+++ b/src/net/interface_bsd.go
@@ -61,13 +61,13 @@ func newLink(m *syscall.InterfaceMessage) (*Interface, error) {
m.Data = m.Data[unsafe.Offsetof(sa.Data):]
var name [syscall.IFNAMSIZ]byte
for i := 0; i < int(sa.Nlen); i++ {
- name[i] = byte(m.Data[i])
+ name[i] = m.Data[i]
}
ifi.Name = string(name[:sa.Nlen])
ifi.MTU = int(m.Header.Data.Mtu)
addr := make([]byte, sa.Alen)
for i := 0; i < int(sa.Alen); i++ {
- addr[i] = byte(m.Data[int(sa.Nlen)+i])
+ addr[i] = m.Data[int(sa.Nlen)+i]
}
ifi.HardwareAddr = addr[:sa.Alen]
}
@@ -166,6 +166,7 @@ func newAddr(ifi *Interface, m *syscall.InterfaceAddrMessage) (*IPNet, error) {
// link-local address as the kernel-internal form.
if ifa.IP.IsLinkLocalUnicast() {
ifa.IP[2], ifa.IP[3] = 0, 0
+ ifa.Zone = ifi.Name
}
}
if ifa.IP == nil || ifa.Mask == nil {
diff --git a/src/net/interface_linux.go b/src/net/interface_linux.go
index 5e391b28b0..b8f57fd7db 100644
--- a/src/net/interface_linux.go
+++ b/src/net/interface_linux.go
@@ -193,6 +193,9 @@ func newAddr(ifi *Interface, ifam *syscall.IfAddrmsg, attrs []syscall.NetlinkRou
case syscall.AF_INET6:
ifa := &IPNet{IP: make(IP, IPv6len), Mask: CIDRMask(int(ifam.Prefixlen), 8*IPv6len)}
copy(ifa.IP, a.Value[:])
+ if ifa.IP.IsLinkLocalUnicast() {
+ ifa.Zone = ifi.Name
+ }
return ifa
}
}
diff --git a/src/net/interface_test.go b/src/net/interface_test.go
index 1487acf601..c3e1ee231f 100644
--- a/src/net/interface_test.go
+++ b/src/net/interface_test.go
@@ -5,6 +5,7 @@
package net
import (
+ "internal/testenv"
"reflect"
"runtime"
"testing"
@@ -56,6 +57,10 @@ type routeStats struct {
}
func TestInterfaces(t *testing.T) {
+ if runtime.GOOS == "freebsd" && runtime.GOARCH == "arm" {
+ // 100% flaky, actually, at least on some FreeBSD versions
+ testenv.SkipFlaky(t, 15262)
+ }
ift, err := Interfaces()
if err != nil {
t.Fatal(err)
@@ -178,10 +183,16 @@ func testInterfaceMulticastAddrs(t *testing.T, ifi *Interface) (nmaf4, nmaf6 int
}
func testAddrs(t *testing.T, ifat []Addr) (naf4, naf6 int) {
+ // Note: BSD variants allow assigning any IPv4/IPv6 address
+ // prefix to IP interface. For example,
+ // - 0.0.0.0/0 through 255.255.255.255/32
+ // - ::/0 through ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128
+ // In other words, there is no tightly-coupled combination of
+ // interface address prefixes and connected routes.
for _, ifa := range ifat {
switch ifa := ifa.(type) {
case *IPNet:
- if ifa == nil || ifa.IP == nil || ifa.IP.IsUnspecified() || ifa.IP.IsMulticast() || ifa.Mask == nil {
+ if ifa == nil || ifa.IP == nil || ifa.IP.IsMulticast() || ifa.Mask == nil {
t.Errorf("unexpected value: %#v", ifa)
continue
}
@@ -210,11 +221,15 @@ func testAddrs(t *testing.T, ifat []Addr) (naf4, naf6 int) {
t.Errorf("unexpected prefix length for IPv6 loopback: %d/%d", prefixLen, maxPrefixLen)
continue
}
+ if ifa.IP.IsLinkLocalUnicast() && ifa.Zone == "" {
+ t.Errorf("no IPv6 zone identifier found: %#v", ifa)
+ continue
+ }
naf6++
}
t.Logf("interface address %q", ifa.String())
case *IPAddr:
- if ifa == nil || ifa.IP == nil || ifa.IP.IsUnspecified() || ifa.IP.IsMulticast() {
+ if ifa == nil || ifa.IP == nil || ifa.IP.IsMulticast() {
t.Errorf("unexpected value: %#v", ifa)
continue
}
@@ -228,7 +243,7 @@ func testAddrs(t *testing.T, ifat []Addr) (naf4, naf6 int) {
if ifa.IP.To16() != nil && ifa.IP.To4() == nil {
naf6++
}
- t.Logf("interface address %s", ifa.String())
+ t.Logf("interface address %q", ifa.String())
default:
t.Errorf("unexpected type: %T", ifa)
}
diff --git a/src/net/interface_windows.go b/src/net/interface_windows.go
index 8b976e585f..a0b26c3750 100644
--- a/src/net/interface_windows.go
+++ b/src/net/interface_windows.go
@@ -158,6 +158,9 @@ func interfaceAddrTable(ifi *Interface) ([]Addr, error) {
l = addrPrefixLen(pfx6, IP(sa.Addr[:]))
}
ifa := &IPNet{IP: make(IP, IPv6len), Mask: CIDRMask(l, 8*IPv6len)}
+ if ifa.IP.IsLinkLocalUnicast() {
+ ifa.Zone = syscall.UTF16ToString((*(*[10000]uint16)(unsafe.Pointer(aa.FriendlyName)))[:])
+ }
copy(ifa.IP, sa.Addr[:])
ifat = append(ifat, ifa)
}
diff --git a/src/net/ip.go b/src/net/ip.go
index 0501f5a6a3..e8b0fd990b 100644
--- a/src/net/ip.go
+++ b/src/net/ip.go
@@ -36,6 +36,7 @@ type IPMask []byte
type IPNet struct {
IP IP // network number
Mask IPMask // network mask
+ Zone string // IPv6 scoped addressing zone
}
// IPv4 returns the IP address (in 16-byte form) of the
@@ -252,9 +253,11 @@ func (ip IP) Mask(mask IPMask) IP {
}
// String returns the string form of the IP address ip.
-// If the address is an IPv4 address, the string representation
-// is dotted decimal ("74.125.19.99"). Otherwise the representation
-// is IPv6 ("2001:4860:0:2001::68").
+// It returns one of 4 forms:
+// - "<nil>", if ip has length 0
+// - dotted decimal ("192.0.2.1"), if ip is an IPv4 or IP4-mapped IPv6 address
+// - IPv6 ("2001:db9::1"), if ip is a valid IPv6 address
+// - the hexadecimal form of ip, without punctuation, if no other cases apply
func (ip IP) String() string {
p := ip
@@ -270,7 +273,7 @@ func (ip IP) String() string {
uitoa(uint(p4[3]))
}
if len(p) != IPv6len {
- return "?"
+ return hexString(ip)
}
// Find longest run of zeros.
@@ -312,6 +315,14 @@ func (ip IP) String() string {
return string(b)
}
+func hexString(b []byte) string {
+ s := make([]byte, len(b)*2)
+ for i, tn := range b {
+ s[i*2], s[i*2+1] = hexDigit[tn>>4], hexDigit[tn&0xf]
+ }
+ return string(s)
+}
+
// ipEmptyString is like ip.String except that it returns
// an empty string when ip is unset.
func ipEmptyString(ip IP) string {
@@ -426,11 +437,7 @@ func (m IPMask) String() string {
if len(m) == 0 {
return "<nil>"
}
- buf := make([]byte, len(m)*2)
- for i, b := range m {
- buf[i*2], buf[i*2+1] = hexDigit[b>>4], hexDigit[b&0xf]
- }
- return string(buf)
+ return hexString(m)
}
func networkNumberAndMask(n *IPNet) (ip IP, m IPMask) {
@@ -488,11 +495,15 @@ func (n *IPNet) String() string {
if nn == nil || m == nil {
return "<nil>"
}
+ ip := nn.String()
+ if n.Zone != "" {
+ ip = ip + "%" + n.Zone
+ }
l := simpleMaskLength(m)
if l == -1 {
- return nn.String() + "/" + m.String()
+ return ip + "/" + m.String()
}
- return nn.String() + "/" + uitoa(uint(l))
+ return ip + "/" + uitoa(uint(l))
}
// Parse IPv4 address (d.d.d.d).
@@ -664,17 +675,18 @@ func ParseCIDR(s string) (IP, *IPNet, error) {
if i < 0 {
return nil, nil, &ParseError{Type: "CIDR address", Text: s}
}
+ var zone string
addr, mask := s[:i], s[i+1:]
iplen := IPv4len
ip := parseIPv4(addr)
if ip == nil {
iplen = IPv6len
- ip, _ = parseIPv6(addr, false)
+ ip, zone = parseIPv6(addr, true)
}
n, i, ok := dtoi(mask, 0)
if ip == nil || !ok || i != len(mask) || n < 0 || n > 8*iplen {
return nil, nil, &ParseError{Type: "CIDR address", Text: s}
}
m := CIDRMask(n, 8*iplen)
- return ip, &IPNet{IP: ip.Mask(m), Mask: m}, nil
+ return ip, &IPNet{IP: ip.Mask(m), Mask: m, Zone: zone}, nil
}
diff --git a/src/net/ip_test.go b/src/net/ip_test.go
index 2006085818..1d67057d6a 100644
--- a/src/net/ip_test.go
+++ b/src/net/ip_test.go
@@ -5,6 +5,7 @@
package net
import (
+ "bytes"
"reflect"
"runtime"
"testing"
@@ -124,30 +125,119 @@ func TestMarshalEmptyIP(t *testing.T) {
}
var ipStringTests = []struct {
- in IP
- out string // see RFC 5952
+ in IP // see RFC 791 and RFC 4291
+ str string // see RFC 791, RFC 4291 and RFC 5952
+ byt []byte
+ error
}{
- {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0x1, 0x23, 0, 0x12, 0, 0x1}, "2001:db8::123:12:1"},
- {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1}, "2001:db8::1"},
- {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1}, "2001:db8:0:1:0:1:0:1"},
- {IP{0x20, 0x1, 0xd, 0xb8, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0}, "2001:db8:1:0:1:0:1:0"},
- {IP{0x20, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0x1}, "2001::1:0:0:1"},
- {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0}, "2001:db8:0:0:1::"},
- {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0x1}, "2001:db8::1:0:0:1"},
- {IP{0x20, 0x1, 0xD, 0xB8, 0, 0, 0, 0, 0, 0xA, 0, 0xB, 0, 0xC, 0, 0xD}, "2001:db8::a:b:c:d"},
- {IPv4(192, 168, 0, 1), "192.168.0.1"},
- {nil, ""},
+ // IPv4 address
+ {
+ IP{192, 0, 2, 1},
+ "192.0.2.1",
+ []byte("192.0.2.1"),
+ nil,
+ },
+ {
+ IP{0, 0, 0, 0},
+ "0.0.0.0",
+ []byte("0.0.0.0"),
+ nil,
+ },
+
+ // IPv4-mapped IPv6 address
+ {
+ IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 0, 2, 1},
+ "192.0.2.1",
+ []byte("192.0.2.1"),
+ nil,
+ },
+ {
+ IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 0, 0, 0},
+ "0.0.0.0",
+ []byte("0.0.0.0"),
+ nil,
+ },
+
+ // IPv6 address
+ {
+ IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0x1, 0x23, 0, 0x12, 0, 0x1},
+ "2001:db8::123:12:1",
+ []byte("2001:db8::123:12:1"),
+ nil,
+ },
+ {
+ IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1},
+ "2001:db8::1",
+ []byte("2001:db8::1"),
+ nil,
+ },
+ {
+ IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1},
+ "2001:db8:0:1:0:1:0:1",
+ []byte("2001:db8:0:1:0:1:0:1"),
+ nil,
+ },
+ {
+ IP{0x20, 0x1, 0xd, 0xb8, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0},
+ "2001:db8:1:0:1:0:1:0",
+ []byte("2001:db8:1:0:1:0:1:0"),
+ nil,
+ },
+ {
+ IP{0x20, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0x1},
+ "2001::1:0:0:1",
+ []byte("2001::1:0:0:1"),
+ nil,
+ },
+ {
+ IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0},
+ "2001:db8:0:0:1::",
+ []byte("2001:db8:0:0:1::"),
+ nil,
+ },
+ {
+ IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0x1},
+ "2001:db8::1:0:0:1",
+ []byte("2001:db8::1:0:0:1"),
+ nil,
+ },
+ {
+ IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0xa, 0, 0xb, 0, 0xc, 0, 0xd},
+ "2001:db8::a:b:c:d",
+ []byte("2001:db8::a:b:c:d"),
+ nil,
+ },
+ {
+ IPv6unspecified,
+ "::",
+ []byte("::"),
+ nil,
+ },
+
+ // IP wildcard equivalent address in Dial/Listen API
+ {
+ nil,
+ "<nil>",
+ nil,
+ nil,
+ },
+
+ // Opaque byte sequence
+ {
+ IP{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef},
+ "0123456789abcdef",
+ nil,
+ &AddrError{Err: "invalid IP address", Addr: "0123456789abcdef"},
+ },
}
func TestIPString(t *testing.T) {
for _, tt := range ipStringTests {
- if tt.in != nil {
- if out := tt.in.String(); out != tt.out {
- t.Errorf("IP.String(%v) = %q, want %q", tt.in, out, tt.out)
- }
+ if out := tt.in.String(); out != tt.str {
+ t.Errorf("IP.String(%v) = %q, want %q", tt.in, out, tt.str)
}
- if out, err := tt.in.MarshalText(); string(out) != tt.out || err != nil {
- t.Errorf("IP.MarshalText(%v) = %q, %v, want %q, nil", tt.in, out, err, tt.out)
+ if out, err := tt.in.MarshalText(); !bytes.Equal(out, tt.byt) || !reflect.DeepEqual(err, tt.error) {
+ t.Errorf("IP.MarshalText(%v) = %v, %v, want %v, %v", tt.in, out, err, tt.byt, tt.error)
}
}
}
@@ -237,6 +327,9 @@ var parseCIDRTests = []struct {
{"abcd:2345::/24", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2300::"), Mask: IPMask(ParseIP("ffff:ff00::"))}, nil},
{"2001:DB8::/48", ParseIP("2001:DB8::"), &IPNet{IP: ParseIP("2001:DB8::"), Mask: IPMask(ParseIP("ffff:ffff:ffff::"))}, nil},
{"2001:DB8::1/48", ParseIP("2001:DB8::1"), &IPNet{IP: ParseIP("2001:DB8::"), Mask: IPMask(ParseIP("ffff:ffff:ffff::"))}, nil},
+ {"fe80::%en0/64", ParseIP("fe80::"), &IPNet{IP: ParseIP("fe80::"), Mask: CIDRMask(64, 128), Zone: "en0"}, nil},
+ {"fe80::1%en0/64", ParseIP("fe80::1"), &IPNet{IP: ParseIP("fe80::"), Mask: CIDRMask(64, 128), Zone: "en0"}, nil},
+
{"192.168.1.1/255.255.255.0", nil, nil, &ParseError{Type: "CIDR address", Text: "192.168.1.1/255.255.255.0"}},
{"192.168.1.1/35", nil, nil, &ParseError{Type: "CIDR address", Text: "192.168.1.1/35"}},
{"2001:db8::1/-1", nil, nil, &ParseError{Type: "CIDR address", Text: "2001:db8::1/-1"}},
@@ -283,8 +376,13 @@ var ipNetStringTests = []struct {
out string
}{
{&IPNet{IP: IPv4(192, 168, 1, 0), Mask: CIDRMask(26, 32)}, "192.168.1.0/26"},
+ {&IPNet{IP: IPv4(192, 168, 1, 1), Mask: CIDRMask(26, 32)}, "192.168.1.1/26"},
{&IPNet{IP: IPv4(192, 168, 1, 0), Mask: IPv4Mask(255, 0, 255, 0)}, "192.168.1.0/ff00ff00"},
+ {&IPNet{IP: ParseIP("fe80::"), Mask: CIDRMask(64, 128), Zone: "en0"}, "fe80::%en0/64"},
+ {&IPNet{IP: ParseIP("fe80::1"), Mask: CIDRMask(64, 128), Zone: "en0"}, "fe80::1%en0/64"},
+ {&IPNet{IP: ParseIP("fe80::"), Mask: IPMask(ParseIP("8000:f123:0:cafe::")), Zone: "en0"}, "fe80::%en0/8000f1230000cafe0000000000000000"},
{&IPNet{IP: ParseIP("2001:db8::"), Mask: CIDRMask(55, 128)}, "2001:db8::/55"},
+ {&IPNet{IP: ParseIP("2001:db8::1"), Mask: CIDRMask(55, 128)}, "2001:db8::1/55"},
{&IPNet{IP: ParseIP("2001:db8::"), Mask: IPMask(ParseIP("8000:f123:0:cafe::"))}, "2001:db8::/8000f1230000cafe0000000000000000"},
}
diff --git a/src/net/iprawsock.go b/src/net/iprawsock.go
index 41cfb2311a..173b3cb411 100644
--- a/src/net/iprawsock.go
+++ b/src/net/iprawsock.go
@@ -4,7 +4,10 @@
package net
-import "syscall"
+import (
+ "context"
+ "syscall"
+)
// IPAddr represents the address of an IP end point.
type IPAddr struct {
@@ -47,7 +50,7 @@ func ResolveIPAddr(net, addr string) (*IPAddr, error) {
if net == "" { // a hint wildcard for Go 1.0 undocumented behavior
net = "ip"
}
- afnet, _, err := parseNetwork(net)
+ afnet, _, err := parseNetwork(context.Background(), net)
if err != nil {
return nil, err
}
@@ -56,7 +59,7 @@ func ResolveIPAddr(net, addr string) (*IPAddr, error) {
default:
return nil, UnknownNetworkError(net)
}
- addrs, err := internetAddrList(afnet, addr, noDeadline)
+ addrs, err := internetAddrList(context.Background(), afnet, addr)
if err != nil {
return nil, err
}
@@ -171,7 +174,7 @@ func newIPConn(fd *netFD) *IPConn { return &IPConn{conn{fd}} }
// netProto, which must be "ip", "ip4", or "ip6" followed by a colon
// and a protocol number or name.
func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) {
- c, err := dialIP(netProto, laddr, raddr, noDeadline)
+ c, err := dialIP(context.Background(), netProto, laddr, raddr)
if err != nil {
return nil, &OpError{Op: "dial", Net: netProto, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: err}
}
@@ -183,7 +186,7 @@ func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) {
// methods can be used to receive and send IP packets with per-packet
// addressing.
func ListenIP(netProto string, laddr *IPAddr) (*IPConn, error) {
- c, err := listenIP(netProto, laddr)
+ c, err := listenIP(context.Background(), netProto, laddr)
if err != nil {
return nil, &OpError{Op: "listen", Net: netProto, Source: nil, Addr: laddr.opAddr(), Err: err}
}
diff --git a/src/net/iprawsock_plan9.go b/src/net/iprawsock_plan9.go
index e08f271e9b..6aebea169c 100644
--- a/src/net/iprawsock_plan9.go
+++ b/src/net/iprawsock_plan9.go
@@ -5,8 +5,8 @@
package net
import (
+ "context"
"syscall"
- "time"
)
func (c *IPConn) readFrom(b []byte) (int, *IPAddr, error) {
@@ -25,10 +25,10 @@ func (c *IPConn) writeMsg(b, oob []byte, addr *IPAddr) (n, oobn int, err error)
return 0, 0, syscall.EPLAN9
}
-func dialIP(netProto string, laddr, raddr *IPAddr, deadline time.Time) (*IPConn, error) {
+func dialIP(ctx context.Context, netProto string, laddr, raddr *IPAddr) (*IPConn, error) {
return nil, syscall.EPLAN9
}
-func listenIP(netProto string, laddr *IPAddr) (*IPConn, error) {
+func listenIP(ctx context.Context, netProto string, laddr *IPAddr) (*IPConn, error) {
return nil, syscall.EPLAN9
}
diff --git a/src/net/iprawsock_posix.go b/src/net/iprawsock_posix.go
index b959afe6b4..3e0b060a8a 100644
--- a/src/net/iprawsock_posix.go
+++ b/src/net/iprawsock_posix.go
@@ -7,8 +7,8 @@
package net
import (
+ "context"
"syscall"
- "time"
)
// BUG(mikio): On every POSIX platform, reads from the "ip4" network
@@ -120,8 +120,8 @@ func (c *IPConn) writeMsg(b, oob []byte, addr *IPAddr) (n, oobn int, err error)
return c.fd.writeMsg(b, oob, sa)
}
-func dialIP(netProto string, laddr, raddr *IPAddr, deadline time.Time) (*IPConn, error) {
- network, proto, err := parseNetwork(netProto)
+func dialIP(ctx context.Context, netProto string, laddr, raddr *IPAddr) (*IPConn, error) {
+ network, proto, err := parseNetwork(ctx, netProto)
if err != nil {
return nil, err
}
@@ -133,15 +133,15 @@ func dialIP(netProto string, laddr, raddr *IPAddr, deadline time.Time) (*IPConn,
if raddr == nil {
return nil, errMissingAddress
}
- fd, err := internetSocket(network, laddr, raddr, deadline, syscall.SOCK_RAW, proto, "dial", noCancel)
+ fd, err := internetSocket(ctx, network, laddr, raddr, syscall.SOCK_RAW, proto, "dial")
if err != nil {
return nil, err
}
return newIPConn(fd), nil
}
-func listenIP(netProto string, laddr *IPAddr) (*IPConn, error) {
- network, proto, err := parseNetwork(netProto)
+func listenIP(ctx context.Context, netProto string, laddr *IPAddr) (*IPConn, error) {
+ network, proto, err := parseNetwork(ctx, netProto)
if err != nil {
return nil, err
}
@@ -150,7 +150,7 @@ func listenIP(netProto string, laddr *IPAddr) (*IPConn, error) {
default:
return nil, UnknownNetworkError(netProto)
}
- fd, err := internetSocket(network, laddr, nil, noDeadline, syscall.SOCK_RAW, proto, "listen", noCancel)
+ fd, err := internetSocket(ctx, network, laddr, nil, syscall.SOCK_RAW, proto, "listen")
if err != nil {
return nil, err
}
diff --git a/src/net/ipsock.go b/src/net/ipsock.go
index f093b4926d..24daf173ac 100644
--- a/src/net/ipsock.go
+++ b/src/net/ipsock.go
@@ -6,7 +6,9 @@
package net
-import "time"
+import (
+ "context"
+)
var (
// supportsIPv4 reports whether the platform supports IPv4
@@ -188,7 +190,7 @@ func JoinHostPort(host, port string) string {
// address or a DNS name, and returns a list of internet protocol
// family addresses. The result contains at least one address when
// error is nil.
-func internetAddrList(net, addr string, deadline time.Time) (addrList, error) {
+func internetAddrList(ctx context.Context, net, addr string) (addrList, error) {
var (
err error
host, port string
@@ -236,7 +238,7 @@ func internetAddrList(net, addr string, deadline time.Time) (addrList, error) {
return addrList{inetaddr(IPAddr{IP: ip, Zone: zone})}, nil
}
// Try as a DNS name.
- ips, err := lookupIPDeadline(host, deadline)
+ ips, err := lookupIPContext(ctx, host)
if err != nil {
return nil, err
}
@@ -249,24 +251,3 @@ func internetAddrList(net, addr string, deadline time.Time) (addrList, error) {
}
return filterAddrList(filter, ips, inetaddr)
}
-
-func zoneToString(zone int) string {
- if zone == 0 {
- return ""
- }
- if ifi, err := InterfaceByIndex(zone); err == nil {
- return ifi.Name
- }
- return uitoa(uint(zone))
-}
-
-func zoneToInt(zone string) int {
- if zone == "" {
- return 0
- }
- if ifi, err := InterfaceByName(zone); err == nil {
- return ifi.Index
- }
- n, _, _ := dtoi(zone, 0)
- return n
-}
diff --git a/src/net/ipsock_plan9.go b/src/net/ipsock_plan9.go
index f7c2b44688..2b84683eeb 100644
--- a/src/net/ipsock_plan9.go
+++ b/src/net/ipsock_plan9.go
@@ -7,6 +7,7 @@
package net
import (
+ "context"
"os"
"syscall"
)
@@ -99,7 +100,7 @@ func readPlan9Addr(proto, filename string) (addr Addr, err error) {
return addr, nil
}
-func startPlan9(net string, addr Addr) (ctl *os.File, dest, proto, name string, err error) {
+func startPlan9(ctx context.Context, net string, addr Addr) (ctl *os.File, dest, proto, name string, err error) {
var (
ip IP
port int
@@ -118,7 +119,7 @@ func startPlan9(net string, addr Addr) (ctl *os.File, dest, proto, name string,
return
}
- clone, dest, err := queryCS1(proto, ip, port)
+ clone, dest, err := queryCS1(ctx, proto, ip, port)
if err != nil {
return
}
@@ -135,8 +136,8 @@ func startPlan9(net string, addr Addr) (ctl *os.File, dest, proto, name string,
return f, dest, proto, string(buf[:n]), nil
}
-func netErr(e error) {
- oe, ok := e.(*OpError)
+func fixErr(err error) {
+ oe, ok := err.(*OpError)
if !ok {
return
}
@@ -165,9 +166,34 @@ func netErr(e error) {
}
}
-func dialPlan9(net string, laddr, raddr Addr) (fd *netFD, err error) {
- defer func() { netErr(err) }()
- f, dest, proto, name, err := startPlan9(net, raddr)
+func dialPlan9(ctx context.Context, net string, laddr, raddr Addr) (fd *netFD, err error) {
+ defer func() { fixErr(err) }()
+ type res struct {
+ fd *netFD
+ err error
+ }
+ resc := make(chan res)
+ go func() {
+ testHookDialChannel()
+ fd, err := dialPlan9Blocking(ctx, net, laddr, raddr)
+ select {
+ case resc <- res{fd, err}:
+ case <-ctx.Done():
+ if fd != nil {
+ fd.Close()
+ }
+ }
+ }()
+ select {
+ case res := <-resc:
+ return res.fd, res.err
+ case <-ctx.Done():
+ return nil, mapErr(ctx.Err())
+ }
+}
+
+func dialPlan9Blocking(ctx context.Context, net string, laddr, raddr Addr) (fd *netFD, err error) {
+ f, dest, proto, name, err := startPlan9(ctx, net, raddr)
if err != nil {
return nil, err
}
@@ -190,9 +216,9 @@ func dialPlan9(net string, laddr, raddr Addr) (fd *netFD, err error) {
return newFD(proto, name, f, data, laddr, raddr)
}
-func listenPlan9(net string, laddr Addr) (fd *netFD, err error) {
- defer func() { netErr(err) }()
- f, dest, proto, name, err := startPlan9(net, laddr)
+func listenPlan9(ctx context.Context, net string, laddr Addr) (fd *netFD, err error) {
+ defer func() { fixErr(err) }()
+ f, dest, proto, name, err := startPlan9(ctx, net, laddr)
if err != nil {
return nil, err
}
@@ -214,7 +240,7 @@ func (fd *netFD) netFD() (*netFD, error) {
}
func (fd *netFD) acceptPlan9() (nfd *netFD, err error) {
- defer func() { netErr(err) }()
+ defer func() { fixErr(err) }()
if err := fd.readLock(); err != nil {
return nil, err
}
diff --git a/src/net/ipsock_posix.go b/src/net/ipsock_posix.go
index 28cdb210ae..abe90ac0e6 100644
--- a/src/net/ipsock_posix.go
+++ b/src/net/ipsock_posix.go
@@ -4,14 +4,12 @@
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
-// Internet protocol family sockets for POSIX
-
package net
import (
+ "context"
"runtime"
"syscall"
- "time"
)
// BUG(rsc,mikio): On DragonFly BSD and OpenBSD, listening on the
@@ -52,7 +50,7 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
}{
// IPv6 communication capability
{laddr: TCPAddr{IP: ParseIP("::1")}, value: 1},
- // IPv6 IPv4-mapped address communication capability
+ // IPv4-mapped IPv6 address communication capability
{laddr: TCPAddr{IP: IPv4(127, 0, 0, 1)}, value: 0},
}
var supps [2]bool
@@ -155,10 +153,9 @@ func favoriteAddrFamily(net string, laddr, raddr sockaddr, mode string) (family
}
// Internet sockets (TCP, UDP, IP)
-
-func internetSocket(net string, laddr, raddr sockaddr, deadline time.Time, sotype, proto int, mode string, cancel <-chan struct{}) (fd *netFD, err error) {
+func internetSocket(ctx context.Context, net string, laddr, raddr sockaddr, sotype, proto int, mode string) (fd *netFD, err error) {
family, ipv6only := favoriteAddrFamily(net, laddr, raddr, mode)
- return socket(net, family, sotype, proto, ipv6only, laddr, raddr, deadline, cancel)
+ return socket(ctx, net, family, sotype, proto, ipv6only, laddr, raddr)
}
func ipToSockaddr(family int, ip IP, port int, zone string) (syscall.Sockaddr, error) {
@@ -167,27 +164,35 @@ func ipToSockaddr(family int, ip IP, port int, zone string) (syscall.Sockaddr, e
if len(ip) == 0 {
ip = IPv4zero
}
- if ip = ip.To4(); ip == nil {
+ ip4 := ip.To4()
+ if ip4 == nil {
return nil, &AddrError{Err: "non-IPv4 address", Addr: ip.String()}
}
sa := &syscall.SockaddrInet4{Port: port}
- copy(sa.Addr[:], ip)
+ copy(sa.Addr[:], ip4)
return sa, nil
case syscall.AF_INET6:
- if len(ip) == 0 {
- ip = IPv6zero
- }
- // IPv4 callers use 0.0.0.0 to mean "announce on any available address".
- // In IPv6 mode, Linux treats that as meaning "announce on 0.0.0.0",
- // which it refuses to do. Rewrite to the IPv6 unspecified address.
- if ip.Equal(IPv4zero) {
+ // In general, an IP wildcard address, which is either
+ // "0.0.0.0" or "::", means the entire IP addressing
+ // space. For some historical reason, it is used to
+ // specify "any available address" on some operations
+ // of IP node.
+ //
+ // When the IP node supports IPv4-mapped IPv6 address,
+ // we allow an listener to listen to the wildcard
+ // address of both IP addressing spaces by specifying
+ // IPv6 wildcard address.
+ if len(ip) == 0 || ip.Equal(IPv4zero) {
ip = IPv6zero
}
- if ip = ip.To16(); ip == nil {
+ // We accept any IPv6 address including IPv4-mapped
+ // IPv6 address.
+ ip6 := ip.To16()
+ if ip6 == nil {
return nil, &AddrError{Err: "non-IPv6 address", Addr: ip.String()}
}
sa := &syscall.SockaddrInet6{Port: port, ZoneId: uint32(zoneToInt(zone))}
- copy(sa.Addr[:], ip)
+ copy(sa.Addr[:], ip6)
return sa, nil
}
return nil, &AddrError{Err: "invalid address family", Addr: ip.String()}
diff --git a/src/net/listen_test.go b/src/net/listen_test.go
index a4320eb5a5..6037f3600d 100644
--- a/src/net/listen_test.go
+++ b/src/net/listen_test.go
@@ -8,6 +8,7 @@ package net
import (
"fmt"
+ "internal/testenv"
"os"
"runtime"
"syscall"
@@ -483,13 +484,12 @@ func checkDualStackAddrFamily(fd *netFD) error {
}
func TestWildWildcardListener(t *testing.T) {
+ testenv.MustHaveExternalNetwork(t)
+
switch runtime.GOOS {
case "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
- if testing.Short() || !*testExternal {
- t.Skip("avoid external network")
- }
defer func() {
if p := recover(); p != nil {
@@ -527,12 +527,17 @@ var ipv4MulticastListenerTests = []struct {
// test listener with same address family, same group address and same
// port.
func TestIPv4MulticastListener(t *testing.T) {
+ testenv.MustHaveExternalNetwork(t)
+
switch runtime.GOOS {
case "android", "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
case "solaris":
t.Skipf("not supported on solaris, see golang.org/issue/7399")
}
+ if !supportsIPv4 {
+ t.Skip("IPv4 is not supported")
+ }
closer := func(cs []*UDPConn) {
for _, c := range cs {
@@ -548,7 +553,7 @@ func TestIPv4MulticastListener(t *testing.T) {
// routing stuff for finding out an appropriate
// nexthop containing both network and link layer
// adjacencies.
- if ifi == nil && (testing.Short() || !*testExternal) {
+ if ifi == nil || !*testIPv4 {
continue
}
for _, tt := range ipv4MulticastListenerTests {
@@ -597,6 +602,8 @@ var ipv6MulticastListenerTests = []struct {
// test listener with same address family, same group address and same
// port.
func TestIPv6MulticastListener(t *testing.T) {
+ testenv.MustHaveExternalNetwork(t)
+
switch runtime.GOOS {
case "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
@@ -604,7 +611,7 @@ func TestIPv6MulticastListener(t *testing.T) {
t.Skipf("not supported on solaris, see issue 7399")
}
if !supportsIPv6 {
- t.Skip("ipv6 is not supported")
+ t.Skip("IPv6 is not supported")
}
if os.Getuid() != 0 {
t.Skip("must be root")
@@ -624,7 +631,7 @@ func TestIPv6MulticastListener(t *testing.T) {
// routing stuff for finding out an appropriate
// nexthop containing both network and link layer
// adjacencies.
- if ifi == nil && (testing.Short() || !*testExternal || !*testIPv6) {
+ if ifi == nil && !*testIPv6 {
continue
}
for _, tt := range ipv6MulticastListenerTests {
diff --git a/src/net/lookup.go b/src/net/lookup.go
index ab6886ddff..5e60011165 100644
--- a/src/net/lookup.go
+++ b/src/net/lookup.go
@@ -5,8 +5,8 @@
package net
import (
+ "context"
"internal/singleflight"
- "time"
)
// protocols contains minimal mappings between internet protocol
@@ -33,7 +33,7 @@ func LookupHost(host string) (addrs []string, err error) {
if ip := ParseIP(host); ip != nil {
return []string{host}, nil
}
- return lookupHost(host)
+ return lookupHost(context.Background(), host)
}
// LookupIP looks up host using the local resolver.
@@ -47,7 +47,7 @@ func LookupIP(host string) (ips []IP, err error) {
if ip := ParseIP(host); ip != nil {
return []IP{ip}, nil
}
- addrs, err := lookupIPMerge(host)
+ addrs, err := lookupIPMerge(context.Background(), host)
if err != nil {
return
}
@@ -63,9 +63,9 @@ var lookupGroup singleflight.Group
// lookupIPMerge wraps lookupIP, but makes sure that for any given
// host, only one lookup is in-flight at a time. The returned memory
// is always owned by the caller.
-func lookupIPMerge(host string) (addrs []IPAddr, err error) {
+func lookupIPMerge(ctx context.Context, host string) (addrs []IPAddr, err error) {
addrsi, err, shared := lookupGroup.Do(host, func() (interface{}, error) {
- return testHookLookupIP(lookupIP, host)
+ return testHookLookupIP(ctx, lookupIP, host)
})
return lookupIPReturn(addrsi, err, shared)
}
@@ -85,37 +85,26 @@ func lookupIPReturn(addrsi interface{}, err error, shared bool) ([]IPAddr, error
return addrs, nil
}
-// lookupIPDeadline looks up a hostname with a deadline.
-func lookupIPDeadline(host string, deadline time.Time) (addrs []IPAddr, err error) {
- if deadline.IsZero() {
- return lookupIPMerge(host)
- }
-
- // We could push the deadline down into the name resolution
- // functions. However, the most commonly used implementation
- // calls getaddrinfo, which has no timeout.
-
- timeout := deadline.Sub(time.Now())
- if timeout <= 0 {
- return nil, errTimeout
- }
- t := time.NewTimer(timeout)
- defer t.Stop()
+// lookupIPContext looks up a hostname with a context.
+func lookupIPContext(ctx context.Context, host string) (addrs []IPAddr, err error) {
+ // TODO(bradfitz): when adding trace hooks later here, make
+ // sure the tracing is done outside of the singleflight
+ // merging. Both callers should see the DNS lookup delay, even
+ // if it's only being done once. The r.Shared bit can be
+ // included in the trace for callers who need it.
ch := lookupGroup.DoChan(host, func() (interface{}, error) {
- return testHookLookupIP(lookupIP, host)
+ return testHookLookupIP(ctx, lookupIP, host)
})
select {
- case <-t.C:
+ case <-ctx.Done():
// The DNS lookup timed out for some reason. Force
// future requests to start the DNS lookup again
// rather than waiting for the current lookup to
// complete. See issue 8602.
lookupGroup.Forget(host)
-
- return nil, errTimeout
-
+ return nil, mapErr(ctx.Err())
case r := <-ch:
return lookupIPReturn(r.Val, r.Err, r.Shared)
}
@@ -123,14 +112,9 @@ func lookupIPDeadline(host string, deadline time.Time) (addrs []IPAddr, err erro
// LookupPort looks up the port for the given network and service.
func LookupPort(network, service string) (port int, err error) {
- if service == "" {
- // Lock in the legacy behavior that an empty string
- // means port 0. See Issue 13610.
- return 0, nil
- }
- port, _, ok := dtoi(service, 0)
- if !ok && port != big && port != -big {
- port, err = lookupPort(network, service)
+ port, needsLookup := parsePort(service)
+ if needsLookup {
+ port, err = lookupPort(context.Background(), network, service)
if err != nil {
return 0, err
}
@@ -146,7 +130,7 @@ func LookupPort(network, service string) (port int, err error) {
// LookupHost or LookupIP directly; both take care of resolving
// the canonical name as part of the lookup.
func LookupCNAME(name string) (cname string, err error) {
- return lookupCNAME(name)
+ return lookupCNAME(context.Background(), name)
}
// LookupSRV tries to resolve an SRV query of the given service,
@@ -159,26 +143,26 @@ func LookupCNAME(name string) (cname string, err error) {
// publishing SRV records under non-standard names, if both service
// and proto are empty strings, LookupSRV looks up name directly.
func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) {
- return lookupSRV(service, proto, name)
+ return lookupSRV(context.Background(), service, proto, name)
}
// LookupMX returns the DNS MX records for the given domain name sorted by preference.
func LookupMX(name string) (mxs []*MX, err error) {
- return lookupMX(name)
+ return lookupMX(context.Background(), name)
}
// LookupNS returns the DNS NS records for the given domain name.
func LookupNS(name string) (nss []*NS, err error) {
- return lookupNS(name)
+ return lookupNS(context.Background(), name)
}
// LookupTXT returns the DNS TXT records for the given domain name.
func LookupTXT(name string) (txts []string, err error) {
- return lookupTXT(name)
+ return lookupTXT(context.Background(), name)
}
// LookupAddr performs a reverse lookup for the given address, returning a list
// of names mapping to that address.
func LookupAddr(addr string) (names []string, err error) {
- return lookupAddr(addr)
+ return lookupAddr(context.Background(), addr)
}
diff --git a/src/net/lookup_plan9.go b/src/net/lookup_plan9.go
index 34ee5354a5..73147a2d3f 100644
--- a/src/net/lookup_plan9.go
+++ b/src/net/lookup_plan9.go
@@ -5,11 +5,12 @@
package net
import (
+ "context"
"errors"
"os"
)
-func query(filename, query string, bufSize int) (res []string, err error) {
+func query(ctx context.Context, filename, query string, bufSize int) (res []string, err error) {
file, err := os.OpenFile(filename, os.O_RDWR, 0)
if err != nil {
return
@@ -39,7 +40,7 @@ func query(filename, query string, bufSize int) (res []string, err error) {
return
}
-func queryCS(net, host, service string) (res []string, err error) {
+func queryCS(ctx context.Context, net, host, service string) (res []string, err error) {
switch net {
case "tcp4", "tcp6":
net = "tcp"
@@ -49,15 +50,15 @@ func queryCS(net, host, service string) (res []string, err error) {
if host == "" {
host = "*"
}
- return query(netdir+"/cs", net+"!"+host+"!"+service, 128)
+ return query(ctx, netdir+"/cs", net+"!"+host+"!"+service, 128)
}
-func queryCS1(net string, ip IP, port int) (clone, dest string, err error) {
+func queryCS1(ctx context.Context, net string, ip IP, port int) (clone, dest string, err error) {
ips := "*"
if len(ip) != 0 && !ip.IsUnspecified() {
ips = ip.String()
}
- lines, err := queryCS(net, ips, itoa(port))
+ lines, err := queryCS(ctx, net, ips, itoa(port))
if err != nil {
return
}
@@ -69,8 +70,8 @@ func queryCS1(net string, ip IP, port int) (clone, dest string, err error) {
return
}
-func queryDNS(addr string, typ string) (res []string, err error) {
- return query(netdir+"/dns", addr+" "+typ, 1024)
+func queryDNS(ctx context.Context, addr string, typ string) (res []string, err error) {
+ return query(ctx, netdir+"/dns", addr+" "+typ, 1024)
}
// toLower returns a lower-case version of in. Restricting us to
@@ -96,8 +97,8 @@ func toLower(in string) string {
// lookupProtocol looks up IP protocol name and returns
// the corresponding protocol number.
-func lookupProtocol(name string) (proto int, err error) {
- lines, err := query(netdir+"/cs", "!protocol="+toLower(name), 128)
+func lookupProtocol(ctx context.Context, name string) (proto int, err error) {
+ lines, err := query(ctx, netdir+"/cs", "!protocol="+toLower(name), 128)
if err != nil {
return 0, err
}
@@ -115,10 +116,10 @@ func lookupProtocol(name string) (proto int, err error) {
return 0, UnknownNetworkError(name)
}
-func lookupHost(host string) (addrs []string, err error) {
+func lookupHost(ctx context.Context, host string) (addrs []string, err error) {
// Use netdir/cs instead of netdir/dns because cs knows about
// host names in local network (e.g. from /lib/ndb/local)
- lines, err := queryCS("net", host, "1")
+ lines, err := queryCS(ctx, "net", host, "1")
if err != nil {
return
}
@@ -146,8 +147,8 @@ loop:
return
}
-func lookupIP(host string) (addrs []IPAddr, err error) {
- lits, err := LookupHost(host)
+func lookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
+ lits, err := lookupHost(ctx, host)
if err != nil {
return
}
@@ -161,14 +162,14 @@ func lookupIP(host string) (addrs []IPAddr, err error) {
return
}
-func lookupPort(network, service string) (port int, err error) {
+func lookupPort(ctx context.Context, network, service string) (port int, err error) {
switch network {
case "tcp4", "tcp6":
network = "tcp"
case "udp4", "udp6":
network = "udp"
}
- lines, err := queryCS(network, "127.0.0.1", service)
+ lines, err := queryCS(ctx, network, "127.0.0.1", service)
if err != nil {
return
}
@@ -190,8 +191,8 @@ func lookupPort(network, service string) (port int, err error) {
return 0, unknownPortError
}
-func lookupCNAME(name string) (cname string, err error) {
- lines, err := queryDNS(name, "cname")
+func lookupCNAME(ctx context.Context, name string) (cname string, err error) {
+ lines, err := queryDNS(ctx, name, "cname")
if err != nil {
return
}
@@ -203,14 +204,14 @@ func lookupCNAME(name string) (cname string, err error) {
return "", errors.New("bad response from ndb/dns")
}
-func lookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) {
+func lookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*SRV, err error) {
var target string
if service == "" && proto == "" {
target = name
} else {
target = "_" + service + "._" + proto + "." + name
}
- lines, err := queryDNS(target, "srv")
+ lines, err := queryDNS(ctx, target, "srv")
if err != nil {
return
}
@@ -232,8 +233,8 @@ func lookupSRV(service, proto, name string) (cname string, addrs []*SRV, err err
return
}
-func lookupMX(name string) (mx []*MX, err error) {
- lines, err := queryDNS(name, "mx")
+func lookupMX(ctx context.Context, name string) (mx []*MX, err error) {
+ lines, err := queryDNS(ctx, name, "mx")
if err != nil {
return
}
@@ -250,8 +251,8 @@ func lookupMX(name string) (mx []*MX, err error) {
return
}
-func lookupNS(name string) (ns []*NS, err error) {
- lines, err := queryDNS(name, "ns")
+func lookupNS(ctx context.Context, name string) (ns []*NS, err error) {
+ lines, err := queryDNS(ctx, name, "ns")
if err != nil {
return
}
@@ -265,8 +266,8 @@ func lookupNS(name string) (ns []*NS, err error) {
return
}
-func lookupTXT(name string) (txt []string, err error) {
- lines, err := queryDNS(name, "txt")
+func lookupTXT(ctx context.Context, name string) (txt []string, err error) {
+ lines, err := queryDNS(ctx, name, "txt")
if err != nil {
return
}
@@ -278,12 +279,12 @@ func lookupTXT(name string) (txt []string, err error) {
return
}
-func lookupAddr(addr string) (name []string, err error) {
+func lookupAddr(ctx context.Context, addr string) (name []string, err error) {
arpa, err := reverseaddr(addr)
if err != nil {
return
}
- lines, err := queryDNS(arpa, "ptr")
+ lines, err := queryDNS(ctx, arpa, "ptr")
if err != nil {
return
}
diff --git a/src/net/lookup_stub.go b/src/net/lookup_stub.go
index a8625905e4..bd096b3965 100644
--- a/src/net/lookup_stub.go
+++ b/src/net/lookup_stub.go
@@ -6,44 +6,47 @@
package net
-import "syscall"
+import (
+ "context"
+ "syscall"
+)
-func lookupProtocol(name string) (proto int, err error) {
+func lookupProtocol(ctx context.Context, name string) (proto int, err error) {
return 0, syscall.ENOPROTOOPT
}
-func lookupHost(host string) (addrs []string, err error) {
+func lookupHost(ctx context.Context, host string) (addrs []string, err error) {
return nil, syscall.ENOPROTOOPT
}
-func lookupIP(host string) (addrs []IPAddr, err error) {
+func lookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
return nil, syscall.ENOPROTOOPT
}
-func lookupPort(network, service string) (port int, err error) {
+func lookupPort(ctx context.Context, network, service string) (port int, err error) {
return 0, syscall.ENOPROTOOPT
}
-func lookupCNAME(name string) (cname string, err error) {
+func lookupCNAME(ctx context.Context, name string) (cname string, err error) {
return "", syscall.ENOPROTOOPT
}
-func lookupSRV(service, proto, name string) (cname string, srvs []*SRV, err error) {
+func lookupSRV(ctx context.Context, service, proto, name string) (cname string, srvs []*SRV, err error) {
return "", nil, syscall.ENOPROTOOPT
}
-func lookupMX(name string) (mxs []*MX, err error) {
+func lookupMX(ctx context.Context, name string) (mxs []*MX, err error) {
return nil, syscall.ENOPROTOOPT
}
-func lookupNS(name string) (nss []*NS, err error) {
+func lookupNS(ctx context.Context, name string) (nss []*NS, err error) {
return nil, syscall.ENOPROTOOPT
}
-func lookupTXT(name string) (txts []string, err error) {
+func lookupTXT(ctx context.Context, name string) (txts []string, err error) {
return nil, syscall.ENOPROTOOPT
}
-func lookupAddr(addr string) (ptrs []string, err error) {
+func lookupAddr(ctx context.Context, addr string) (ptrs []string, err error) {
return nil, syscall.ENOPROTOOPT
}
diff --git a/src/net/lookup_test.go b/src/net/lookup_test.go
index 6307a8612d..6e54fdba76 100644
--- a/src/net/lookup_test.go
+++ b/src/net/lookup_test.go
@@ -6,6 +6,7 @@ package net
import (
"bytes"
+ "context"
"fmt"
"internal/testenv"
"runtime"
@@ -14,7 +15,7 @@ import (
"time"
)
-func lookupLocalhost(fn func(string) ([]IPAddr, error), host string) ([]IPAddr, error) {
+func lookupLocalhost(ctx context.Context, fn func(context.Context, string) ([]IPAddr, error), host string) ([]IPAddr, error) {
switch host {
case "localhost":
return []IPAddr{
@@ -22,7 +23,7 @@ func lookupLocalhost(fn func(string) ([]IPAddr, error), host string) ([]IPAddr,
{IP: IPv6loopback},
}, nil
default:
- return fn(host)
+ return fn(ctx, host)
}
}
@@ -58,9 +59,10 @@ var lookupGoogleSRVTests = []struct {
}
func TestLookupGoogleSRV(t *testing.T) {
- if testing.Short() && testenv.Builder() == "" || !*testExternal {
- t.Skip("avoid external network")
+ if testenv.Builder() == "" {
+ testenv.MustHaveExternalNetwork(t)
}
+
if !supportsIPv4 || !*testIPv4 {
t.Skip("IPv4 is required")
}
@@ -92,9 +94,10 @@ var lookupGmailMXTests = []struct {
}
func TestLookupGmailMX(t *testing.T) {
- if testing.Short() && testenv.Builder() == "" || !*testExternal {
- t.Skip("avoid external network")
+ if testenv.Builder() == "" {
+ testenv.MustHaveExternalNetwork(t)
}
+
if !supportsIPv4 || !*testIPv4 {
t.Skip("IPv4 is required")
}
@@ -123,9 +126,10 @@ var lookupGmailNSTests = []struct {
}
func TestLookupGmailNS(t *testing.T) {
- if testing.Short() && testenv.Builder() == "" || !*testExternal {
- t.Skip("avoid external network")
+ if testenv.Builder() == "" {
+ testenv.MustHaveExternalNetwork(t)
}
+
if !supportsIPv4 || !*testIPv4 {
t.Skip("IPv4 is required")
}
@@ -154,9 +158,10 @@ var lookupGmailTXTTests = []struct {
}
func TestLookupGmailTXT(t *testing.T) {
- if testing.Short() && testenv.Builder() == "" || !*testExternal {
- t.Skip("avoid external network")
+ if testenv.Builder() == "" {
+ testenv.MustHaveExternalNetwork(t)
}
+
if !supportsIPv4 || !*testIPv4 {
t.Skip("IPv4 is required")
}
@@ -188,9 +193,10 @@ var lookupGooglePublicDNSAddrTests = []struct {
}
func TestLookupGooglePublicDNSAddr(t *testing.T) {
- if testing.Short() && testenv.Builder() == "" || !*testExternal {
- t.Skip("avoid external network")
+ if testenv.Builder() == "" {
+ testenv.MustHaveExternalNetwork(t)
}
+
if !supportsIPv4 || !supportsIPv6 || !*testIPv4 || !*testIPv6 {
t.Skip("both IPv4 and IPv6 are required")
}
@@ -243,9 +249,10 @@ var lookupIANACNAMETests = []struct {
}
func TestLookupIANACNAME(t *testing.T) {
- if testing.Short() && testenv.Builder() == "" || !*testExternal {
- t.Skip("avoid external network")
+ if testenv.Builder() == "" {
+ testenv.MustHaveExternalNetwork(t)
}
+
if !supportsIPv4 || !*testIPv4 {
t.Skip("IPv4 is required")
}
@@ -269,9 +276,10 @@ var lookupGoogleHostTests = []struct {
}
func TestLookupGoogleHost(t *testing.T) {
- if testing.Short() && testenv.Builder() == "" || !*testExternal {
- t.Skip("avoid external network")
+ if testenv.Builder() == "" {
+ testenv.MustHaveExternalNetwork(t)
}
+
if !supportsIPv4 || !*testIPv4 {
t.Skip("IPv4 is required")
}
@@ -300,9 +308,10 @@ var lookupGoogleIPTests = []struct {
}
func TestLookupGoogleIP(t *testing.T) {
- if testing.Short() && testenv.Builder() == "" || !*testExternal {
- t.Skip("avoid external network")
+ if testenv.Builder() == "" {
+ testenv.MustHaveExternalNetwork(t)
}
+
if !supportsIPv4 || !*testIPv4 {
t.Skip("IPv4 is required")
}
@@ -367,15 +376,20 @@ func TestLookupIPDeadline(t *testing.T) {
const N = 5000
const timeout = 3 * time.Second
+ ctxHalfTimeout, cancel := context.WithTimeout(context.Background(), timeout/2)
+ defer cancel()
+ ctxTimeout, cancel := context.WithTimeout(context.Background(), timeout)
+ defer cancel()
+
c := make(chan error, 2*N)
for i := 0; i < N; i++ {
name := fmt.Sprintf("%d.net-test.golang.org", i)
go func() {
- _, err := lookupIPDeadline(name, time.Now().Add(timeout/2))
+ _, err := lookupIPContext(ctxHalfTimeout, name)
c <- err
}()
go func() {
- _, err := lookupIPDeadline(name, time.Now().Add(timeout))
+ _, err := lookupIPContext(ctxTimeout, name)
c <- err
}()
}
@@ -463,9 +477,10 @@ func TestLookupDotsWithLocalSource(t *testing.T) {
}
func TestLookupDotsWithRemoteSource(t *testing.T) {
- if testing.Short() && testenv.Builder() == "" || !*testExternal {
- t.Skip("avoid external network")
+ if testenv.Builder() == "" {
+ testenv.MustHaveExternalNetwork(t)
}
+
if !supportsIPv4 || !*testIPv4 {
t.Skip("IPv4 is required")
}
@@ -612,6 +627,7 @@ var lookupPortTests = []struct {
{"tcp", "65536", 0, false},
{"udp", "-1", 0, false},
{"udp", "65536", 0, false},
+ {"tcp", "123456789", 0, false},
// Issue 13610: LookupPort("tcp", "")
{"tcp", "", 0, true},
@@ -632,7 +648,7 @@ func TestLookupPort(t *testing.T) {
for _, tt := range lookupPortTests {
if port, err := LookupPort(tt.network, tt.name); port != tt.port || (err == nil) != tt.ok {
- t.Errorf("LookupPort(%q, %q) = %d, %v; want %d", tt.network, tt.name, port, err, tt.port)
+ t.Errorf("LookupPort(%q, %q) = %d, %v; want %d, error=%t", tt.network, tt.name, port, err, tt.port, !tt.ok)
}
}
}
diff --git a/src/net/lookup_unix.go b/src/net/lookup_unix.go
index cd4ddbdb24..5461fe8a41 100644
--- a/src/net/lookup_unix.go
+++ b/src/net/lookup_unix.go
@@ -6,7 +6,10 @@
package net
-import "sync"
+import (
+ "context"
+ "sync"
+)
var onceReadProtocols sync.Once
@@ -40,7 +43,7 @@ func readProtocols() {
// lookupProtocol looks up IP protocol name in /etc/protocols and
// returns correspondent protocol number.
-func lookupProtocol(name string) (int, error) {
+func lookupProtocol(_ context.Context, name string) (int, error) {
onceReadProtocols.Do(readProtocols)
proto, found := protocols[name]
if !found {
@@ -49,7 +52,7 @@ func lookupProtocol(name string) (int, error) {
return proto, nil
}
-func lookupHost(host string) (addrs []string, err error) {
+func lookupHost(ctx context.Context, host string) (addrs []string, err error) {
order := systemConf().hostLookupOrder(host)
if order == hostLookupCgo {
if addrs, err, ok := cgoLookupHost(host); ok {
@@ -58,22 +61,28 @@ func lookupHost(host string) (addrs []string, err error) {
// cgo not available (or netgo); fall back to Go's DNS resolver
order = hostLookupFilesDNS
}
- return goLookupHostOrder(host, order)
+ return goLookupHostOrder(ctx, host, order)
}
-func lookupIP(host string) (addrs []IPAddr, err error) {
+func lookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
order := systemConf().hostLookupOrder(host)
if order == hostLookupCgo {
+ // TODO(bradfitz): push down ctx, or at least its deadline to start
if addrs, err, ok := cgoLookupIP(host); ok {
return addrs, err
}
// cgo not available (or netgo); fall back to Go's DNS resolver
order = hostLookupFilesDNS
}
- return goLookupIPOrder(host, order)
+ return goLookupIPOrder(ctx, host, order)
}
-func lookupPort(network, service string) (int, error) {
+func lookupPort(ctx context.Context, network, service string) (int, error) {
+ // TODO: use the context if there ever becomes a need. Related
+ // is issue 15321. But port lookup generally just involves
+ // local files, and the os package has no context support. The
+ // files might be on a remote filesystem, though. This should
+ // probably race goroutines if ctx != context.Background().
if systemConf().canUseCgo() {
if port, err, ok := cgoLookupPort(network, service); ok {
return port, err
@@ -82,23 +91,24 @@ func lookupPort(network, service string) (int, error) {
return goLookupPort(network, service)
}
-func lookupCNAME(name string) (string, error) {
+func lookupCNAME(ctx context.Context, name string) (string, error) {
if systemConf().canUseCgo() {
+ // TODO: use ctx. issue 15321. Or race goroutines.
if cname, err, ok := cgoLookupCNAME(name); ok {
return cname, err
}
}
- return goLookupCNAME(name)
+ return goLookupCNAME(ctx, name)
}
-func lookupSRV(service, proto, name string) (string, []*SRV, error) {
+func lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) {
var target string
if service == "" && proto == "" {
target = name
} else {
target = "_" + service + "._" + proto + "." + name
}
- cname, rrs, err := lookup(target, dnsTypeSRV)
+ cname, rrs, err := lookup(ctx, target, dnsTypeSRV)
if err != nil {
return "", nil, err
}
@@ -111,8 +121,8 @@ func lookupSRV(service, proto, name string) (string, []*SRV, error) {
return cname, srvs, nil
}
-func lookupMX(name string) ([]*MX, error) {
- _, rrs, err := lookup(name, dnsTypeMX)
+func lookupMX(ctx context.Context, name string) ([]*MX, error) {
+ _, rrs, err := lookup(ctx, name, dnsTypeMX)
if err != nil {
return nil, err
}
@@ -125,8 +135,8 @@ func lookupMX(name string) ([]*MX, error) {
return mxs, nil
}
-func lookupNS(name string) ([]*NS, error) {
- _, rrs, err := lookup(name, dnsTypeNS)
+func lookupNS(ctx context.Context, name string) ([]*NS, error) {
+ _, rrs, err := lookup(ctx, name, dnsTypeNS)
if err != nil {
return nil, err
}
@@ -137,8 +147,8 @@ func lookupNS(name string) ([]*NS, error) {
return nss, nil
}
-func lookupTXT(name string) ([]string, error) {
- _, rrs, err := lookup(name, dnsTypeTXT)
+func lookupTXT(ctx context.Context, name string) ([]string, error) {
+ _, rrs, err := lookup(ctx, name, dnsTypeTXT)
if err != nil {
return nil, err
}
@@ -149,11 +159,11 @@ func lookupTXT(name string) ([]string, error) {
return txts, nil
}
-func lookupAddr(addr string) ([]string, error) {
+func lookupAddr(ctx context.Context, addr string) ([]string, error) {
if systemConf().canUseCgo() {
if ptrs, err, ok := cgoLookupPTR(addr); ok {
return ptrs, err
}
}
- return goLookupPTR(addr)
+ return goLookupPTR(ctx, addr)
}
diff --git a/src/net/lookup_windows.go b/src/net/lookup_windows.go
index 13edc264e8..5f65c2d00d 100644
--- a/src/net/lookup_windows.go
+++ b/src/net/lookup_windows.go
@@ -5,17 +5,13 @@
package net
import (
+ "context"
"os"
"runtime"
"syscall"
"unsafe"
)
-var (
- lookupPort = oldLookupPort
- lookupIP = oldLookupIP
-)
-
func getprotobyname(name string) (proto int, err error) {
p, err := syscall.GetProtoByName(name)
if err != nil {
@@ -25,34 +21,41 @@ func getprotobyname(name string) (proto int, err error) {
}
// lookupProtocol looks up IP protocol name and returns correspondent protocol number.
-func lookupProtocol(name string) (int, error) {
+func lookupProtocol(ctx context.Context, name string) (int, error) {
// GetProtoByName return value is stored in thread local storage.
// Start new os thread before the call to prevent races.
type result struct {
proto int
err error
}
- ch := make(chan result)
+ ch := make(chan result) // unbuffered
go func() {
acquireThread()
defer releaseThread()
runtime.LockOSThread()
defer runtime.UnlockOSThread()
proto, err := getprotobyname(name)
- ch <- result{proto: proto, err: err}
+ select {
+ case ch <- result{proto: proto, err: err}:
+ case <-ctx.Done():
+ }
}()
- r := <-ch
- if r.err != nil {
- if proto, ok := protocols[name]; ok {
- return proto, nil
+ select {
+ case r := <-ch:
+ if r.err != nil {
+ if proto, ok := protocols[name]; ok {
+ return proto, nil
+ }
+ r.err = &DNSError{Err: r.err.Error(), Name: name}
}
- r.err = &DNSError{Err: r.err.Error(), Name: name}
+ return r.proto, r.err
+ case <-ctx.Done():
+ return 0, mapErr(ctx.Err())
}
- return r.proto, r.err
}
-func lookupHost(name string) ([]string, error) {
- ips, err := LookupIP(name)
+func lookupHost(ctx context.Context, name string) ([]string, error) {
+ ips, err := lookupIP(ctx, name)
if err != nil {
return nil, err
}
@@ -63,121 +66,67 @@ func lookupHost(name string) ([]string, error) {
return addrs, nil
}
-func gethostbyname(name string) (addrs []IPAddr, err error) {
- // caller already acquired thread
- h, err := syscall.GetHostByName(name)
- if err != nil {
- return nil, os.NewSyscallError("gethostbyname", err)
- }
- switch h.AddrType {
- case syscall.AF_INET:
- i := 0
- addrs = make([]IPAddr, 100) // plenty of room to grow
- for p := (*[100](*[4]byte))(unsafe.Pointer(h.AddrList)); i < cap(addrs) && p[i] != nil; i++ {
- addrs[i] = IPAddr{IP: IPv4(p[i][0], p[i][1], p[i][2], p[i][3])}
- }
- addrs = addrs[0:i]
- default: // TODO(vcc): Implement non IPv4 address lookups.
- return nil, syscall.EWINDOWS
- }
- return addrs, nil
-}
+func lookupIP(ctx context.Context, name string) ([]IPAddr, error) {
+ // TODO(bradfitz,brainman): use ctx?
-func oldLookupIP(name string) ([]IPAddr, error) {
- // GetHostByName return value is stored in thread local storage.
- // Start new os thread before the call to prevent races.
- type result struct {
+ type ret struct {
addrs []IPAddr
err error
}
- ch := make(chan result)
+ ch := make(chan ret, 1)
go func() {
acquireThread()
defer releaseThread()
- runtime.LockOSThread()
- defer runtime.UnlockOSThread()
- addrs, err := gethostbyname(name)
- ch <- result{addrs: addrs, err: err}
- }()
- r := <-ch
- if r.err != nil {
- r.err = &DNSError{Err: r.err.Error(), Name: name}
- }
- return r.addrs, r.err
-}
-
-func newLookupIP(name string) ([]IPAddr, error) {
- acquireThread()
- defer releaseThread()
- hints := syscall.AddrinfoW{
- Family: syscall.AF_UNSPEC,
- Socktype: syscall.SOCK_STREAM,
- Protocol: syscall.IPPROTO_IP,
- }
- var result *syscall.AddrinfoW
- e := syscall.GetAddrInfoW(syscall.StringToUTF16Ptr(name), nil, &hints, &result)
- if e != nil {
- return nil, &DNSError{Err: os.NewSyscallError("getaddrinfow", e).Error(), Name: name}
- }
- defer syscall.FreeAddrInfoW(result)
- addrs := make([]IPAddr, 0, 5)
- for ; result != nil; result = result.Next {
- addr := unsafe.Pointer(result.Addr)
- switch result.Family {
- case syscall.AF_INET:
- a := (*syscall.RawSockaddrInet4)(addr).Addr
- addrs = append(addrs, IPAddr{IP: IPv4(a[0], a[1], a[2], a[3])})
- case syscall.AF_INET6:
- a := (*syscall.RawSockaddrInet6)(addr).Addr
- zone := zoneToString(int((*syscall.RawSockaddrInet6)(addr).Scope_id))
- addrs = append(addrs, IPAddr{IP: IP{a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]}, Zone: zone})
- default:
- return nil, &DNSError{Err: syscall.EWINDOWS.Error(), Name: name}
+ hints := syscall.AddrinfoW{
+ Family: syscall.AF_UNSPEC,
+ Socktype: syscall.SOCK_STREAM,
+ Protocol: syscall.IPPROTO_IP,
}
- }
- return addrs, nil
-}
-
-func getservbyname(network, service string) (int, error) {
- acquireThread()
- defer releaseThread()
- switch network {
- case "tcp4", "tcp6":
- network = "tcp"
- case "udp4", "udp6":
- network = "udp"
- }
- s, err := syscall.GetServByName(service, network)
- if err != nil {
- return 0, os.NewSyscallError("getservbyname", err)
- }
- return int(syscall.Ntohs(s.Port)), nil
-}
-
-func oldLookupPort(network, service string) (int, error) {
- // GetServByName return value is stored in thread local storage.
- // Start new os thread before the call to prevent races.
- type result struct {
- port int
- err error
- }
- ch := make(chan result)
- go func() {
- acquireThread()
- defer releaseThread()
- runtime.LockOSThread()
- defer runtime.UnlockOSThread()
- port, err := getservbyname(network, service)
- ch <- result{port: port, err: err}
+ var result *syscall.AddrinfoW
+ e := syscall.GetAddrInfoW(syscall.StringToUTF16Ptr(name), nil, &hints, &result)
+ if e != nil {
+ ch <- ret{err: &DNSError{Err: os.NewSyscallError("getaddrinfow", e).Error(), Name: name}}
+ }
+ defer syscall.FreeAddrInfoW(result)
+ addrs := make([]IPAddr, 0, 5)
+ for ; result != nil; result = result.Next {
+ addr := unsafe.Pointer(result.Addr)
+ switch result.Family {
+ case syscall.AF_INET:
+ a := (*syscall.RawSockaddrInet4)(addr).Addr
+ addrs = append(addrs, IPAddr{IP: IPv4(a[0], a[1], a[2], a[3])})
+ case syscall.AF_INET6:
+ a := (*syscall.RawSockaddrInet6)(addr).Addr
+ zone := zoneToString(int((*syscall.RawSockaddrInet6)(addr).Scope_id))
+ addrs = append(addrs, IPAddr{IP: IP{a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]}, Zone: zone})
+ default:
+ ch <- ret{err: &DNSError{Err: syscall.EWINDOWS.Error(), Name: name}}
+ }
+ }
+ ch <- ret{addrs: addrs}
}()
- r := <-ch
- if r.err != nil {
- r.err = &DNSError{Err: r.err.Error(), Name: network + "/" + service}
+ select {
+ case r := <-ch:
+ return r.addrs, r.err
+ case <-ctx.Done():
+ // TODO(bradfitz,brainman): cancel the ongoing
+ // GetAddrInfoW? It would require conditionally using
+ // GetAddrInfoEx with lpOverlapped, which requires
+ // Windows 8 or newer. I guess we'll need oldLookupIP,
+ // newLookupIP, and newerLookUP.
+ //
+ // For now we just let it finish and write to the
+ // buffered channel.
+ return nil, &DNSError{
+ Name: name,
+ Err: ctx.Err().Error(),
+ IsTimeout: ctx.Err() == context.DeadlineExceeded,
+ }
}
- return r.port, r.err
}
-func newLookupPort(network, service string) (int, error) {
+func lookupPort(ctx context.Context, network, service string) (int, error) {
+ // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
acquireThread()
defer releaseThread()
var stype int32
@@ -213,7 +162,8 @@ func newLookupPort(network, service string) (int, error) {
return 0, &DNSError{Err: syscall.EINVAL.Error(), Name: network + "/" + service}
}
-func lookupCNAME(name string) (string, error) {
+func lookupCNAME(ctx context.Context, name string) (string, error) {
+ // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
acquireThread()
defer releaseThread()
var r *syscall.DNSRecord
@@ -233,7 +183,8 @@ func lookupCNAME(name string) (string, error) {
return absDomainName([]byte(cname)), nil
}
-func lookupSRV(service, proto, name string) (string, []*SRV, error) {
+func lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) {
+ // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
acquireThread()
defer releaseThread()
var target string
@@ -258,7 +209,8 @@ func lookupSRV(service, proto, name string) (string, []*SRV, error) {
return absDomainName([]byte(target)), srvs, nil
}
-func lookupMX(name string) ([]*MX, error) {
+func lookupMX(ctx context.Context, name string) ([]*MX, error) {
+ // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
acquireThread()
defer releaseThread()
var r *syscall.DNSRecord
@@ -277,7 +229,8 @@ func lookupMX(name string) ([]*MX, error) {
return mxs, nil
}
-func lookupNS(name string) ([]*NS, error) {
+func lookupNS(ctx context.Context, name string) ([]*NS, error) {
+ // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
acquireThread()
defer releaseThread()
var r *syscall.DNSRecord
@@ -295,7 +248,8 @@ func lookupNS(name string) ([]*NS, error) {
return nss, nil
}
-func lookupTXT(name string) ([]string, error) {
+func lookupTXT(ctx context.Context, name string) ([]string, error) {
+ // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
acquireThread()
defer releaseThread()
var r *syscall.DNSRecord
@@ -316,7 +270,8 @@ func lookupTXT(name string) ([]string, error) {
return txts, nil
}
-func lookupAddr(addr string) ([]string, error) {
+func lookupAddr(ctx context.Context, addr string) ([]string, error) {
+ // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
acquireThread()
defer releaseThread()
arpa, err := reverseaddr(addr)
diff --git a/src/net/lookup_windows_test.go b/src/net/lookup_windows_test.go
index 7ff32b809b..9af2c61b74 100644
--- a/src/net/lookup_windows_test.go
+++ b/src/net/lookup_windows_test.go
@@ -8,6 +8,7 @@ import (
"bytes"
"encoding/json"
"errors"
+ "internal/testenv"
"os/exec"
"reflect"
"regexp"
@@ -24,9 +25,7 @@ func toJson(v interface{}) string {
}
func TestLookupMX(t *testing.T) {
- if testing.Short() || !*testExternal {
- t.Skip("avoid external network")
- }
+ testenv.MustHaveExternalNetwork(t)
for _, server := range nslookupTestServers {
mx, err := LookupMX(server)
@@ -51,9 +50,7 @@ func TestLookupMX(t *testing.T) {
}
func TestLookupCNAME(t *testing.T) {
- if testing.Short() || !*testExternal {
- t.Skip("avoid external network")
- }
+ testenv.MustHaveExternalNetwork(t)
for _, server := range nslookupTestServers {
cname, err := LookupCNAME(server)
@@ -76,9 +73,7 @@ func TestLookupCNAME(t *testing.T) {
}
func TestLookupNS(t *testing.T) {
- if testing.Short() || !*testExternal {
- t.Skip("avoid external network")
- }
+ testenv.MustHaveExternalNetwork(t)
for _, server := range nslookupTestServers {
ns, err := LookupNS(server)
@@ -104,9 +99,7 @@ func TestLookupNS(t *testing.T) {
}
func TestLookupTXT(t *testing.T) {
- if testing.Short() || !*testExternal {
- t.Skip("avoid external network")
- }
+ testenv.MustHaveExternalNetwork(t)
for _, server := range nslookupTestServers {
txt, err := LookupTXT(server)
diff --git a/src/net/mail/message.go b/src/net/mail/message.go
index 12342b368f..0c000697f7 100644
--- a/src/net/mail/message.go
+++ b/src/net/mail/message.go
@@ -5,13 +5,15 @@
/*
Package mail implements parsing of mail messages.
-For the most part, this package follows the syntax as specified by RFC 5322.
+For the most part, this package follows the syntax as specified by RFC 5322 and
+extended by RFC 6532.
Notable divergences:
* Obsolete address formats are not parsed, including addresses with
embedded route information.
* Group addresses are not parsed.
* The full range of spacing (the CFWS syntax element) is not supported,
such as breaking addresses across lines.
+ * No unicode normalization is performed.
*/
package mail
@@ -26,6 +28,7 @@ import (
"net/textproto"
"strings"
"time"
+ "unicode/utf8"
)
var debug = debugT(false)
@@ -180,15 +183,12 @@ func (a *Address) String() string {
}
// Add quotes if needed
- // TODO: rendering quoted local part and rendering printable name
- // should be merged in helper function.
quoteLocal := false
- for i := 0; i < len(local); i++ {
- ch := local[i]
- if isAtext(ch, false) {
+ for i, r := range local {
+ if isAtext(r, false) {
continue
}
- if ch == '.' {
+ if r == '.' {
// Dots are okay if they are surrounded by atext.
// We only need to check that the previous byte is
// not a dot, and this isn't the end of the string.
@@ -212,25 +212,16 @@ func (a *Address) String() string {
// If every character is printable ASCII, quoting is simple.
allPrintable := true
- for i := 0; i < len(a.Name); i++ {
+ for _, r := range a.Name {
// isWSP here should actually be isFWS,
// but we don't support folding yet.
- if !isVchar(a.Name[i]) && !isWSP(a.Name[i]) {
+ if !isVchar(r) && !isWSP(r) || isMultibyte(r) {
allPrintable = false
break
}
}
if allPrintable {
- b := bytes.NewBufferString(`"`)
- for i := 0; i < len(a.Name); i++ {
- if !isQtext(a.Name[i]) && !isWSP(a.Name[i]) {
- b.WriteByte('\\')
- }
- b.WriteByte(a.Name[i])
- }
- b.WriteString(`" `)
- b.WriteString(s)
- return b.String()
+ return quoteString(a.Name) + " " + s
}
// Text in an encoded-word in a display-name must not contain certain
@@ -427,29 +418,48 @@ func (p *addrParser) consumePhrase() (phrase string, err error) {
func (p *addrParser) consumeQuotedString() (qs string, err error) {
// Assume first byte is '"'.
i := 1
- qsb := make([]byte, 0, 10)
+ qsb := make([]rune, 0, 10)
+
+ escaped := false
+
Loop:
for {
- if i >= p.len() {
+ r, size := utf8.DecodeRuneInString(p.s[i:])
+
+ switch {
+ case size == 0:
return "", errors.New("mail: unclosed quoted-string")
- }
- switch c := p.s[i]; {
- case c == '"':
- break Loop
- case c == '\\':
- if i+1 == p.len() {
- return "", errors.New("mail: unclosed quoted-string")
+
+ case size == 1 && r == utf8.RuneError:
+ return "", fmt.Errorf("mail: invalid utf-8 in quoted-string: %q", p.s)
+
+ case escaped:
+ // quoted-pair = ("\" (VCHAR / WSP))
+
+ if !isVchar(r) && !isWSP(r) {
+ return "", fmt.Errorf("mail: bad character in quoted-string: %q", r)
}
- qsb = append(qsb, p.s[i+1])
- i += 2
- case isQtext(c), c == ' ':
+
+ qsb = append(qsb, r)
+ escaped = false
+
+ case isQtext(r) || isWSP(r):
// qtext (printable US-ASCII excluding " and \), or
// FWS (almost; we're ignoring CRLF)
- qsb = append(qsb, c)
- i++
+ qsb = append(qsb, r)
+
+ case r == '"':
+ break Loop
+
+ case r == '\\':
+ escaped = true
+
default:
- return "", fmt.Errorf("mail: bad character in quoted-string: %q", c)
+ return "", fmt.Errorf("mail: bad character in quoted-string: %q", r)
+
}
+
+ i += size
}
p.s = p.s[i+1:]
if len(qsb) == 0 {
@@ -458,26 +468,34 @@ Loop:
return string(qsb), nil
}
-var errNonASCII = errors.New("mail: unencoded non-ASCII text in address")
-
// consumeAtom parses an RFC 5322 atom at the start of p.
// If dot is true, consumeAtom parses an RFC 5322 dot-atom instead.
// If permissive is true, consumeAtom will not fail on
// leading/trailing/double dots in the atom (see golang.org/issue/4938).
func (p *addrParser) consumeAtom(dot bool, permissive bool) (atom string, err error) {
- if c := p.peek(); !isAtext(c, false) {
- if c > 127 {
- return "", errNonASCII
+ i := 0
+
+Loop:
+ for {
+ r, size := utf8.DecodeRuneInString(p.s[i:])
+
+ switch {
+ case size == 1 && r == utf8.RuneError:
+ return "", fmt.Errorf("mail: invalid utf-8 in address: %q", p.s)
+
+ case size == 0 || !isAtext(r, dot):
+ break Loop
+
+ default:
+ i += size
+
}
- return "", errors.New("mail: invalid string")
}
- i := 1
- for ; i < p.len() && isAtext(p.s[i], dot); i++ {
- }
- if i < p.len() && p.s[i] > 127 {
- return "", errNonASCII
+
+ if i == 0 {
+ return "", errors.New("mail: invalid string")
}
- atom, p.s = string(p.s[:i]), p.s[i:]
+ atom, p.s = p.s[:i], p.s[i:]
if !permissive {
if strings.HasPrefix(atom, ".") {
return "", errors.New("mail: leading dot in atom")
@@ -547,54 +565,58 @@ func (e charsetError) Error() string {
return fmt.Sprintf("charset not supported: %q", string(e))
}
-var atextChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
- "abcdefghijklmnopqrstuvwxyz" +
- "0123456789" +
- "!#$%&'*+-/=?^_`{|}~")
-
-// isAtext reports whether c is an RFC 5322 atext character.
+// isAtext reports whether r is an RFC 5322 atext character.
// If dot is true, period is included.
-func isAtext(c byte, dot bool) bool {
- if dot && c == '.' {
- return true
+func isAtext(r rune, dot bool) bool {
+ switch r {
+ case '.':
+ return dot
+
+ case '(', ')', '<', '>', '[', ']', ':', ';', '@', '\\', ',', '"': // RFC 5322 3.2.3. specials
+ return false
}
- return bytes.IndexByte(atextChars, c) >= 0
+ return isVchar(r)
}
-// isQtext reports whether c is an RFC 5322 qtext character.
-func isQtext(c byte) bool {
+// isQtext reports whether r is an RFC 5322 qtext character.
+func isQtext(r rune) bool {
// Printable US-ASCII, excluding backslash or quote.
- if c == '\\' || c == '"' {
+ if r == '\\' || r == '"' {
return false
}
- return '!' <= c && c <= '~'
+ return isVchar(r)
}
-// quoteString renders a string as a RFC5322 quoted-string.
+// quoteString renders a string as an RFC 5322 quoted-string.
func quoteString(s string) string {
var buf bytes.Buffer
buf.WriteByte('"')
- for _, c := range s {
- ch := byte(c)
- if isQtext(ch) || isWSP(ch) {
- buf.WriteByte(ch)
- } else if isVchar(ch) {
+ for _, r := range s {
+ if isQtext(r) || isWSP(r) {
+ buf.WriteRune(r)
+ } else if isVchar(r) {
buf.WriteByte('\\')
- buf.WriteByte(ch)
+ buf.WriteRune(r)
}
}
buf.WriteByte('"')
return buf.String()
}
-// isVchar reports whether c is an RFC 5322 VCHAR character.
-func isVchar(c byte) bool {
+// isVchar reports whether r is an RFC 5322 VCHAR character.
+func isVchar(r rune) bool {
// Visible (printing) characters.
- return '!' <= c && c <= '~'
+ return '!' <= r && r <= '~' || isMultibyte(r)
+}
+
+// isMultibyte reports whether r is a multi-byte UTF-8 character
+// as supported by RFC 6532
+func isMultibyte(r rune) bool {
+ return r >= utf8.RuneSelf
}
-// isWSP reports whether c is a WSP (white space).
-// WSP is a space or horizontal tab (RFC5234 Appendix B).
-func isWSP(c byte) bool {
- return c == ' ' || c == '\t'
+// isWSP reports whether r is a WSP (white space).
+// WSP is a space or horizontal tab (RFC 5234 Appendix B).
+func isWSP(r rune) bool {
+ return r == ' ' || r == '\t'
}
diff --git a/src/net/mail/message_test.go b/src/net/mail/message_test.go
index cf86ace68f..bbbba6b584 100644
--- a/src/net/mail/message_test.go
+++ b/src/net/mail/message_test.go
@@ -92,7 +92,7 @@ func TestDateParsing(t *testing.T) {
"Fri, 21 Nov 1997 09:55:06 -0600",
time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
},
- // RFC5322, Appendix A.6.2
+ // RFC 5322, Appendix A.6.2
// Obsolete date.
{
"21 Nov 97 09:55:06 GMT",
@@ -125,8 +125,12 @@ func TestAddressParsingError(t *testing.T) {
wantErrText string
}{
0: {"=?iso-8859-2?Q?Bogl=E1rka_Tak=E1cs?= <unknown@gmail.com>", "charset not supported"},
- 1: {"µ <micro@example.net>", "unencoded non-ASCII text in address"},
- 2: {"a@gmail.com b@gmail.com", "expected single address"},
+ 1: {"a@gmail.com b@gmail.com", "expected single address"},
+ 2: {string([]byte{0xed, 0xa0, 0x80}) + " <micro@example.net>", "invalid utf-8 in address"},
+ 3: {"\"" + string([]byte{0xed, 0xa0, 0x80}) + "\" <half-surrogate@example.com>", "invalid utf-8 in quoted-string"},
+ 4: {"\"\\" + string([]byte{0x80}) + "\" <escaped-invalid-unicode@example.net>", "invalid utf-8 in quoted-string"},
+ 5: {"\"\x00\" <null@example.net>", "bad character in quoted-string"},
+ 6: {"\"\\\x00\" <escaped-null@example.net>", "bad character in quoted-string"},
}
for i, tc := range mustErrTestCases {
@@ -266,6 +270,46 @@ func TestAddressParsing(t *testing.T) {
},
},
},
+ // RFC 6532 3.2.3, qtext /= UTF8-non-ascii
+ {
+ `"Gø Pher" <gopher@example.com>`,
+ []*Address{
+ {
+ Name: `Gø Pher`,
+ Address: "gopher@example.com",
+ },
+ },
+ },
+ // RFC 6532 3.2, atext /= UTF8-non-ascii
+ {
+ `µ <micro@example.com>`,
+ []*Address{
+ {
+ Name: `µ`,
+ Address: "micro@example.com",
+ },
+ },
+ },
+ // RFC 6532 3.2.2, local address parts allow UTF-8
+ {
+ `Micro <µ@example.com>`,
+ []*Address{
+ {
+ Name: `Micro`,
+ Address: "µ@example.com",
+ },
+ },
+ },
+ // RFC 6532 3.2.4, domains parts allow UTF-8
+ {
+ `Micro <micro@µ.example.com>`,
+ []*Address{
+ {
+ Name: `Micro`,
+ Address: "micro@µ.example.com",
+ },
+ },
+ },
}
for _, test := range tests {
if len(test.exp) == 1 {
@@ -517,6 +561,11 @@ func TestAddressString(t *testing.T) {
&Address{Name: "world?=", Address: "hello@world.com"},
`"world?=" <hello@world.com>`,
},
+ {
+ // should q-encode even for invalid utf-8.
+ &Address{Name: string([]byte{0xed, 0xa0, 0x80}), Address: "invalid-utf8@example.net"},
+ "=?utf-8?q?=ED=A0=80?= <invalid-utf8@example.net>",
+ },
}
for _, test := range tests {
s := test.addr.String()
@@ -612,7 +661,6 @@ func TestAddressParsingAndFormatting(t *testing.T) {
`< @example.com>`,
`<""test""blah""@example.com>`,
`<""@0>`,
- "<\"\t0\"@0>",
}
for _, test := range badTests {
diff --git a/src/net/main_test.go b/src/net/main_test.go
index f3f8b1a900..7573ded93b 100644
--- a/src/net/main_test.go
+++ b/src/net/main_test.go
@@ -26,8 +26,6 @@ var (
var (
testDNSFlood = flag.Bool("dnsflood", false, "whether to test DNS query flooding")
- testExternal = flag.Bool("external", true, "allow use of external networks during long test")
-
// If external IPv4 connectivity exists, we can try dialing
// non-node/interface local scope IPv4 addresses.
// On Windows, Lookup APIs may not return IPv4-related
diff --git a/src/net/mockserver_test.go b/src/net/mockserver_test.go
index ffc6836e73..9e6907c09a 100644
--- a/src/net/mockserver_test.go
+++ b/src/net/mockserver_test.go
@@ -336,13 +336,21 @@ func timeoutTransmitter(c Conn, d, min, max time.Duration, ch chan<- error) {
func newLocalPacketListener(network string) (PacketConn, error) {
switch network {
- case "udp", "udp4", "udp6":
+ case "udp":
if supportsIPv4 {
return ListenPacket("udp4", "127.0.0.1:0")
}
if supportsIPv6 {
return ListenPacket("udp6", "[::1]:0")
}
+ case "udp4":
+ if supportsIPv4 {
+ return ListenPacket("udp4", "127.0.0.1:0")
+ }
+ case "udp6":
+ if supportsIPv6 {
+ return ListenPacket("udp6", "[::1]:0")
+ }
case "unixgram":
return ListenPacket(network, testUnixAddr())
}
diff --git a/src/net/net.go b/src/net/net.go
index 3b37b336d1..27e9ca367d 100644
--- a/src/net/net.go
+++ b/src/net/net.go
@@ -79,6 +79,7 @@ On Windows, the resolver always uses C library functions, such as GetAddrInfo an
package net
import (
+ "context"
"errors"
"io"
"os"
@@ -377,6 +378,22 @@ var (
ErrWriteToConnected = errors.New("use of WriteTo with pre-connected connection")
)
+// mapErr maps from the context errors to the historical internal net
+// error values.
+//
+// TODO(bradfitz): get rid of this after adjusting tests and making
+// context.DeadlineExceeded implement net.Error?
+func mapErr(err error) error {
+ switch err {
+ case context.Canceled:
+ return errCanceled
+ case context.DeadlineExceeded:
+ return errTimeout
+ default:
+ return err
+ }
+}
+
// OpError is the error type usually returned by functions in the net
// package. It describes the operation, network type, and address of
// an error.
diff --git a/src/net/netgo_unix_test.go b/src/net/netgo_unix_test.go
index 1d950d67d7..0a118874c2 100644
--- a/src/net/netgo_unix_test.go
+++ b/src/net/netgo_unix_test.go
@@ -7,7 +7,10 @@
package net
-import "testing"
+import (
+ "context"
+ "testing"
+)
func TestGoLookupIP(t *testing.T) {
host := "localhost"
@@ -18,7 +21,7 @@ func TestGoLookupIP(t *testing.T) {
if err != nil {
t.Error(err)
}
- if _, err := goLookupIP(host); err != nil {
+ if _, err := goLookupIP(context.Background(), host); err != nil {
t.Error(err)
}
}
diff --git a/src/net/parse.go b/src/net/parse.go
index eaaa1edf30..ed82a7769b 100644
--- a/src/net/parse.go
+++ b/src/net/parse.go
@@ -105,14 +105,14 @@ func splitAtBytes(s string, t string) []string {
for i := 0; i < len(s); i++ {
if byteIndex(t, s[i]) >= 0 {
if last < i {
- a[n] = string(s[last:i])
+ a[n] = s[last:i]
n++
}
last = i + 1
}
}
if last < len(s) {
- a[n] = string(s[last:])
+ a[n] = s[last:]
n++
}
return a[0:n]
diff --git a/src/net/platform_test.go b/src/net/platform_test.go
index 76c53138cd..2a14095cc2 100644
--- a/src/net/platform_test.go
+++ b/src/net/platform_test.go
@@ -5,6 +5,7 @@
package net
import (
+ "internal/testenv"
"os"
"runtime"
"strings"
@@ -110,7 +111,7 @@ func testableListenArgs(network, address, client string) bool {
}
// Test wildcard IP addresses.
- if wildcard && (testing.Short() || !*testExternal) {
+ if wildcard && !testenv.HasExternalNetwork() {
return false
}
diff --git a/src/net/port.go b/src/net/port.go
new file mode 100644
index 0000000000..8e1321afa4
--- /dev/null
+++ b/src/net/port.go
@@ -0,0 +1,62 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+// parsePort parses service as a decimal interger and returns the
+// corresponding value as port. It is the caller's responsibility to
+// parse service as a non-decimal integer when needsLookup is true.
+//
+// Some system resolvers will return a valid port number when given a number
+// over 65536 (see https://github.com/golang/go/issues/11715). Alas, the parser
+// can't bail early on numbers > 65536. Therefore reasonably large/small
+// numbers are parsed in full and rejected if invalid.
+func parsePort(service string) (port int, needsLookup bool) {
+ if service == "" {
+ // Lock in the legacy behavior that an empty string
+ // means port 0. See golang.org/issue/13610.
+ return 0, false
+ }
+ const (
+ max = uint32(1<<32 - 1)
+ cutoff = uint32(1 << 30)
+ )
+ neg := false
+ if service[0] == '+' {
+ service = service[1:]
+ } else if service[0] == '-' {
+ neg = true
+ service = service[1:]
+ }
+ var n uint32
+ for _, d := range service {
+ if '0' <= d && d <= '9' {
+ d -= '0'
+ } else {
+ return 0, true
+ }
+ if n >= cutoff {
+ n = max
+ break
+ }
+ n *= 10
+ nn := n + uint32(d)
+ if nn < n || nn > max {
+ n = max
+ break
+ }
+ n = nn
+ }
+ if !neg && n >= cutoff {
+ port = int(cutoff - 1)
+ } else if neg && n > cutoff {
+ port = int(cutoff)
+ } else {
+ port = int(n)
+ }
+ if neg {
+ port = -port
+ }
+ return port, false
+}
diff --git a/src/net/port_test.go b/src/net/port_test.go
new file mode 100644
index 0000000000..e0bdb4247d
--- /dev/null
+++ b/src/net/port_test.go
@@ -0,0 +1,52 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+import "testing"
+
+var parsePortTests = []struct {
+ service string
+ port int
+ needsLookup bool
+}{
+ {"", 0, false},
+
+ // Decimal number literals
+ {"-1073741825", -1 << 30, false},
+ {"-1073741824", -1 << 30, false},
+ {"-1073741823", -(1<<30 - 1), false},
+ {"-123456789", -123456789, false},
+ {"-1", -1, false},
+ {"-0", 0, false},
+ {"0", 0, false},
+ {"+0", 0, false},
+ {"+1", 1, false},
+ {"65535", 65535, false},
+ {"65536", 65536, false},
+ {"123456789", 123456789, false},
+ {"1073741822", 1<<30 - 2, false},
+ {"1073741823", 1<<30 - 1, false},
+ {"1073741824", 1<<30 - 1, false},
+ {"1073741825", 1<<30 - 1, false},
+
+ // Others
+ {"abc", 0, true},
+ {"9pfs", 0, true},
+ {"123badport", 0, true},
+ {"bad123port", 0, true},
+ {"badport123", 0, true},
+ {"123456789badport", 0, true},
+ {"-2147483649badport", 0, true},
+ {"2147483649badport", 0, true},
+}
+
+func TestParsePort(t *testing.T) {
+ // The following test cases are cribbed from the strconv
+ for _, tt := range parsePortTests {
+ if port, needsLookup := parsePort(tt.service); port != tt.port || needsLookup != tt.needsLookup {
+ t.Errorf("parsePort(%q) = %d, %t; want %d, %t", tt.service, port, needsLookup, tt.port, tt.needsLookup)
+ }
+ }
+}
diff --git a/src/net/sendfile_dragonfly.go b/src/net/sendfile_dragonfly.go
index 17021c3801..d4b825c370 100644
--- a/src/net/sendfile_dragonfly.go
+++ b/src/net/sendfile_dragonfly.go
@@ -53,7 +53,7 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
// use the current position of the file -- if you pass it offset 0, it starts
// from offset 0. There's no way to tell it "start from current position", so
// we have to manage that explicitly.
- pos, err := f.Seek(0, os.SEEK_CUR)
+ pos, err := f.Seek(0, io.SeekCurrent)
if err != nil {
return 0, err, false
}
diff --git a/src/net/sendfile_freebsd.go b/src/net/sendfile_freebsd.go
index f7a8529560..18cbb27b53 100644
--- a/src/net/sendfile_freebsd.go
+++ b/src/net/sendfile_freebsd.go
@@ -53,7 +53,7 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
// use the current position of the file -- if you pass it offset 0, it starts
// from offset 0. There's no way to tell it "start from current position", so
// we have to manage that explicitly.
- pos, err := f.Seek(0, os.SEEK_CUR)
+ pos, err := f.Seek(0, io.SeekCurrent)
if err != nil {
return 0, err, false
}
diff --git a/src/net/sendfile_solaris.go b/src/net/sendfile_solaris.go
index eb9d2d1830..add70c3147 100644
--- a/src/net/sendfile_solaris.go
+++ b/src/net/sendfile_solaris.go
@@ -26,8 +26,6 @@ const maxSendfileSize int = 4 << 20
//
// if handled == false, sendFile performed no work.
func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
- return // Solaris sendfile is disabled until Issue 13892 is understood and fixed
-
// Solaris uses 0 as the "until EOF" value. If you pass in more bytes than the
// file contains, it will loop back to the beginning ad nauseam until it's sent
// exactly the number of bytes told to. As such, we need to know exactly how many
@@ -59,7 +57,7 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
// use the current position of the file -- if you pass it offset 0, it starts
// from offset 0. There's no way to tell it "start from current position", so
// we have to manage that explicitly.
- pos, err := f.Seek(0, os.SEEK_CUR)
+ pos, err := f.Seek(0, io.SeekCurrent)
if err != nil {
return 0, err, false
}
@@ -78,6 +76,13 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
}
pos1 := pos
n, err1 := syscall.Sendfile(dst, src, &pos1, n)
+ if err1 == syscall.EAGAIN || err1 == syscall.EINTR {
+ // partial write may have occurred
+ if n = int(pos1 - pos); n == 0 {
+ // nothing more to write
+ err1 = nil
+ }
+ }
if n > 0 {
pos += int64(n)
written += int64(n)
diff --git a/src/net/sendfile_test.go b/src/net/sendfile_test.go
new file mode 100644
index 0000000000..2255e7c478
--- /dev/null
+++ b/src/net/sendfile_test.go
@@ -0,0 +1,90 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+import (
+ "crypto/sha256"
+ "encoding/hex"
+ "fmt"
+ "io"
+ "os"
+ "testing"
+)
+
+const (
+ twain = "testdata/Mark.Twain-Tom.Sawyer.txt"
+ twainLen = 387851
+ twainSHA256 = "461eb7cb2d57d293fc680c836464c9125e4382be3596f7d415093ae9db8fcb0e"
+)
+
+func TestSendfile(t *testing.T) {
+ ln, err := newLocalListener("tcp")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ln.Close()
+
+ errc := make(chan error, 1)
+ go func(ln Listener) {
+ // Wait for a connection.
+ conn, err := ln.Accept()
+ if err != nil {
+ errc <- err
+ close(errc)
+ return
+ }
+
+ go func() {
+ defer close(errc)
+ defer conn.Close()
+
+ f, err := os.Open(twain)
+ if err != nil {
+ errc <- err
+ return
+ }
+ defer f.Close()
+
+ // Return file data using io.Copy, which should use
+ // sendFile if available.
+ sbytes, err := io.Copy(conn, f)
+ if err != nil {
+ errc <- err
+ return
+ }
+
+ if sbytes != twainLen {
+ errc <- fmt.Errorf("sent %d bytes; expected %d", sbytes, twainLen)
+ return
+ }
+ }()
+ }(ln)
+
+ // Connect to listener to retrieve file and verify digest matches
+ // expected.
+ c, err := Dial("tcp", ln.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ h := sha256.New()
+ rbytes, err := io.Copy(h, c)
+ if err != nil {
+ t.Error(err)
+ }
+
+ if rbytes != twainLen {
+ t.Errorf("received %d bytes; expected %d", rbytes, twainLen)
+ }
+
+ if res := hex.EncodeToString(h.Sum(nil)); res != twainSHA256 {
+ t.Error("retrieved data hash did not match")
+ }
+
+ for err := range errc {
+ t.Error(err)
+ }
+}
diff --git a/src/net/sock_posix.go b/src/net/sock_posix.go
index 3dddfef4c5..c3af27b596 100644
--- a/src/net/sock_posix.go
+++ b/src/net/sock_posix.go
@@ -7,9 +7,9 @@
package net
import (
+ "context"
"os"
"syscall"
- "time"
)
// A sockaddr represents a TCP, UDP, IP or Unix network endpoint
@@ -34,7 +34,7 @@ type sockaddr interface {
// socket returns a network file descriptor that is ready for
// asynchronous I/O using the network poller.
-func socket(net string, family, sotype, proto int, ipv6only bool, laddr, raddr sockaddr, deadline time.Time, cancel <-chan struct{}) (fd *netFD, err error) {
+func socket(ctx context.Context, net string, family, sotype, proto int, ipv6only bool, laddr, raddr sockaddr) (fd *netFD, err error) {
s, err := sysSocket(family, sotype, proto)
if err != nil {
return nil, err
@@ -86,7 +86,7 @@ func socket(net string, family, sotype, proto int, ipv6only bool, laddr, raddr s
return fd, nil
}
}
- if err := fd.dial(laddr, raddr, deadline, cancel); err != nil {
+ if err := fd.dial(ctx, laddr, raddr); err != nil {
fd.Close()
return nil, err
}
@@ -117,7 +117,7 @@ func (fd *netFD) addrFunc() func(syscall.Sockaddr) Addr {
return func(syscall.Sockaddr) Addr { return nil }
}
-func (fd *netFD) dial(laddr, raddr sockaddr, deadline time.Time, cancel <-chan struct{}) error {
+func (fd *netFD) dial(ctx context.Context, laddr, raddr sockaddr) error {
var err error
var lsa syscall.Sockaddr
if laddr != nil {
@@ -134,7 +134,7 @@ func (fd *netFD) dial(laddr, raddr sockaddr, deadline time.Time, cancel <-chan s
if rsa, err = raddr.sockaddr(fd.family); err != nil {
return err
}
- if err := fd.connect(lsa, rsa, deadline, cancel); err != nil {
+ if err := fd.connect(ctx, lsa, rsa); err != nil {
return err
}
fd.isConnected = true
diff --git a/src/net/tcpsock.go b/src/net/tcpsock.go
index a5c3515c19..7cffcc58cb 100644
--- a/src/net/tcpsock.go
+++ b/src/net/tcpsock.go
@@ -5,6 +5,7 @@
package net
import (
+ "context"
"io"
"os"
"syscall"
@@ -60,7 +61,7 @@ func ResolveTCPAddr(net, addr string) (*TCPAddr, error) {
default:
return nil, UnknownNetworkError(net)
}
- addrs, err := internetAddrList(net, addr, noDeadline)
+ addrs, err := internetAddrList(context.Background(), net, addr)
if err != nil {
return nil, err
}
@@ -186,7 +187,7 @@ func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
if raddr == nil {
return nil, &OpError{Op: "dial", Net: net, Source: laddr.opAddr(), Addr: nil, Err: errMissingAddress}
}
- c, err := dialTCP(net, laddr, raddr, noDeadline, noCancel)
+ c, err := dialTCP(context.Background(), net, laddr, raddr)
if err != nil {
return nil, &OpError{Op: "dial", Net: net, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: err}
}
@@ -285,7 +286,7 @@ func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) {
if laddr == nil {
laddr = &TCPAddr{}
}
- ln, err := listenTCP(net, laddr)
+ ln, err := listenTCP(context.Background(), net, laddr)
if err != nil {
return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: laddr.opAddr(), Err: err}
}
diff --git a/src/net/tcpsock_plan9.go b/src/net/tcpsock_plan9.go
index 698b834295..d2860607f8 100644
--- a/src/net/tcpsock_plan9.go
+++ b/src/net/tcpsock_plan9.go
@@ -5,20 +5,23 @@
package net
import (
+ "context"
"io"
"os"
- "time"
)
func (c *TCPConn) readFrom(r io.Reader) (int64, error) {
return genericReadFrom(c, r)
}
-func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time, cancel <-chan struct{}) (*TCPConn, error) {
- if !deadline.IsZero() {
- panic("net.dialTCP: deadline not implemented on Plan 9")
+func dialTCP(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
+ if testHookDialTCP != nil {
+ return testHookDialTCP(ctx, net, laddr, raddr)
}
- // TODO(bradfitz,0intro): also use the cancel channel.
+ return doDialTCP(ctx, net, laddr, raddr)
+}
+
+func doDialTCP(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
switch net {
case "tcp", "tcp4", "tcp6":
default:
@@ -27,7 +30,7 @@ func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time, cancel <-cha
if raddr == nil {
return nil, errMissingAddress
}
- fd, err := dialPlan9(net, laddr, raddr)
+ fd, err := dialPlan9(ctx, net, laddr, raddr)
if err != nil {
return nil, err
}
@@ -63,8 +66,8 @@ func (ln *TCPListener) file() (*os.File, error) {
return f, nil
}
-func listenTCP(network string, laddr *TCPAddr) (*TCPListener, error) {
- fd, err := listenPlan9(network, laddr)
+func listenTCP(ctx context.Context, network string, laddr *TCPAddr) (*TCPListener, error) {
+ fd, err := listenPlan9(ctx, network, laddr)
if err != nil {
return nil, err
}
diff --git a/src/net/tcpsock_posix.go b/src/net/tcpsock_posix.go
index 3902565f73..c9a8b6808e 100644
--- a/src/net/tcpsock_posix.go
+++ b/src/net/tcpsock_posix.go
@@ -7,10 +7,10 @@
package net
import (
+ "context"
"io"
"os"
"syscall"
- "time"
)
func sockaddrToTCP(sa syscall.Sockaddr) Addr {
@@ -47,8 +47,15 @@ func (c *TCPConn) readFrom(r io.Reader) (int64, error) {
return genericReadFrom(c, r)
}
-func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time, cancel <-chan struct{}) (*TCPConn, error) {
- fd, err := internetSocket(net, laddr, raddr, deadline, syscall.SOCK_STREAM, 0, "dial", cancel)
+func dialTCP(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
+ if testHookDialTCP != nil {
+ return testHookDialTCP(ctx, net, laddr, raddr)
+ }
+ return doDialTCP(ctx, net, laddr, raddr)
+}
+
+func doDialTCP(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
+ fd, err := internetSocket(ctx, net, laddr, raddr, syscall.SOCK_STREAM, 0, "dial")
// TCP has a rarely used mechanism called a 'simultaneous connection' in
// which Dial("tcp", addr1, addr2) run on the machine at addr1 can
@@ -78,7 +85,7 @@ func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time, cancel <-cha
if err == nil {
fd.Close()
}
- fd, err = internetSocket(net, laddr, raddr, deadline, syscall.SOCK_STREAM, 0, "dial", cancel)
+ fd, err = internetSocket(ctx, net, laddr, raddr, syscall.SOCK_STREAM, 0, "dial")
}
if err != nil {
@@ -141,8 +148,8 @@ func (ln *TCPListener) file() (*os.File, error) {
return f, nil
}
-func listenTCP(network string, laddr *TCPAddr) (*TCPListener, error) {
- fd, err := internetSocket(network, laddr, nil, noDeadline, syscall.SOCK_STREAM, 0, "listen", noCancel)
+func listenTCP(ctx context.Context, network string, laddr *TCPAddr) (*TCPListener, error) {
+ fd, err := internetSocket(ctx, network, laddr, nil, syscall.SOCK_STREAM, 0, "listen")
if err != nil {
return nil, err
}
diff --git a/src/net/tcpsock_test.go b/src/net/tcpsock_test.go
index 8de6ad71ce..4af47fcf1a 100644
--- a/src/net/tcpsock_test.go
+++ b/src/net/tcpsock_test.go
@@ -5,6 +5,7 @@
package net
import (
+ "internal/testenv"
"io"
"reflect"
"runtime"
@@ -345,9 +346,7 @@ var tcpListenerNameTests = []struct {
}
func TestTCPListenerName(t *testing.T) {
- if testing.Short() || !*testExternal {
- t.Skip("avoid external network")
- }
+ testenv.MustHaveExternalNetwork(t)
for _, tt := range tcpListenerNameTests {
ln, err := ListenTCP(tt.net, tt.laddr)
@@ -363,9 +362,8 @@ func TestTCPListenerName(t *testing.T) {
}
func TestIPv6LinkLocalUnicastTCP(t *testing.T) {
- if testing.Short() || !*testExternal {
- t.Skip("avoid external network")
- }
+ testenv.MustHaveExternalNetwork(t)
+
if !supportsIPv6 {
t.Skip("IPv6 is not supported")
}
diff --git a/src/net/testdata/Mark.Twain-Tom.Sawyer.txt b/src/net/testdata/Mark.Twain-Tom.Sawyer.txt
new file mode 100644
index 0000000000..c9106fd522
--- /dev/null
+++ b/src/net/testdata/Mark.Twain-Tom.Sawyer.txt
@@ -0,0 +1,8465 @@
+Produced by David Widger. The previous edition was updated by Jose
+Menendez.
+
+
+
+
+
+ THE ADVENTURES OF TOM SAWYER
+ BY
+ MARK TWAIN
+ (Samuel Langhorne Clemens)
+
+
+
+
+ P R E F A C E
+
+MOST of the adventures recorded in this book really occurred; one or
+two were experiences of my own, the rest those of boys who were
+schoolmates of mine. Huck Finn is drawn from life; Tom Sawyer also, but
+not from an individual--he is a combination of the characteristics of
+three boys whom I knew, and therefore belongs to the composite order of
+architecture.
+
+The odd superstitions touched upon were all prevalent among children
+and slaves in the West at the period of this story--that is to say,
+thirty or forty years ago.
+
+Although my book is intended mainly for the entertainment of boys and
+girls, I hope it will not be shunned by men and women on that account,
+for part of my plan has been to try to pleasantly remind adults of what
+they once were themselves, and of how they felt and thought and talked,
+and what queer enterprises they sometimes engaged in.
+
+ THE AUTHOR.
+
+HARTFORD, 1876.
+
+
+
+ T O M S A W Y E R
+
+
+
+CHAPTER I
+
+"TOM!"
+
+No answer.
+
+"TOM!"
+
+No answer.
+
+"What's gone with that boy, I wonder? You TOM!"
+
+No answer.
+
+The old lady pulled her spectacles down and looked over them about the
+room; then she put them up and looked out under them. She seldom or
+never looked THROUGH them for so small a thing as a boy; they were her
+state pair, the pride of her heart, and were built for "style," not
+service--she could have seen through a pair of stove-lids just as well.
+She looked perplexed for a moment, and then said, not fiercely, but
+still loud enough for the furniture to hear:
+
+"Well, I lay if I get hold of you I'll--"
+
+She did not finish, for by this time she was bending down and punching
+under the bed with the broom, and so she needed breath to punctuate the
+punches with. She resurrected nothing but the cat.
+
+"I never did see the beat of that boy!"
+
+She went to the open door and stood in it and looked out among the
+tomato vines and "jimpson" weeds that constituted the garden. No Tom.
+So she lifted up her voice at an angle calculated for distance and
+shouted:
+
+"Y-o-u-u TOM!"
+
+There was a slight noise behind her and she turned just in time to
+seize a small boy by the slack of his roundabout and arrest his flight.
+
+"There! I might 'a' thought of that closet. What you been doing in
+there?"
+
+"Nothing."
+
+"Nothing! Look at your hands. And look at your mouth. What IS that
+truck?"
+
+"I don't know, aunt."
+
+"Well, I know. It's jam--that's what it is. Forty times I've said if
+you didn't let that jam alone I'd skin you. Hand me that switch."
+
+The switch hovered in the air--the peril was desperate--
+
+"My! Look behind you, aunt!"
+
+The old lady whirled round, and snatched her skirts out of danger. The
+lad fled on the instant, scrambled up the high board-fence, and
+disappeared over it.
+
+His aunt Polly stood surprised a moment, and then broke into a gentle
+laugh.
+
+"Hang the boy, can't I never learn anything? Ain't he played me tricks
+enough like that for me to be looking out for him by this time? But old
+fools is the biggest fools there is. Can't learn an old dog new tricks,
+as the saying is. But my goodness, he never plays them alike, two days,
+and how is a body to know what's coming? He 'pears to know just how
+long he can torment me before I get my dander up, and he knows if he
+can make out to put me off for a minute or make me laugh, it's all down
+again and I can't hit him a lick. I ain't doing my duty by that boy,
+and that's the Lord's truth, goodness knows. Spare the rod and spile
+the child, as the Good Book says. I'm a laying up sin and suffering for
+us both, I know. He's full of the Old Scratch, but laws-a-me! he's my
+own dead sister's boy, poor thing, and I ain't got the heart to lash
+him, somehow. Every time I let him off, my conscience does hurt me so,
+and every time I hit him my old heart most breaks. Well-a-well, man
+that is born of woman is of few days and full of trouble, as the
+Scripture says, and I reckon it's so. He'll play hookey this evening, *
+and [* Southwestern for "afternoon"] I'll just be obleeged to make him
+work, to-morrow, to punish him. It's mighty hard to make him work
+Saturdays, when all the boys is having holiday, but he hates work more
+than he hates anything else, and I've GOT to do some of my duty by him,
+or I'll be the ruination of the child."
+
+Tom did play hookey, and he had a very good time. He got back home
+barely in season to help Jim, the small colored boy, saw next-day's
+wood and split the kindlings before supper--at least he was there in
+time to tell his adventures to Jim while Jim did three-fourths of the
+work. Tom's younger brother (or rather half-brother) Sid was already
+through with his part of the work (picking up chips), for he was a
+quiet boy, and had no adventurous, troublesome ways.
+
+While Tom was eating his supper, and stealing sugar as opportunity
+offered, Aunt Polly asked him questions that were full of guile, and
+very deep--for she wanted to trap him into damaging revealments. Like
+many other simple-hearted souls, it was her pet vanity to believe she
+was endowed with a talent for dark and mysterious diplomacy, and she
+loved to contemplate her most transparent devices as marvels of low
+cunning. Said she:
+
+"Tom, it was middling warm in school, warn't it?"
+
+"Yes'm."
+
+"Powerful warm, warn't it?"
+
+"Yes'm."
+
+"Didn't you want to go in a-swimming, Tom?"
+
+A bit of a scare shot through Tom--a touch of uncomfortable suspicion.
+He searched Aunt Polly's face, but it told him nothing. So he said:
+
+"No'm--well, not very much."
+
+The old lady reached out her hand and felt Tom's shirt, and said:
+
+"But you ain't too warm now, though." And it flattered her to reflect
+that she had discovered that the shirt was dry without anybody knowing
+that that was what she had in her mind. But in spite of her, Tom knew
+where the wind lay, now. So he forestalled what might be the next move:
+
+"Some of us pumped on our heads--mine's damp yet. See?"
+
+Aunt Polly was vexed to think she had overlooked that bit of
+circumstantial evidence, and missed a trick. Then she had a new
+inspiration:
+
+"Tom, you didn't have to undo your shirt collar where I sewed it, to
+pump on your head, did you? Unbutton your jacket!"
+
+The trouble vanished out of Tom's face. He opened his jacket. His
+shirt collar was securely sewed.
+
+"Bother! Well, go 'long with you. I'd made sure you'd played hookey
+and been a-swimming. But I forgive ye, Tom. I reckon you're a kind of a
+singed cat, as the saying is--better'n you look. THIS time."
+
+She was half sorry her sagacity had miscarried, and half glad that Tom
+had stumbled into obedient conduct for once.
+
+But Sidney said:
+
+"Well, now, if I didn't think you sewed his collar with white thread,
+but it's black."
+
+"Why, I did sew it with white! Tom!"
+
+But Tom did not wait for the rest. As he went out at the door he said:
+
+"Siddy, I'll lick you for that."
+
+In a safe place Tom examined two large needles which were thrust into
+the lapels of his jacket, and had thread bound about them--one needle
+carried white thread and the other black. He said:
+
+"She'd never noticed if it hadn't been for Sid. Confound it! sometimes
+she sews it with white, and sometimes she sews it with black. I wish to
+geeminy she'd stick to one or t'other--I can't keep the run of 'em. But
+I bet you I'll lam Sid for that. I'll learn him!"
+
+He was not the Model Boy of the village. He knew the model boy very
+well though--and loathed him.
+
+Within two minutes, or even less, he had forgotten all his troubles.
+Not because his troubles were one whit less heavy and bitter to him
+than a man's are to a man, but because a new and powerful interest bore
+them down and drove them out of his mind for the time--just as men's
+misfortunes are forgotten in the excitement of new enterprises. This
+new interest was a valued novelty in whistling, which he had just
+acquired from a negro, and he was suffering to practise it undisturbed.
+It consisted in a peculiar bird-like turn, a sort of liquid warble,
+produced by touching the tongue to the roof of the mouth at short
+intervals in the midst of the music--the reader probably remembers how
+to do it, if he has ever been a boy. Diligence and attention soon gave
+him the knack of it, and he strode down the street with his mouth full
+of harmony and his soul full of gratitude. He felt much as an
+astronomer feels who has discovered a new planet--no doubt, as far as
+strong, deep, unalloyed pleasure is concerned, the advantage was with
+the boy, not the astronomer.
+
+The summer evenings were long. It was not dark, yet. Presently Tom
+checked his whistle. A stranger was before him--a boy a shade larger
+than himself. A new-comer of any age or either sex was an impressive
+curiosity in the poor little shabby village of St. Petersburg. This boy
+was well dressed, too--well dressed on a week-day. This was simply
+astounding. His cap was a dainty thing, his close-buttoned blue cloth
+roundabout was new and natty, and so were his pantaloons. He had shoes
+on--and it was only Friday. He even wore a necktie, a bright bit of
+ribbon. He had a citified air about him that ate into Tom's vitals. The
+more Tom stared at the splendid marvel, the higher he turned up his
+nose at his finery and the shabbier and shabbier his own outfit seemed
+to him to grow. Neither boy spoke. If one moved, the other moved--but
+only sidewise, in a circle; they kept face to face and eye to eye all
+the time. Finally Tom said:
+
+"I can lick you!"
+
+"I'd like to see you try it."
+
+"Well, I can do it."
+
+"No you can't, either."
+
+"Yes I can."
+
+"No you can't."
+
+"I can."
+
+"You can't."
+
+"Can!"
+
+"Can't!"
+
+An uncomfortable pause. Then Tom said:
+
+"What's your name?"
+
+"'Tisn't any of your business, maybe."
+
+"Well I 'low I'll MAKE it my business."
+
+"Well why don't you?"
+
+"If you say much, I will."
+
+"Much--much--MUCH. There now."
+
+"Oh, you think you're mighty smart, DON'T you? I could lick you with
+one hand tied behind me, if I wanted to."
+
+"Well why don't you DO it? You SAY you can do it."
+
+"Well I WILL, if you fool with me."
+
+"Oh yes--I've seen whole families in the same fix."
+
+"Smarty! You think you're SOME, now, DON'T you? Oh, what a hat!"
+
+"You can lump that hat if you don't like it. I dare you to knock it
+off--and anybody that'll take a dare will suck eggs."
+
+"You're a liar!"
+
+"You're another."
+
+"You're a fighting liar and dasn't take it up."
+
+"Aw--take a walk!"
+
+"Say--if you give me much more of your sass I'll take and bounce a
+rock off'n your head."
+
+"Oh, of COURSE you will."
+
+"Well I WILL."
+
+"Well why don't you DO it then? What do you keep SAYING you will for?
+Why don't you DO it? It's because you're afraid."
+
+"I AIN'T afraid."
+
+"You are."
+
+"I ain't."
+
+"You are."
+
+Another pause, and more eying and sidling around each other. Presently
+they were shoulder to shoulder. Tom said:
+
+"Get away from here!"
+
+"Go away yourself!"
+
+"I won't."
+
+"I won't either."
+
+So they stood, each with a foot placed at an angle as a brace, and
+both shoving with might and main, and glowering at each other with
+hate. But neither could get an advantage. After struggling till both
+were hot and flushed, each relaxed his strain with watchful caution,
+and Tom said:
+
+"You're a coward and a pup. I'll tell my big brother on you, and he
+can thrash you with his little finger, and I'll make him do it, too."
+
+"What do I care for your big brother? I've got a brother that's bigger
+than he is--and what's more, he can throw him over that fence, too."
+[Both brothers were imaginary.]
+
+"That's a lie."
+
+"YOUR saying so don't make it so."
+
+Tom drew a line in the dust with his big toe, and said:
+
+"I dare you to step over that, and I'll lick you till you can't stand
+up. Anybody that'll take a dare will steal sheep."
+
+The new boy stepped over promptly, and said:
+
+"Now you said you'd do it, now let's see you do it."
+
+"Don't you crowd me now; you better look out."
+
+"Well, you SAID you'd do it--why don't you do it?"
+
+"By jingo! for two cents I WILL do it."
+
+The new boy took two broad coppers out of his pocket and held them out
+with derision. Tom struck them to the ground. In an instant both boys
+were rolling and tumbling in the dirt, gripped together like cats; and
+for the space of a minute they tugged and tore at each other's hair and
+clothes, punched and scratched each other's nose, and covered
+themselves with dust and glory. Presently the confusion took form, and
+through the fog of battle Tom appeared, seated astride the new boy, and
+pounding him with his fists. "Holler 'nuff!" said he.
+
+The boy only struggled to free himself. He was crying--mainly from rage.
+
+"Holler 'nuff!"--and the pounding went on.
+
+At last the stranger got out a smothered "'Nuff!" and Tom let him up
+and said:
+
+"Now that'll learn you. Better look out who you're fooling with next
+time."
+
+The new boy went off brushing the dust from his clothes, sobbing,
+snuffling, and occasionally looking back and shaking his head and
+threatening what he would do to Tom the "next time he caught him out."
+To which Tom responded with jeers, and started off in high feather, and
+as soon as his back was turned the new boy snatched up a stone, threw
+it and hit him between the shoulders and then turned tail and ran like
+an antelope. Tom chased the traitor home, and thus found out where he
+lived. He then held a position at the gate for some time, daring the
+enemy to come outside, but the enemy only made faces at him through the
+window and declined. At last the enemy's mother appeared, and called
+Tom a bad, vicious, vulgar child, and ordered him away. So he went
+away; but he said he "'lowed" to "lay" for that boy.
+
+He got home pretty late that night, and when he climbed cautiously in
+at the window, he uncovered an ambuscade, in the person of his aunt;
+and when she saw the state his clothes were in her resolution to turn
+his Saturday holiday into captivity at hard labor became adamantine in
+its firmness.
+
+
+
+CHAPTER II
+
+SATURDAY morning was come, and all the summer world was bright and
+fresh, and brimming with life. There was a song in every heart; and if
+the heart was young the music issued at the lips. There was cheer in
+every face and a spring in every step. The locust-trees were in bloom
+and the fragrance of the blossoms filled the air. Cardiff Hill, beyond
+the village and above it, was green with vegetation and it lay just far
+enough away to seem a Delectable Land, dreamy, reposeful, and inviting.
+
+Tom appeared on the sidewalk with a bucket of whitewash and a
+long-handled brush. He surveyed the fence, and all gladness left him and
+a deep melancholy settled down upon his spirit. Thirty yards of board
+fence nine feet high. Life to him seemed hollow, and existence but a
+burden. Sighing, he dipped his brush and passed it along the topmost
+plank; repeated the operation; did it again; compared the insignificant
+whitewashed streak with the far-reaching continent of unwhitewashed
+fence, and sat down on a tree-box discouraged. Jim came skipping out at
+the gate with a tin pail, and singing Buffalo Gals. Bringing water from
+the town pump had always been hateful work in Tom's eyes, before, but
+now it did not strike him so. He remembered that there was company at
+the pump. White, mulatto, and negro boys and girls were always there
+waiting their turns, resting, trading playthings, quarrelling,
+fighting, skylarking. And he remembered that although the pump was only
+a hundred and fifty yards off, Jim never got back with a bucket of
+water under an hour--and even then somebody generally had to go after
+him. Tom said:
+
+"Say, Jim, I'll fetch the water if you'll whitewash some."
+
+Jim shook his head and said:
+
+"Can't, Mars Tom. Ole missis, she tole me I got to go an' git dis
+water an' not stop foolin' roun' wid anybody. She say she spec' Mars
+Tom gwine to ax me to whitewash, an' so she tole me go 'long an' 'tend
+to my own business--she 'lowed SHE'D 'tend to de whitewashin'."
+
+"Oh, never you mind what she said, Jim. That's the way she always
+talks. Gimme the bucket--I won't be gone only a a minute. SHE won't
+ever know."
+
+"Oh, I dasn't, Mars Tom. Ole missis she'd take an' tar de head off'n
+me. 'Deed she would."
+
+"SHE! She never licks anybody--whacks 'em over the head with her
+thimble--and who cares for that, I'd like to know. She talks awful, but
+talk don't hurt--anyways it don't if she don't cry. Jim, I'll give you
+a marvel. I'll give you a white alley!"
+
+Jim began to waver.
+
+"White alley, Jim! And it's a bully taw."
+
+"My! Dat's a mighty gay marvel, I tell you! But Mars Tom I's powerful
+'fraid ole missis--"
+
+"And besides, if you will I'll show you my sore toe."
+
+Jim was only human--this attraction was too much for him. He put down
+his pail, took the white alley, and bent over the toe with absorbing
+interest while the bandage was being unwound. In another moment he was
+flying down the street with his pail and a tingling rear, Tom was
+whitewashing with vigor, and Aunt Polly was retiring from the field
+with a slipper in her hand and triumph in her eye.
+
+But Tom's energy did not last. He began to think of the fun he had
+planned for this day, and his sorrows multiplied. Soon the free boys
+would come tripping along on all sorts of delicious expeditions, and
+they would make a world of fun of him for having to work--the very
+thought of it burnt him like fire. He got out his worldly wealth and
+examined it--bits of toys, marbles, and trash; enough to buy an
+exchange of WORK, maybe, but not half enough to buy so much as half an
+hour of pure freedom. So he returned his straitened means to his
+pocket, and gave up the idea of trying to buy the boys. At this dark
+and hopeless moment an inspiration burst upon him! Nothing less than a
+great, magnificent inspiration.
+
+He took up his brush and went tranquilly to work. Ben Rogers hove in
+sight presently--the very boy, of all boys, whose ridicule he had been
+dreading. Ben's gait was the hop-skip-and-jump--proof enough that his
+heart was light and his anticipations high. He was eating an apple, and
+giving a long, melodious whoop, at intervals, followed by a deep-toned
+ding-dong-dong, ding-dong-dong, for he was personating a steamboat. As
+he drew near, he slackened speed, took the middle of the street, leaned
+far over to starboard and rounded to ponderously and with laborious
+pomp and circumstance--for he was personating the Big Missouri, and
+considered himself to be drawing nine feet of water. He was boat and
+captain and engine-bells combined, so he had to imagine himself
+standing on his own hurricane-deck giving the orders and executing them:
+
+"Stop her, sir! Ting-a-ling-ling!" The headway ran almost out, and he
+drew up slowly toward the sidewalk.
+
+"Ship up to back! Ting-a-ling-ling!" His arms straightened and
+stiffened down his sides.
+
+"Set her back on the stabboard! Ting-a-ling-ling! Chow! ch-chow-wow!
+Chow!" His right hand, meantime, describing stately circles--for it was
+representing a forty-foot wheel.
+
+"Let her go back on the labboard! Ting-a-lingling! Chow-ch-chow-chow!"
+The left hand began to describe circles.
+
+"Stop the stabboard! Ting-a-ling-ling! Stop the labboard! Come ahead
+on the stabboard! Stop her! Let your outside turn over slow!
+Ting-a-ling-ling! Chow-ow-ow! Get out that head-line! LIVELY now!
+Come--out with your spring-line--what're you about there! Take a turn
+round that stump with the bight of it! Stand by that stage, now--let her
+go! Done with the engines, sir! Ting-a-ling-ling! SH'T! S'H'T! SH'T!"
+(trying the gauge-cocks).
+
+Tom went on whitewashing--paid no attention to the steamboat. Ben
+stared a moment and then said: "Hi-YI! YOU'RE up a stump, ain't you!"
+
+No answer. Tom surveyed his last touch with the eye of an artist, then
+he gave his brush another gentle sweep and surveyed the result, as
+before. Ben ranged up alongside of him. Tom's mouth watered for the
+apple, but he stuck to his work. Ben said:
+
+"Hello, old chap, you got to work, hey?"
+
+Tom wheeled suddenly and said:
+
+"Why, it's you, Ben! I warn't noticing."
+
+"Say--I'm going in a-swimming, I am. Don't you wish you could? But of
+course you'd druther WORK--wouldn't you? Course you would!"
+
+Tom contemplated the boy a bit, and said:
+
+"What do you call work?"
+
+"Why, ain't THAT work?"
+
+Tom resumed his whitewashing, and answered carelessly:
+
+"Well, maybe it is, and maybe it ain't. All I know, is, it suits Tom
+Sawyer."
+
+"Oh come, now, you don't mean to let on that you LIKE it?"
+
+The brush continued to move.
+
+"Like it? Well, I don't see why I oughtn't to like it. Does a boy get
+a chance to whitewash a fence every day?"
+
+That put the thing in a new light. Ben stopped nibbling his apple. Tom
+swept his brush daintily back and forth--stepped back to note the
+effect--added a touch here and there--criticised the effect again--Ben
+watching every move and getting more and more interested, more and more
+absorbed. Presently he said:
+
+"Say, Tom, let ME whitewash a little."
+
+Tom considered, was about to consent; but he altered his mind:
+
+"No--no--I reckon it wouldn't hardly do, Ben. You see, Aunt Polly's
+awful particular about this fence--right here on the street, you know
+--but if it was the back fence I wouldn't mind and SHE wouldn't. Yes,
+she's awful particular about this fence; it's got to be done very
+careful; I reckon there ain't one boy in a thousand, maybe two
+thousand, that can do it the way it's got to be done."
+
+"No--is that so? Oh come, now--lemme just try. Only just a little--I'd
+let YOU, if you was me, Tom."
+
+"Ben, I'd like to, honest injun; but Aunt Polly--well, Jim wanted to
+do it, but she wouldn't let him; Sid wanted to do it, and she wouldn't
+let Sid. Now don't you see how I'm fixed? If you was to tackle this
+fence and anything was to happen to it--"
+
+"Oh, shucks, I'll be just as careful. Now lemme try. Say--I'll give
+you the core of my apple."
+
+"Well, here--No, Ben, now don't. I'm afeard--"
+
+"I'll give you ALL of it!"
+
+Tom gave up the brush with reluctance in his face, but alacrity in his
+heart. And while the late steamer Big Missouri worked and sweated in
+the sun, the retired artist sat on a barrel in the shade close by,
+dangled his legs, munched his apple, and planned the slaughter of more
+innocents. There was no lack of material; boys happened along every
+little while; they came to jeer, but remained to whitewash. By the time
+Ben was fagged out, Tom had traded the next chance to Billy Fisher for
+a kite, in good repair; and when he played out, Johnny Miller bought in
+for a dead rat and a string to swing it with--and so on, and so on,
+hour after hour. And when the middle of the afternoon came, from being
+a poor poverty-stricken boy in the morning, Tom was literally rolling
+in wealth. He had besides the things before mentioned, twelve marbles,
+part of a jews-harp, a piece of blue bottle-glass to look through, a
+spool cannon, a key that wouldn't unlock anything, a fragment of chalk,
+a glass stopper of a decanter, a tin soldier, a couple of tadpoles, six
+fire-crackers, a kitten with only one eye, a brass doorknob, a
+dog-collar--but no dog--the handle of a knife, four pieces of
+orange-peel, and a dilapidated old window sash.
+
+He had had a nice, good, idle time all the while--plenty of company
+--and the fence had three coats of whitewash on it! If he hadn't run out
+of whitewash he would have bankrupted every boy in the village.
+
+Tom said to himself that it was not such a hollow world, after all. He
+had discovered a great law of human action, without knowing it--namely,
+that in order to make a man or a boy covet a thing, it is only
+necessary to make the thing difficult to attain. If he had been a great
+and wise philosopher, like the writer of this book, he would now have
+comprehended that Work consists of whatever a body is OBLIGED to do,
+and that Play consists of whatever a body is not obliged to do. And
+this would help him to understand why constructing artificial flowers
+or performing on a tread-mill is work, while rolling ten-pins or
+climbing Mont Blanc is only amusement. There are wealthy gentlemen in
+England who drive four-horse passenger-coaches twenty or thirty miles
+on a daily line, in the summer, because the privilege costs them
+considerable money; but if they were offered wages for the service,
+that would turn it into work and then they would resign.
+
+The boy mused awhile over the substantial change which had taken place
+in his worldly circumstances, and then wended toward headquarters to
+report.
+
+
+
+CHAPTER III
+
+TOM presented himself before Aunt Polly, who was sitting by an open
+window in a pleasant rearward apartment, which was bedroom,
+breakfast-room, dining-room, and library, combined. The balmy summer
+air, the restful quiet, the odor of the flowers, and the drowsing murmur
+of the bees had had their effect, and she was nodding over her knitting
+--for she had no company but the cat, and it was asleep in her lap. Her
+spectacles were propped up on her gray head for safety. She had thought
+that of course Tom had deserted long ago, and she wondered at seeing him
+place himself in her power again in this intrepid way. He said: "Mayn't
+I go and play now, aunt?"
+
+"What, a'ready? How much have you done?"
+
+"It's all done, aunt."
+
+"Tom, don't lie to me--I can't bear it."
+
+"I ain't, aunt; it IS all done."
+
+Aunt Polly placed small trust in such evidence. She went out to see
+for herself; and she would have been content to find twenty per cent.
+of Tom's statement true. When she found the entire fence whitewashed,
+and not only whitewashed but elaborately coated and recoated, and even
+a streak added to the ground, her astonishment was almost unspeakable.
+She said:
+
+"Well, I never! There's no getting round it, you can work when you're
+a mind to, Tom." And then she diluted the compliment by adding, "But
+it's powerful seldom you're a mind to, I'm bound to say. Well, go 'long
+and play; but mind you get back some time in a week, or I'll tan you."
+
+She was so overcome by the splendor of his achievement that she took
+him into the closet and selected a choice apple and delivered it to
+him, along with an improving lecture upon the added value and flavor a
+treat took to itself when it came without sin through virtuous effort.
+And while she closed with a happy Scriptural flourish, he "hooked" a
+doughnut.
+
+Then he skipped out, and saw Sid just starting up the outside stairway
+that led to the back rooms on the second floor. Clods were handy and
+the air was full of them in a twinkling. They raged around Sid like a
+hail-storm; and before Aunt Polly could collect her surprised faculties
+and sally to the rescue, six or seven clods had taken personal effect,
+and Tom was over the fence and gone. There was a gate, but as a general
+thing he was too crowded for time to make use of it. His soul was at
+peace, now that he had settled with Sid for calling attention to his
+black thread and getting him into trouble.
+
+Tom skirted the block, and came round into a muddy alley that led by
+the back of his aunt's cow-stable. He presently got safely beyond the
+reach of capture and punishment, and hastened toward the public square
+of the village, where two "military" companies of boys had met for
+conflict, according to previous appointment. Tom was General of one of
+these armies, Joe Harper (a bosom friend) General of the other. These
+two great commanders did not condescend to fight in person--that being
+better suited to the still smaller fry--but sat together on an eminence
+and conducted the field operations by orders delivered through
+aides-de-camp. Tom's army won a great victory, after a long and
+hard-fought battle. Then the dead were counted, prisoners exchanged,
+the terms of the next disagreement agreed upon, and the day for the
+necessary battle appointed; after which the armies fell into line and
+marched away, and Tom turned homeward alone.
+
+As he was passing by the house where Jeff Thatcher lived, he saw a new
+girl in the garden--a lovely little blue-eyed creature with yellow hair
+plaited into two long-tails, white summer frock and embroidered
+pantalettes. The fresh-crowned hero fell without firing a shot. A
+certain Amy Lawrence vanished out of his heart and left not even a
+memory of herself behind. He had thought he loved her to distraction;
+he had regarded his passion as adoration; and behold it was only a poor
+little evanescent partiality. He had been months winning her; she had
+confessed hardly a week ago; he had been the happiest and the proudest
+boy in the world only seven short days, and here in one instant of time
+she had gone out of his heart like a casual stranger whose visit is
+done.
+
+He worshipped this new angel with furtive eye, till he saw that she
+had discovered him; then he pretended he did not know she was present,
+and began to "show off" in all sorts of absurd boyish ways, in order to
+win her admiration. He kept up this grotesque foolishness for some
+time; but by-and-by, while he was in the midst of some dangerous
+gymnastic performances, he glanced aside and saw that the little girl
+was wending her way toward the house. Tom came up to the fence and
+leaned on it, grieving, and hoping she would tarry yet awhile longer.
+She halted a moment on the steps and then moved toward the door. Tom
+heaved a great sigh as she put her foot on the threshold. But his face
+lit up, right away, for she tossed a pansy over the fence a moment
+before she disappeared.
+
+The boy ran around and stopped within a foot or two of the flower, and
+then shaded his eyes with his hand and began to look down street as if
+he had discovered something of interest going on in that direction.
+Presently he picked up a straw and began trying to balance it on his
+nose, with his head tilted far back; and as he moved from side to side,
+in his efforts, he edged nearer and nearer toward the pansy; finally
+his bare foot rested upon it, his pliant toes closed upon it, and he
+hopped away with the treasure and disappeared round the corner. But
+only for a minute--only while he could button the flower inside his
+jacket, next his heart--or next his stomach, possibly, for he was not
+much posted in anatomy, and not hypercritical, anyway.
+
+He returned, now, and hung about the fence till nightfall, "showing
+off," as before; but the girl never exhibited herself again, though Tom
+comforted himself a little with the hope that she had been near some
+window, meantime, and been aware of his attentions. Finally he strode
+home reluctantly, with his poor head full of visions.
+
+All through supper his spirits were so high that his aunt wondered
+"what had got into the child." He took a good scolding about clodding
+Sid, and did not seem to mind it in the least. He tried to steal sugar
+under his aunt's very nose, and got his knuckles rapped for it. He said:
+
+"Aunt, you don't whack Sid when he takes it."
+
+"Well, Sid don't torment a body the way you do. You'd be always into
+that sugar if I warn't watching you."
+
+Presently she stepped into the kitchen, and Sid, happy in his
+immunity, reached for the sugar-bowl--a sort of glorying over Tom which
+was wellnigh unbearable. But Sid's fingers slipped and the bowl dropped
+and broke. Tom was in ecstasies. In such ecstasies that he even
+controlled his tongue and was silent. He said to himself that he would
+not speak a word, even when his aunt came in, but would sit perfectly
+still till she asked who did the mischief; and then he would tell, and
+there would be nothing so good in the world as to see that pet model
+"catch it." He was so brimful of exultation that he could hardly hold
+himself when the old lady came back and stood above the wreck
+discharging lightnings of wrath from over her spectacles. He said to
+himself, "Now it's coming!" And the next instant he was sprawling on
+the floor! The potent palm was uplifted to strike again when Tom cried
+out:
+
+"Hold on, now, what 'er you belting ME for?--Sid broke it!"
+
+Aunt Polly paused, perplexed, and Tom looked for healing pity. But
+when she got her tongue again, she only said:
+
+"Umf! Well, you didn't get a lick amiss, I reckon. You been into some
+other audacious mischief when I wasn't around, like enough."
+
+Then her conscience reproached her, and she yearned to say something
+kind and loving; but she judged that this would be construed into a
+confession that she had been in the wrong, and discipline forbade that.
+So she kept silence, and went about her affairs with a troubled heart.
+Tom sulked in a corner and exalted his woes. He knew that in her heart
+his aunt was on her knees to him, and he was morosely gratified by the
+consciousness of it. He would hang out no signals, he would take notice
+of none. He knew that a yearning glance fell upon him, now and then,
+through a film of tears, but he refused recognition of it. He pictured
+himself lying sick unto death and his aunt bending over him beseeching
+one little forgiving word, but he would turn his face to the wall, and
+die with that word unsaid. Ah, how would she feel then? And he pictured
+himself brought home from the river, dead, with his curls all wet, and
+his sore heart at rest. How she would throw herself upon him, and how
+her tears would fall like rain, and her lips pray God to give her back
+her boy and she would never, never abuse him any more! But he would lie
+there cold and white and make no sign--a poor little sufferer, whose
+griefs were at an end. He so worked upon his feelings with the pathos
+of these dreams, that he had to keep swallowing, he was so like to
+choke; and his eyes swam in a blur of water, which overflowed when he
+winked, and ran down and trickled from the end of his nose. And such a
+luxury to him was this petting of his sorrows, that he could not bear
+to have any worldly cheeriness or any grating delight intrude upon it;
+it was too sacred for such contact; and so, presently, when his cousin
+Mary danced in, all alive with the joy of seeing home again after an
+age-long visit of one week to the country, he got up and moved in
+clouds and darkness out at one door as she brought song and sunshine in
+at the other.
+
+He wandered far from the accustomed haunts of boys, and sought
+desolate places that were in harmony with his spirit. A log raft in the
+river invited him, and he seated himself on its outer edge and
+contemplated the dreary vastness of the stream, wishing, the while,
+that he could only be drowned, all at once and unconsciously, without
+undergoing the uncomfortable routine devised by nature. Then he thought
+of his flower. He got it out, rumpled and wilted, and it mightily
+increased his dismal felicity. He wondered if she would pity him if she
+knew? Would she cry, and wish that she had a right to put her arms
+around his neck and comfort him? Or would she turn coldly away like all
+the hollow world? This picture brought such an agony of pleasurable
+suffering that he worked it over and over again in his mind and set it
+up in new and varied lights, till he wore it threadbare. At last he
+rose up sighing and departed in the darkness.
+
+About half-past nine or ten o'clock he came along the deserted street
+to where the Adored Unknown lived; he paused a moment; no sound fell
+upon his listening ear; a candle was casting a dull glow upon the
+curtain of a second-story window. Was the sacred presence there? He
+climbed the fence, threaded his stealthy way through the plants, till
+he stood under that window; he looked up at it long, and with emotion;
+then he laid him down on the ground under it, disposing himself upon
+his back, with his hands clasped upon his breast and holding his poor
+wilted flower. And thus he would die--out in the cold world, with no
+shelter over his homeless head, no friendly hand to wipe the
+death-damps from his brow, no loving face to bend pityingly over him
+when the great agony came. And thus SHE would see him when she looked
+out upon the glad morning, and oh! would she drop one little tear upon
+his poor, lifeless form, would she heave one little sigh to see a bright
+young life so rudely blighted, so untimely cut down?
+
+The window went up, a maid-servant's discordant voice profaned the
+holy calm, and a deluge of water drenched the prone martyr's remains!
+
+The strangling hero sprang up with a relieving snort. There was a whiz
+as of a missile in the air, mingled with the murmur of a curse, a sound
+as of shivering glass followed, and a small, vague form went over the
+fence and shot away in the gloom.
+
+Not long after, as Tom, all undressed for bed, was surveying his
+drenched garments by the light of a tallow dip, Sid woke up; but if he
+had any dim idea of making any "references to allusions," he thought
+better of it and held his peace, for there was danger in Tom's eye.
+
+Tom turned in without the added vexation of prayers, and Sid made
+mental note of the omission.
+
+
+
+CHAPTER IV
+
+THE sun rose upon a tranquil world, and beamed down upon the peaceful
+village like a benediction. Breakfast over, Aunt Polly had family
+worship: it began with a prayer built from the ground up of solid
+courses of Scriptural quotations, welded together with a thin mortar of
+originality; and from the summit of this she delivered a grim chapter
+of the Mosaic Law, as from Sinai.
+
+Then Tom girded up his loins, so to speak, and went to work to "get
+his verses." Sid had learned his lesson days before. Tom bent all his
+energies to the memorizing of five verses, and he chose part of the
+Sermon on the Mount, because he could find no verses that were shorter.
+At the end of half an hour Tom had a vague general idea of his lesson,
+but no more, for his mind was traversing the whole field of human
+thought, and his hands were busy with distracting recreations. Mary
+took his book to hear him recite, and he tried to find his way through
+the fog:
+
+"Blessed are the--a--a--"
+
+"Poor"--
+
+"Yes--poor; blessed are the poor--a--a--"
+
+"In spirit--"
+
+"In spirit; blessed are the poor in spirit, for they--they--"
+
+"THEIRS--"
+
+"For THEIRS. Blessed are the poor in spirit, for theirs is the kingdom
+of heaven. Blessed are they that mourn, for they--they--"
+
+"Sh--"
+
+"For they--a--"
+
+"S, H, A--"
+
+"For they S, H--Oh, I don't know what it is!"
+
+"SHALL!"
+
+"Oh, SHALL! for they shall--for they shall--a--a--shall mourn--a--a--
+blessed are they that shall--they that--a--they that shall mourn, for
+they shall--a--shall WHAT? Why don't you tell me, Mary?--what do you
+want to be so mean for?"
+
+"Oh, Tom, you poor thick-headed thing, I'm not teasing you. I wouldn't
+do that. You must go and learn it again. Don't you be discouraged, Tom,
+you'll manage it--and if you do, I'll give you something ever so nice.
+There, now, that's a good boy."
+
+"All right! What is it, Mary, tell me what it is."
+
+"Never you mind, Tom. You know if I say it's nice, it is nice."
+
+"You bet you that's so, Mary. All right, I'll tackle it again."
+
+And he did "tackle it again"--and under the double pressure of
+curiosity and prospective gain he did it with such spirit that he
+accomplished a shining success. Mary gave him a brand-new "Barlow"
+knife worth twelve and a half cents; and the convulsion of delight that
+swept his system shook him to his foundations. True, the knife would
+not cut anything, but it was a "sure-enough" Barlow, and there was
+inconceivable grandeur in that--though where the Western boys ever got
+the idea that such a weapon could possibly be counterfeited to its
+injury is an imposing mystery and will always remain so, perhaps. Tom
+contrived to scarify the cupboard with it, and was arranging to begin
+on the bureau, when he was called off to dress for Sunday-school.
+
+Mary gave him a tin basin of water and a piece of soap, and he went
+outside the door and set the basin on a little bench there; then he
+dipped the soap in the water and laid it down; turned up his sleeves;
+poured out the water on the ground, gently, and then entered the
+kitchen and began to wipe his face diligently on the towel behind the
+door. But Mary removed the towel and said:
+
+"Now ain't you ashamed, Tom. You mustn't be so bad. Water won't hurt
+you."
+
+Tom was a trifle disconcerted. The basin was refilled, and this time
+he stood over it a little while, gathering resolution; took in a big
+breath and began. When he entered the kitchen presently, with both eyes
+shut and groping for the towel with his hands, an honorable testimony
+of suds and water was dripping from his face. But when he emerged from
+the towel, he was not yet satisfactory, for the clean territory stopped
+short at his chin and his jaws, like a mask; below and beyond this line
+there was a dark expanse of unirrigated soil that spread downward in
+front and backward around his neck. Mary took him in hand, and when she
+was done with him he was a man and a brother, without distinction of
+color, and his saturated hair was neatly brushed, and its short curls
+wrought into a dainty and symmetrical general effect. [He privately
+smoothed out the curls, with labor and difficulty, and plastered his
+hair close down to his head; for he held curls to be effeminate, and
+his own filled his life with bitterness.] Then Mary got out a suit of
+his clothing that had been used only on Sundays during two years--they
+were simply called his "other clothes"--and so by that we know the
+size of his wardrobe. The girl "put him to rights" after he had dressed
+himself; she buttoned his neat roundabout up to his chin, turned his
+vast shirt collar down over his shoulders, brushed him off and crowned
+him with his speckled straw hat. He now looked exceedingly improved and
+uncomfortable. He was fully as uncomfortable as he looked; for there
+was a restraint about whole clothes and cleanliness that galled him. He
+hoped that Mary would forget his shoes, but the hope was blighted; she
+coated them thoroughly with tallow, as was the custom, and brought them
+out. He lost his temper and said he was always being made to do
+everything he didn't want to do. But Mary said, persuasively:
+
+"Please, Tom--that's a good boy."
+
+So he got into the shoes snarling. Mary was soon ready, and the three
+children set out for Sunday-school--a place that Tom hated with his
+whole heart; but Sid and Mary were fond of it.
+
+Sabbath-school hours were from nine to half-past ten; and then church
+service. Two of the children always remained for the sermon
+voluntarily, and the other always remained too--for stronger reasons.
+The church's high-backed, uncushioned pews would seat about three
+hundred persons; the edifice was but a small, plain affair, with a sort
+of pine board tree-box on top of it for a steeple. At the door Tom
+dropped back a step and accosted a Sunday-dressed comrade:
+
+"Say, Billy, got a yaller ticket?"
+
+"Yes."
+
+"What'll you take for her?"
+
+"What'll you give?"
+
+"Piece of lickrish and a fish-hook."
+
+"Less see 'em."
+
+Tom exhibited. They were satisfactory, and the property changed hands.
+Then Tom traded a couple of white alleys for three red tickets, and
+some small trifle or other for a couple of blue ones. He waylaid other
+boys as they came, and went on buying tickets of various colors ten or
+fifteen minutes longer. He entered the church, now, with a swarm of
+clean and noisy boys and girls, proceeded to his seat and started a
+quarrel with the first boy that came handy. The teacher, a grave,
+elderly man, interfered; then turned his back a moment and Tom pulled a
+boy's hair in the next bench, and was absorbed in his book when the boy
+turned around; stuck a pin in another boy, presently, in order to hear
+him say "Ouch!" and got a new reprimand from his teacher. Tom's whole
+class were of a pattern--restless, noisy, and troublesome. When they
+came to recite their lessons, not one of them knew his verses
+perfectly, but had to be prompted all along. However, they worried
+through, and each got his reward--in small blue tickets, each with a
+passage of Scripture on it; each blue ticket was pay for two verses of
+the recitation. Ten blue tickets equalled a red one, and could be
+exchanged for it; ten red tickets equalled a yellow one; for ten yellow
+tickets the superintendent gave a very plainly bound Bible (worth forty
+cents in those easy times) to the pupil. How many of my readers would
+have the industry and application to memorize two thousand verses, even
+for a Dore Bible? And yet Mary had acquired two Bibles in this way--it
+was the patient work of two years--and a boy of German parentage had
+won four or five. He once recited three thousand verses without
+stopping; but the strain upon his mental faculties was too great, and
+he was little better than an idiot from that day forth--a grievous
+misfortune for the school, for on great occasions, before company, the
+superintendent (as Tom expressed it) had always made this boy come out
+and "spread himself." Only the older pupils managed to keep their
+tickets and stick to their tedious work long enough to get a Bible, and
+so the delivery of one of these prizes was a rare and noteworthy
+circumstance; the successful pupil was so great and conspicuous for
+that day that on the spot every scholar's heart was fired with a fresh
+ambition that often lasted a couple of weeks. It is possible that Tom's
+mental stomach had never really hungered for one of those prizes, but
+unquestionably his entire being had for many a day longed for the glory
+and the eclat that came with it.
+
+In due course the superintendent stood up in front of the pulpit, with
+a closed hymn-book in his hand and his forefinger inserted between its
+leaves, and commanded attention. When a Sunday-school superintendent
+makes his customary little speech, a hymn-book in the hand is as
+necessary as is the inevitable sheet of music in the hand of a singer
+who stands forward on the platform and sings a solo at a concert
+--though why, is a mystery: for neither the hymn-book nor the sheet of
+music is ever referred to by the sufferer. This superintendent was a
+slim creature of thirty-five, with a sandy goatee and short sandy hair;
+he wore a stiff standing-collar whose upper edge almost reached his
+ears and whose sharp points curved forward abreast the corners of his
+mouth--a fence that compelled a straight lookout ahead, and a turning
+of the whole body when a side view was required; his chin was propped
+on a spreading cravat which was as broad and as long as a bank-note,
+and had fringed ends; his boot toes were turned sharply up, in the
+fashion of the day, like sleigh-runners--an effect patiently and
+laboriously produced by the young men by sitting with their toes
+pressed against a wall for hours together. Mr. Walters was very earnest
+of mien, and very sincere and honest at heart; and he held sacred
+things and places in such reverence, and so separated them from worldly
+matters, that unconsciously to himself his Sunday-school voice had
+acquired a peculiar intonation which was wholly absent on week-days. He
+began after this fashion:
+
+"Now, children, I want you all to sit up just as straight and pretty
+as you can and give me all your attention for a minute or two. There
+--that is it. That is the way good little boys and girls should do. I see
+one little girl who is looking out of the window--I am afraid she
+thinks I am out there somewhere--perhaps up in one of the trees making
+a speech to the little birds. [Applausive titter.] I want to tell you
+how good it makes me feel to see so many bright, clean little faces
+assembled in a place like this, learning to do right and be good." And
+so forth and so on. It is not necessary to set down the rest of the
+oration. It was of a pattern which does not vary, and so it is familiar
+to us all.
+
+The latter third of the speech was marred by the resumption of fights
+and other recreations among certain of the bad boys, and by fidgetings
+and whisperings that extended far and wide, washing even to the bases
+of isolated and incorruptible rocks like Sid and Mary. But now every
+sound ceased suddenly, with the subsidence of Mr. Walters' voice, and
+the conclusion of the speech was received with a burst of silent
+gratitude.
+
+A good part of the whispering had been occasioned by an event which
+was more or less rare--the entrance of visitors: lawyer Thatcher,
+accompanied by a very feeble and aged man; a fine, portly, middle-aged
+gentleman with iron-gray hair; and a dignified lady who was doubtless
+the latter's wife. The lady was leading a child. Tom had been restless
+and full of chafings and repinings; conscience-smitten, too--he could
+not meet Amy Lawrence's eye, he could not brook her loving gaze. But
+when he saw this small new-comer his soul was all ablaze with bliss in
+a moment. The next moment he was "showing off" with all his might
+--cuffing boys, pulling hair, making faces--in a word, using every art
+that seemed likely to fascinate a girl and win her applause. His
+exaltation had but one alloy--the memory of his humiliation in this
+angel's garden--and that record in sand was fast washing out, under
+the waves of happiness that were sweeping over it now.
+
+The visitors were given the highest seat of honor, and as soon as Mr.
+Walters' speech was finished, he introduced them to the school. The
+middle-aged man turned out to be a prodigious personage--no less a one
+than the county judge--altogether the most august creation these
+children had ever looked upon--and they wondered what kind of material
+he was made of--and they half wanted to hear him roar, and were half
+afraid he might, too. He was from Constantinople, twelve miles away--so
+he had travelled, and seen the world--these very eyes had looked upon
+the county court-house--which was said to have a tin roof. The awe
+which these reflections inspired was attested by the impressive silence
+and the ranks of staring eyes. This was the great Judge Thatcher,
+brother of their own lawyer. Jeff Thatcher immediately went forward, to
+be familiar with the great man and be envied by the school. It would
+have been music to his soul to hear the whisperings:
+
+"Look at him, Jim! He's a going up there. Say--look! he's a going to
+shake hands with him--he IS shaking hands with him! By jings, don't you
+wish you was Jeff?"
+
+Mr. Walters fell to "showing off," with all sorts of official
+bustlings and activities, giving orders, delivering judgments,
+discharging directions here, there, everywhere that he could find a
+target. The librarian "showed off"--running hither and thither with his
+arms full of books and making a deal of the splutter and fuss that
+insect authority delights in. The young lady teachers "showed off"
+--bending sweetly over pupils that were lately being boxed, lifting
+pretty warning fingers at bad little boys and patting good ones
+lovingly. The young gentlemen teachers "showed off" with small
+scoldings and other little displays of authority and fine attention to
+discipline--and most of the teachers, of both sexes, found business up
+at the library, by the pulpit; and it was business that frequently had
+to be done over again two or three times (with much seeming vexation).
+The little girls "showed off" in various ways, and the little boys
+"showed off" with such diligence that the air was thick with paper wads
+and the murmur of scufflings. And above it all the great man sat and
+beamed a majestic judicial smile upon all the house, and warmed himself
+in the sun of his own grandeur--for he was "showing off," too.
+
+There was only one thing wanting to make Mr. Walters' ecstasy
+complete, and that was a chance to deliver a Bible-prize and exhibit a
+prodigy. Several pupils had a few yellow tickets, but none had enough
+--he had been around among the star pupils inquiring. He would have given
+worlds, now, to have that German lad back again with a sound mind.
+
+And now at this moment, when hope was dead, Tom Sawyer came forward
+with nine yellow tickets, nine red tickets, and ten blue ones, and
+demanded a Bible. This was a thunderbolt out of a clear sky. Walters
+was not expecting an application from this source for the next ten
+years. But there was no getting around it--here were the certified
+checks, and they were good for their face. Tom was therefore elevated
+to a place with the Judge and the other elect, and the great news was
+announced from headquarters. It was the most stunning surprise of the
+decade, and so profound was the sensation that it lifted the new hero
+up to the judicial one's altitude, and the school had two marvels to
+gaze upon in place of one. The boys were all eaten up with envy--but
+those that suffered the bitterest pangs were those who perceived too
+late that they themselves had contributed to this hated splendor by
+trading tickets to Tom for the wealth he had amassed in selling
+whitewashing privileges. These despised themselves, as being the dupes
+of a wily fraud, a guileful snake in the grass.
+
+The prize was delivered to Tom with as much effusion as the
+superintendent could pump up under the circumstances; but it lacked
+somewhat of the true gush, for the poor fellow's instinct taught him
+that there was a mystery here that could not well bear the light,
+perhaps; it was simply preposterous that this boy had warehoused two
+thousand sheaves of Scriptural wisdom on his premises--a dozen would
+strain his capacity, without a doubt.
+
+Amy Lawrence was proud and glad, and she tried to make Tom see it in
+her face--but he wouldn't look. She wondered; then she was just a grain
+troubled; next a dim suspicion came and went--came again; she watched;
+a furtive glance told her worlds--and then her heart broke, and she was
+jealous, and angry, and the tears came and she hated everybody. Tom
+most of all (she thought).
+
+Tom was introduced to the Judge; but his tongue was tied, his breath
+would hardly come, his heart quaked--partly because of the awful
+greatness of the man, but mainly because he was her parent. He would
+have liked to fall down and worship him, if it were in the dark. The
+Judge put his hand on Tom's head and called him a fine little man, and
+asked him what his name was. The boy stammered, gasped, and got it out:
+
+"Tom."
+
+"Oh, no, not Tom--it is--"
+
+"Thomas."
+
+"Ah, that's it. I thought there was more to it, maybe. That's very
+well. But you've another one I daresay, and you'll tell it to me, won't
+you?"
+
+"Tell the gentleman your other name, Thomas," said Walters, "and say
+sir. You mustn't forget your manners."
+
+"Thomas Sawyer--sir."
+
+"That's it! That's a good boy. Fine boy. Fine, manly little fellow.
+Two thousand verses is a great many--very, very great many. And you
+never can be sorry for the trouble you took to learn them; for
+knowledge is worth more than anything there is in the world; it's what
+makes great men and good men; you'll be a great man and a good man
+yourself, some day, Thomas, and then you'll look back and say, It's all
+owing to the precious Sunday-school privileges of my boyhood--it's all
+owing to my dear teachers that taught me to learn--it's all owing to
+the good superintendent, who encouraged me, and watched over me, and
+gave me a beautiful Bible--a splendid elegant Bible--to keep and have
+it all for my own, always--it's all owing to right bringing up! That is
+what you will say, Thomas--and you wouldn't take any money for those
+two thousand verses--no indeed you wouldn't. And now you wouldn't mind
+telling me and this lady some of the things you've learned--no, I know
+you wouldn't--for we are proud of little boys that learn. Now, no
+doubt you know the names of all the twelve disciples. Won't you tell us
+the names of the first two that were appointed?"
+
+Tom was tugging at a button-hole and looking sheepish. He blushed,
+now, and his eyes fell. Mr. Walters' heart sank within him. He said to
+himself, it is not possible that the boy can answer the simplest
+question--why DID the Judge ask him? Yet he felt obliged to speak up
+and say:
+
+"Answer the gentleman, Thomas--don't be afraid."
+
+Tom still hung fire.
+
+"Now I know you'll tell me," said the lady. "The names of the first
+two disciples were--"
+
+"DAVID AND GOLIAH!"
+
+Let us draw the curtain of charity over the rest of the scene.
+
+
+
+CHAPTER V
+
+ABOUT half-past ten the cracked bell of the small church began to
+ring, and presently the people began to gather for the morning sermon.
+The Sunday-school children distributed themselves about the house and
+occupied pews with their parents, so as to be under supervision. Aunt
+Polly came, and Tom and Sid and Mary sat with her--Tom being placed
+next the aisle, in order that he might be as far away from the open
+window and the seductive outside summer scenes as possible. The crowd
+filed up the aisles: the aged and needy postmaster, who had seen better
+days; the mayor and his wife--for they had a mayor there, among other
+unnecessaries; the justice of the peace; the widow Douglass, fair,
+smart, and forty, a generous, good-hearted soul and well-to-do, her
+hill mansion the only palace in the town, and the most hospitable and
+much the most lavish in the matter of festivities that St. Petersburg
+could boast; the bent and venerable Major and Mrs. Ward; lawyer
+Riverson, the new notable from a distance; next the belle of the
+village, followed by a troop of lawn-clad and ribbon-decked young
+heart-breakers; then all the young clerks in town in a body--for they
+had stood in the vestibule sucking their cane-heads, a circling wall of
+oiled and simpering admirers, till the last girl had run their gantlet;
+and last of all came the Model Boy, Willie Mufferson, taking as heedful
+care of his mother as if she were cut glass. He always brought his
+mother to church, and was the pride of all the matrons. The boys all
+hated him, he was so good. And besides, he had been "thrown up to them"
+so much. His white handkerchief was hanging out of his pocket behind, as
+usual on Sundays--accidentally. Tom had no handkerchief, and he looked
+upon boys who had as snobs.
+
+The congregation being fully assembled, now, the bell rang once more,
+to warn laggards and stragglers, and then a solemn hush fell upon the
+church which was only broken by the tittering and whispering of the
+choir in the gallery. The choir always tittered and whispered all
+through service. There was once a church choir that was not ill-bred,
+but I have forgotten where it was, now. It was a great many years ago,
+and I can scarcely remember anything about it, but I think it was in
+some foreign country.
+
+The minister gave out the hymn, and read it through with a relish, in
+a peculiar style which was much admired in that part of the country.
+His voice began on a medium key and climbed steadily up till it reached
+a certain point, where it bore with strong emphasis upon the topmost
+word and then plunged down as if from a spring-board:
+
+ Shall I be car-ri-ed toe the skies, on flow'ry BEDS of ease,
+
+ Whilst others fight to win the prize, and sail thro' BLOODY seas?
+
+He was regarded as a wonderful reader. At church "sociables" he was
+always called upon to read poetry; and when he was through, the ladies
+would lift up their hands and let them fall helplessly in their laps,
+and "wall" their eyes, and shake their heads, as much as to say, "Words
+cannot express it; it is too beautiful, TOO beautiful for this mortal
+earth."
+
+After the hymn had been sung, the Rev. Mr. Sprague turned himself into
+a bulletin-board, and read off "notices" of meetings and societies and
+things till it seemed that the list would stretch out to the crack of
+doom--a queer custom which is still kept up in America, even in cities,
+away here in this age of abundant newspapers. Often, the less there is
+to justify a traditional custom, the harder it is to get rid of it.
+
+And now the minister prayed. A good, generous prayer it was, and went
+into details: it pleaded for the church, and the little children of the
+church; for the other churches of the village; for the village itself;
+for the county; for the State; for the State officers; for the United
+States; for the churches of the United States; for Congress; for the
+President; for the officers of the Government; for poor sailors, tossed
+by stormy seas; for the oppressed millions groaning under the heel of
+European monarchies and Oriental despotisms; for such as have the light
+and the good tidings, and yet have not eyes to see nor ears to hear
+withal; for the heathen in the far islands of the sea; and closed with
+a supplication that the words he was about to speak might find grace
+and favor, and be as seed sown in fertile ground, yielding in time a
+grateful harvest of good. Amen.
+
+There was a rustling of dresses, and the standing congregation sat
+down. The boy whose history this book relates did not enjoy the prayer,
+he only endured it--if he even did that much. He was restive all
+through it; he kept tally of the details of the prayer, unconsciously
+--for he was not listening, but he knew the ground of old, and the
+clergyman's regular route over it--and when a little trifle of new
+matter was interlarded, his ear detected it and his whole nature
+resented it; he considered additions unfair, and scoundrelly. In the
+midst of the prayer a fly had lit on the back of the pew in front of
+him and tortured his spirit by calmly rubbing its hands together,
+embracing its head with its arms, and polishing it so vigorously that
+it seemed to almost part company with the body, and the slender thread
+of a neck was exposed to view; scraping its wings with its hind legs
+and smoothing them to its body as if they had been coat-tails; going
+through its whole toilet as tranquilly as if it knew it was perfectly
+safe. As indeed it was; for as sorely as Tom's hands itched to grab for
+it they did not dare--he believed his soul would be instantly destroyed
+if he did such a thing while the prayer was going on. But with the
+closing sentence his hand began to curve and steal forward; and the
+instant the "Amen" was out the fly was a prisoner of war. His aunt
+detected the act and made him let it go.
+
+The minister gave out his text and droned along monotonously through
+an argument that was so prosy that many a head by and by began to nod
+--and yet it was an argument that dealt in limitless fire and brimstone
+and thinned the predestined elect down to a company so small as to be
+hardly worth the saving. Tom counted the pages of the sermon; after
+church he always knew how many pages there had been, but he seldom knew
+anything else about the discourse. However, this time he was really
+interested for a little while. The minister made a grand and moving
+picture of the assembling together of the world's hosts at the
+millennium when the lion and the lamb should lie down together and a
+little child should lead them. But the pathos, the lesson, the moral of
+the great spectacle were lost upon the boy; he only thought of the
+conspicuousness of the principal character before the on-looking
+nations; his face lit with the thought, and he said to himself that he
+wished he could be that child, if it was a tame lion.
+
+Now he lapsed into suffering again, as the dry argument was resumed.
+Presently he bethought him of a treasure he had and got it out. It was
+a large black beetle with formidable jaws--a "pinchbug," he called it.
+It was in a percussion-cap box. The first thing the beetle did was to
+take him by the finger. A natural fillip followed, the beetle went
+floundering into the aisle and lit on its back, and the hurt finger
+went into the boy's mouth. The beetle lay there working its helpless
+legs, unable to turn over. Tom eyed it, and longed for it; but it was
+safe out of his reach. Other people uninterested in the sermon found
+relief in the beetle, and they eyed it too. Presently a vagrant poodle
+dog came idling along, sad at heart, lazy with the summer softness and
+the quiet, weary of captivity, sighing for change. He spied the beetle;
+the drooping tail lifted and wagged. He surveyed the prize; walked
+around it; smelt at it from a safe distance; walked around it again;
+grew bolder, and took a closer smell; then lifted his lip and made a
+gingerly snatch at it, just missing it; made another, and another;
+began to enjoy the diversion; subsided to his stomach with the beetle
+between his paws, and continued his experiments; grew weary at last,
+and then indifferent and absent-minded. His head nodded, and little by
+little his chin descended and touched the enemy, who seized it. There
+was a sharp yelp, a flirt of the poodle's head, and the beetle fell a
+couple of yards away, and lit on its back once more. The neighboring
+spectators shook with a gentle inward joy, several faces went behind
+fans and handkerchiefs, and Tom was entirely happy. The dog looked
+foolish, and probably felt so; but there was resentment in his heart,
+too, and a craving for revenge. So he went to the beetle and began a
+wary attack on it again; jumping at it from every point of a circle,
+lighting with his fore-paws within an inch of the creature, making even
+closer snatches at it with his teeth, and jerking his head till his
+ears flapped again. But he grew tired once more, after a while; tried
+to amuse himself with a fly but found no relief; followed an ant
+around, with his nose close to the floor, and quickly wearied of that;
+yawned, sighed, forgot the beetle entirely, and sat down on it. Then
+there was a wild yelp of agony and the poodle went sailing up the
+aisle; the yelps continued, and so did the dog; he crossed the house in
+front of the altar; he flew down the other aisle; he crossed before the
+doors; he clamored up the home-stretch; his anguish grew with his
+progress, till presently he was but a woolly comet moving in its orbit
+with the gleam and the speed of light. At last the frantic sufferer
+sheered from its course, and sprang into its master's lap; he flung it
+out of the window, and the voice of distress quickly thinned away and
+died in the distance.
+
+By this time the whole church was red-faced and suffocating with
+suppressed laughter, and the sermon had come to a dead standstill. The
+discourse was resumed presently, but it went lame and halting, all
+possibility of impressiveness being at an end; for even the gravest
+sentiments were constantly being received with a smothered burst of
+unholy mirth, under cover of some remote pew-back, as if the poor
+parson had said a rarely facetious thing. It was a genuine relief to
+the whole congregation when the ordeal was over and the benediction
+pronounced.
+
+Tom Sawyer went home quite cheerful, thinking to himself that there
+was some satisfaction about divine service when there was a bit of
+variety in it. He had but one marring thought; he was willing that the
+dog should play with his pinchbug, but he did not think it was upright
+in him to carry it off.
+
+
+
+CHAPTER VI
+
+MONDAY morning found Tom Sawyer miserable. Monday morning always found
+him so--because it began another week's slow suffering in school. He
+generally began that day with wishing he had had no intervening
+holiday, it made the going into captivity and fetters again so much
+more odious.
+
+Tom lay thinking. Presently it occurred to him that he wished he was
+sick; then he could stay home from school. Here was a vague
+possibility. He canvassed his system. No ailment was found, and he
+investigated again. This time he thought he could detect colicky
+symptoms, and he began to encourage them with considerable hope. But
+they soon grew feeble, and presently died wholly away. He reflected
+further. Suddenly he discovered something. One of his upper front teeth
+was loose. This was lucky; he was about to begin to groan, as a
+"starter," as he called it, when it occurred to him that if he came
+into court with that argument, his aunt would pull it out, and that
+would hurt. So he thought he would hold the tooth in reserve for the
+present, and seek further. Nothing offered for some little time, and
+then he remembered hearing the doctor tell about a certain thing that
+laid up a patient for two or three weeks and threatened to make him
+lose a finger. So the boy eagerly drew his sore toe from under the
+sheet and held it up for inspection. But now he did not know the
+necessary symptoms. However, it seemed well worth while to chance it,
+so he fell to groaning with considerable spirit.
+
+But Sid slept on unconscious.
+
+Tom groaned louder, and fancied that he began to feel pain in the toe.
+
+No result from Sid.
+
+Tom was panting with his exertions by this time. He took a rest and
+then swelled himself up and fetched a succession of admirable groans.
+
+Sid snored on.
+
+Tom was aggravated. He said, "Sid, Sid!" and shook him. This course
+worked well, and Tom began to groan again. Sid yawned, stretched, then
+brought himself up on his elbow with a snort, and began to stare at
+Tom. Tom went on groaning. Sid said:
+
+"Tom! Say, Tom!" [No response.] "Here, Tom! TOM! What is the matter,
+Tom?" And he shook him and looked in his face anxiously.
+
+Tom moaned out:
+
+"Oh, don't, Sid. Don't joggle me."
+
+"Why, what's the matter, Tom? I must call auntie."
+
+"No--never mind. It'll be over by and by, maybe. Don't call anybody."
+
+"But I must! DON'T groan so, Tom, it's awful. How long you been this
+way?"
+
+"Hours. Ouch! Oh, don't stir so, Sid, you'll kill me."
+
+"Tom, why didn't you wake me sooner? Oh, Tom, DON'T! It makes my
+flesh crawl to hear you. Tom, what is the matter?"
+
+"I forgive you everything, Sid. [Groan.] Everything you've ever done
+to me. When I'm gone--"
+
+"Oh, Tom, you ain't dying, are you? Don't, Tom--oh, don't. Maybe--"
+
+"I forgive everybody, Sid. [Groan.] Tell 'em so, Sid. And Sid, you
+give my window-sash and my cat with one eye to that new girl that's
+come to town, and tell her--"
+
+But Sid had snatched his clothes and gone. Tom was suffering in
+reality, now, so handsomely was his imagination working, and so his
+groans had gathered quite a genuine tone.
+
+Sid flew down-stairs and said:
+
+"Oh, Aunt Polly, come! Tom's dying!"
+
+"Dying!"
+
+"Yes'm. Don't wait--come quick!"
+
+"Rubbage! I don't believe it!"
+
+But she fled up-stairs, nevertheless, with Sid and Mary at her heels.
+And her face grew white, too, and her lip trembled. When she reached
+the bedside she gasped out:
+
+"You, Tom! Tom, what's the matter with you?"
+
+"Oh, auntie, I'm--"
+
+"What's the matter with you--what is the matter with you, child?"
+
+"Oh, auntie, my sore toe's mortified!"
+
+The old lady sank down into a chair and laughed a little, then cried a
+little, then did both together. This restored her and she said:
+
+"Tom, what a turn you did give me. Now you shut up that nonsense and
+climb out of this."
+
+The groans ceased and the pain vanished from the toe. The boy felt a
+little foolish, and he said:
+
+"Aunt Polly, it SEEMED mortified, and it hurt so I never minded my
+tooth at all."
+
+"Your tooth, indeed! What's the matter with your tooth?"
+
+"One of them's loose, and it aches perfectly awful."
+
+"There, there, now, don't begin that groaning again. Open your mouth.
+Well--your tooth IS loose, but you're not going to die about that.
+Mary, get me a silk thread, and a chunk of fire out of the kitchen."
+
+Tom said:
+
+"Oh, please, auntie, don't pull it out. It don't hurt any more. I wish
+I may never stir if it does. Please don't, auntie. I don't want to stay
+home from school."
+
+"Oh, you don't, don't you? So all this row was because you thought
+you'd get to stay home from school and go a-fishing? Tom, Tom, I love
+you so, and you seem to try every way you can to break my old heart
+with your outrageousness." By this time the dental instruments were
+ready. The old lady made one end of the silk thread fast to Tom's tooth
+with a loop and tied the other to the bedpost. Then she seized the
+chunk of fire and suddenly thrust it almost into the boy's face. The
+tooth hung dangling by the bedpost, now.
+
+But all trials bring their compensations. As Tom wended to school
+after breakfast, he was the envy of every boy he met because the gap in
+his upper row of teeth enabled him to expectorate in a new and
+admirable way. He gathered quite a following of lads interested in the
+exhibition; and one that had cut his finger and had been a centre of
+fascination and homage up to this time, now found himself suddenly
+without an adherent, and shorn of his glory. His heart was heavy, and
+he said with a disdain which he did not feel that it wasn't anything to
+spit like Tom Sawyer; but another boy said, "Sour grapes!" and he
+wandered away a dismantled hero.
+
+Shortly Tom came upon the juvenile pariah of the village, Huckleberry
+Finn, son of the town drunkard. Huckleberry was cordially hated and
+dreaded by all the mothers of the town, because he was idle and lawless
+and vulgar and bad--and because all their children admired him so, and
+delighted in his forbidden society, and wished they dared to be like
+him. Tom was like the rest of the respectable boys, in that he envied
+Huckleberry his gaudy outcast condition, and was under strict orders
+not to play with him. So he played with him every time he got a chance.
+Huckleberry was always dressed in the cast-off clothes of full-grown
+men, and they were in perennial bloom and fluttering with rags. His hat
+was a vast ruin with a wide crescent lopped out of its brim; his coat,
+when he wore one, hung nearly to his heels and had the rearward buttons
+far down the back; but one suspender supported his trousers; the seat
+of the trousers bagged low and contained nothing, the fringed legs
+dragged in the dirt when not rolled up.
+
+Huckleberry came and went, at his own free will. He slept on doorsteps
+in fine weather and in empty hogsheads in wet; he did not have to go to
+school or to church, or call any being master or obey anybody; he could
+go fishing or swimming when and where he chose, and stay as long as it
+suited him; nobody forbade him to fight; he could sit up as late as he
+pleased; he was always the first boy that went barefoot in the spring
+and the last to resume leather in the fall; he never had to wash, nor
+put on clean clothes; he could swear wonderfully. In a word, everything
+that goes to make life precious that boy had. So thought every
+harassed, hampered, respectable boy in St. Petersburg.
+
+Tom hailed the romantic outcast:
+
+"Hello, Huckleberry!"
+
+"Hello yourself, and see how you like it."
+
+"What's that you got?"
+
+"Dead cat."
+
+"Lemme see him, Huck. My, he's pretty stiff. Where'd you get him?"
+
+"Bought him off'n a boy."
+
+"What did you give?"
+
+"I give a blue ticket and a bladder that I got at the slaughter-house."
+
+"Where'd you get the blue ticket?"
+
+"Bought it off'n Ben Rogers two weeks ago for a hoop-stick."
+
+"Say--what is dead cats good for, Huck?"
+
+"Good for? Cure warts with."
+
+"No! Is that so? I know something that's better."
+
+"I bet you don't. What is it?"
+
+"Why, spunk-water."
+
+"Spunk-water! I wouldn't give a dern for spunk-water."
+
+"You wouldn't, wouldn't you? D'you ever try it?"
+
+"No, I hain't. But Bob Tanner did."
+
+"Who told you so!"
+
+"Why, he told Jeff Thatcher, and Jeff told Johnny Baker, and Johnny
+told Jim Hollis, and Jim told Ben Rogers, and Ben told a nigger, and
+the nigger told me. There now!"
+
+"Well, what of it? They'll all lie. Leastways all but the nigger. I
+don't know HIM. But I never see a nigger that WOULDN'T lie. Shucks! Now
+you tell me how Bob Tanner done it, Huck."
+
+"Why, he took and dipped his hand in a rotten stump where the
+rain-water was."
+
+"In the daytime?"
+
+"Certainly."
+
+"With his face to the stump?"
+
+"Yes. Least I reckon so."
+
+"Did he say anything?"
+
+"I don't reckon he did. I don't know."
+
+"Aha! Talk about trying to cure warts with spunk-water such a blame
+fool way as that! Why, that ain't a-going to do any good. You got to go
+all by yourself, to the middle of the woods, where you know there's a
+spunk-water stump, and just as it's midnight you back up against the
+stump and jam your hand in and say:
+
+ 'Barley-corn, barley-corn, injun-meal shorts,
+ Spunk-water, spunk-water, swaller these warts,'
+
+and then walk away quick, eleven steps, with your eyes shut, and then
+turn around three times and walk home without speaking to anybody.
+Because if you speak the charm's busted."
+
+"Well, that sounds like a good way; but that ain't the way Bob Tanner
+done."
+
+"No, sir, you can bet he didn't, becuz he's the wartiest boy in this
+town; and he wouldn't have a wart on him if he'd knowed how to work
+spunk-water. I've took off thousands of warts off of my hands that way,
+Huck. I play with frogs so much that I've always got considerable many
+warts. Sometimes I take 'em off with a bean."
+
+"Yes, bean's good. I've done that."
+
+"Have you? What's your way?"
+
+"You take and split the bean, and cut the wart so as to get some
+blood, and then you put the blood on one piece of the bean and take and
+dig a hole and bury it 'bout midnight at the crossroads in the dark of
+the moon, and then you burn up the rest of the bean. You see that piece
+that's got the blood on it will keep drawing and drawing, trying to
+fetch the other piece to it, and so that helps the blood to draw the
+wart, and pretty soon off she comes."
+
+"Yes, that's it, Huck--that's it; though when you're burying it if you
+say 'Down bean; off wart; come no more to bother me!' it's better.
+That's the way Joe Harper does, and he's been nearly to Coonville and
+most everywheres. But say--how do you cure 'em with dead cats?"
+
+"Why, you take your cat and go and get in the graveyard 'long about
+midnight when somebody that was wicked has been buried; and when it's
+midnight a devil will come, or maybe two or three, but you can't see
+'em, you can only hear something like the wind, or maybe hear 'em talk;
+and when they're taking that feller away, you heave your cat after 'em
+and say, 'Devil follow corpse, cat follow devil, warts follow cat, I'm
+done with ye!' That'll fetch ANY wart."
+
+"Sounds right. D'you ever try it, Huck?"
+
+"No, but old Mother Hopkins told me."
+
+"Well, I reckon it's so, then. Becuz they say she's a witch."
+
+"Say! Why, Tom, I KNOW she is. She witched pap. Pap says so his own
+self. He come along one day, and he see she was a-witching him, so he
+took up a rock, and if she hadn't dodged, he'd a got her. Well, that
+very night he rolled off'n a shed wher' he was a layin drunk, and broke
+his arm."
+
+"Why, that's awful. How did he know she was a-witching him?"
+
+"Lord, pap can tell, easy. Pap says when they keep looking at you
+right stiddy, they're a-witching you. Specially if they mumble. Becuz
+when they mumble they're saying the Lord's Prayer backards."
+
+"Say, Hucky, when you going to try the cat?"
+
+"To-night. I reckon they'll come after old Hoss Williams to-night."
+
+"But they buried him Saturday. Didn't they get him Saturday night?"
+
+"Why, how you talk! How could their charms work till midnight?--and
+THEN it's Sunday. Devils don't slosh around much of a Sunday, I don't
+reckon."
+
+"I never thought of that. That's so. Lemme go with you?"
+
+"Of course--if you ain't afeard."
+
+"Afeard! 'Tain't likely. Will you meow?"
+
+"Yes--and you meow back, if you get a chance. Last time, you kep' me
+a-meowing around till old Hays went to throwing rocks at me and says
+'Dern that cat!' and so I hove a brick through his window--but don't
+you tell."
+
+"I won't. I couldn't meow that night, becuz auntie was watching me,
+but I'll meow this time. Say--what's that?"
+
+"Nothing but a tick."
+
+"Where'd you get him?"
+
+"Out in the woods."
+
+"What'll you take for him?"
+
+"I don't know. I don't want to sell him."
+
+"All right. It's a mighty small tick, anyway."
+
+"Oh, anybody can run a tick down that don't belong to them. I'm
+satisfied with it. It's a good enough tick for me."
+
+"Sho, there's ticks a plenty. I could have a thousand of 'em if I
+wanted to."
+
+"Well, why don't you? Becuz you know mighty well you can't. This is a
+pretty early tick, I reckon. It's the first one I've seen this year."
+
+"Say, Huck--I'll give you my tooth for him."
+
+"Less see it."
+
+Tom got out a bit of paper and carefully unrolled it. Huckleberry
+viewed it wistfully. The temptation was very strong. At last he said:
+
+"Is it genuwyne?"
+
+Tom lifted his lip and showed the vacancy.
+
+"Well, all right," said Huckleberry, "it's a trade."
+
+Tom enclosed the tick in the percussion-cap box that had lately been
+the pinchbug's prison, and the boys separated, each feeling wealthier
+than before.
+
+When Tom reached the little isolated frame schoolhouse, he strode in
+briskly, with the manner of one who had come with all honest speed.
+He hung his hat on a peg and flung himself into his seat with
+business-like alacrity. The master, throned on high in his great
+splint-bottom arm-chair, was dozing, lulled by the drowsy hum of study.
+The interruption roused him.
+
+"Thomas Sawyer!"
+
+Tom knew that when his name was pronounced in full, it meant trouble.
+
+"Sir!"
+
+"Come up here. Now, sir, why are you late again, as usual?"
+
+Tom was about to take refuge in a lie, when he saw two long tails of
+yellow hair hanging down a back that he recognized by the electric
+sympathy of love; and by that form was THE ONLY VACANT PLACE on the
+girls' side of the schoolhouse. He instantly said:
+
+"I STOPPED TO TALK WITH HUCKLEBERRY FINN!"
+
+The master's pulse stood still, and he stared helplessly. The buzz of
+study ceased. The pupils wondered if this foolhardy boy had lost his
+mind. The master said:
+
+"You--you did what?"
+
+"Stopped to talk with Huckleberry Finn."
+
+There was no mistaking the words.
+
+"Thomas Sawyer, this is the most astounding confession I have ever
+listened to. No mere ferule will answer for this offence. Take off your
+jacket."
+
+The master's arm performed until it was tired and the stock of
+switches notably diminished. Then the order followed:
+
+"Now, sir, go and sit with the girls! And let this be a warning to you."
+
+The titter that rippled around the room appeared to abash the boy, but
+in reality that result was caused rather more by his worshipful awe of
+his unknown idol and the dread pleasure that lay in his high good
+fortune. He sat down upon the end of the pine bench and the girl
+hitched herself away from him with a toss of her head. Nudges and winks
+and whispers traversed the room, but Tom sat still, with his arms upon
+the long, low desk before him, and seemed to study his book.
+
+By and by attention ceased from him, and the accustomed school murmur
+rose upon the dull air once more. Presently the boy began to steal
+furtive glances at the girl. She observed it, "made a mouth" at him and
+gave him the back of her head for the space of a minute. When she
+cautiously faced around again, a peach lay before her. She thrust it
+away. Tom gently put it back. She thrust it away again, but with less
+animosity. Tom patiently returned it to its place. Then she let it
+remain. Tom scrawled on his slate, "Please take it--I got more." The
+girl glanced at the words, but made no sign. Now the boy began to draw
+something on the slate, hiding his work with his left hand. For a time
+the girl refused to notice; but her human curiosity presently began to
+manifest itself by hardly perceptible signs. The boy worked on,
+apparently unconscious. The girl made a sort of noncommittal attempt to
+see, but the boy did not betray that he was aware of it. At last she
+gave in and hesitatingly whispered:
+
+"Let me see it."
+
+Tom partly uncovered a dismal caricature of a house with two gable
+ends to it and a corkscrew of smoke issuing from the chimney. Then the
+girl's interest began to fasten itself upon the work and she forgot
+everything else. When it was finished, she gazed a moment, then
+whispered:
+
+"It's nice--make a man."
+
+The artist erected a man in the front yard, that resembled a derrick.
+He could have stepped over the house; but the girl was not
+hypercritical; she was satisfied with the monster, and whispered:
+
+"It's a beautiful man--now make me coming along."
+
+Tom drew an hour-glass with a full moon and straw limbs to it and
+armed the spreading fingers with a portentous fan. The girl said:
+
+"It's ever so nice--I wish I could draw."
+
+"It's easy," whispered Tom, "I'll learn you."
+
+"Oh, will you? When?"
+
+"At noon. Do you go home to dinner?"
+
+"I'll stay if you will."
+
+"Good--that's a whack. What's your name?"
+
+"Becky Thatcher. What's yours? Oh, I know. It's Thomas Sawyer."
+
+"That's the name they lick me by. I'm Tom when I'm good. You call me
+Tom, will you?"
+
+"Yes."
+
+Now Tom began to scrawl something on the slate, hiding the words from
+the girl. But she was not backward this time. She begged to see. Tom
+said:
+
+"Oh, it ain't anything."
+
+"Yes it is."
+
+"No it ain't. You don't want to see."
+
+"Yes I do, indeed I do. Please let me."
+
+"You'll tell."
+
+"No I won't--deed and deed and double deed won't."
+
+"You won't tell anybody at all? Ever, as long as you live?"
+
+"No, I won't ever tell ANYbody. Now let me."
+
+"Oh, YOU don't want to see!"
+
+"Now that you treat me so, I WILL see." And she put her small hand
+upon his and a little scuffle ensued, Tom pretending to resist in
+earnest but letting his hand slip by degrees till these words were
+revealed: "I LOVE YOU."
+
+"Oh, you bad thing!" And she hit his hand a smart rap, but reddened
+and looked pleased, nevertheless.
+
+Just at this juncture the boy felt a slow, fateful grip closing on his
+ear, and a steady lifting impulse. In that wise he was borne across the
+house and deposited in his own seat, under a peppering fire of giggles
+from the whole school. Then the master stood over him during a few
+awful moments, and finally moved away to his throne without saying a
+word. But although Tom's ear tingled, his heart was jubilant.
+
+As the school quieted down Tom made an honest effort to study, but the
+turmoil within him was too great. In turn he took his place in the
+reading class and made a botch of it; then in the geography class and
+turned lakes into mountains, mountains into rivers, and rivers into
+continents, till chaos was come again; then in the spelling class, and
+got "turned down," by a succession of mere baby words, till he brought
+up at the foot and yielded up the pewter medal which he had worn with
+ostentation for months.
+
+
+
+CHAPTER VII
+
+THE harder Tom tried to fasten his mind on his book, the more his
+ideas wandered. So at last, with a sigh and a yawn, he gave it up. It
+seemed to him that the noon recess would never come. The air was
+utterly dead. There was not a breath stirring. It was the sleepiest of
+sleepy days. The drowsing murmur of the five and twenty studying
+scholars soothed the soul like the spell that is in the murmur of bees.
+Away off in the flaming sunshine, Cardiff Hill lifted its soft green
+sides through a shimmering veil of heat, tinted with the purple of
+distance; a few birds floated on lazy wing high in the air; no other
+living thing was visible but some cows, and they were asleep. Tom's
+heart ached to be free, or else to have something of interest to do to
+pass the dreary time. His hand wandered into his pocket and his face
+lit up with a glow of gratitude that was prayer, though he did not know
+it. Then furtively the percussion-cap box came out. He released the
+tick and put him on the long flat desk. The creature probably glowed
+with a gratitude that amounted to prayer, too, at this moment, but it
+was premature: for when he started thankfully to travel off, Tom turned
+him aside with a pin and made him take a new direction.
+
+Tom's bosom friend sat next him, suffering just as Tom had been, and
+now he was deeply and gratefully interested in this entertainment in an
+instant. This bosom friend was Joe Harper. The two boys were sworn
+friends all the week, and embattled enemies on Saturdays. Joe took a
+pin out of his lapel and began to assist in exercising the prisoner.
+The sport grew in interest momently. Soon Tom said that they were
+interfering with each other, and neither getting the fullest benefit of
+the tick. So he put Joe's slate on the desk and drew a line down the
+middle of it from top to bottom.
+
+"Now," said he, "as long as he is on your side you can stir him up and
+I'll let him alone; but if you let him get away and get on my side,
+you're to leave him alone as long as I can keep him from crossing over."
+
+"All right, go ahead; start him up."
+
+The tick escaped from Tom, presently, and crossed the equator. Joe
+harassed him awhile, and then he got away and crossed back again. This
+change of base occurred often. While one boy was worrying the tick with
+absorbing interest, the other would look on with interest as strong,
+the two heads bowed together over the slate, and the two souls dead to
+all things else. At last luck seemed to settle and abide with Joe. The
+tick tried this, that, and the other course, and got as excited and as
+anxious as the boys themselves, but time and again just as he would
+have victory in his very grasp, so to speak, and Tom's fingers would be
+twitching to begin, Joe's pin would deftly head him off, and keep
+possession. At last Tom could stand it no longer. The temptation was
+too strong. So he reached out and lent a hand with his pin. Joe was
+angry in a moment. Said he:
+
+"Tom, you let him alone."
+
+"I only just want to stir him up a little, Joe."
+
+"No, sir, it ain't fair; you just let him alone."
+
+"Blame it, I ain't going to stir him much."
+
+"Let him alone, I tell you."
+
+"I won't!"
+
+"You shall--he's on my side of the line."
+
+"Look here, Joe Harper, whose is that tick?"
+
+"I don't care whose tick he is--he's on my side of the line, and you
+sha'n't touch him."
+
+"Well, I'll just bet I will, though. He's my tick and I'll do what I
+blame please with him, or die!"
+
+A tremendous whack came down on Tom's shoulders, and its duplicate on
+Joe's; and for the space of two minutes the dust continued to fly from
+the two jackets and the whole school to enjoy it. The boys had been too
+absorbed to notice the hush that had stolen upon the school awhile
+before when the master came tiptoeing down the room and stood over
+them. He had contemplated a good part of the performance before he
+contributed his bit of variety to it.
+
+When school broke up at noon, Tom flew to Becky Thatcher, and
+whispered in her ear:
+
+"Put on your bonnet and let on you're going home; and when you get to
+the corner, give the rest of 'em the slip, and turn down through the
+lane and come back. I'll go the other way and come it over 'em the same
+way."
+
+So the one went off with one group of scholars, and the other with
+another. In a little while the two met at the bottom of the lane, and
+when they reached the school they had it all to themselves. Then they
+sat together, with a slate before them, and Tom gave Becky the pencil
+and held her hand in his, guiding it, and so created another surprising
+house. When the interest in art began to wane, the two fell to talking.
+Tom was swimming in bliss. He said:
+
+"Do you love rats?"
+
+"No! I hate them!"
+
+"Well, I do, too--LIVE ones. But I mean dead ones, to swing round your
+head with a string."
+
+"No, I don't care for rats much, anyway. What I like is chewing-gum."
+
+"Oh, I should say so! I wish I had some now."
+
+"Do you? I've got some. I'll let you chew it awhile, but you must give
+it back to me."
+
+That was agreeable, so they chewed it turn about, and dangled their
+legs against the bench in excess of contentment.
+
+"Was you ever at a circus?" said Tom.
+
+"Yes, and my pa's going to take me again some time, if I'm good."
+
+"I been to the circus three or four times--lots of times. Church ain't
+shucks to a circus. There's things going on at a circus all the time.
+I'm going to be a clown in a circus when I grow up."
+
+"Oh, are you! That will be nice. They're so lovely, all spotted up."
+
+"Yes, that's so. And they get slathers of money--most a dollar a day,
+Ben Rogers says. Say, Becky, was you ever engaged?"
+
+"What's that?"
+
+"Why, engaged to be married."
+
+"No."
+
+"Would you like to?"
+
+"I reckon so. I don't know. What is it like?"
+
+"Like? Why it ain't like anything. You only just tell a boy you won't
+ever have anybody but him, ever ever ever, and then you kiss and that's
+all. Anybody can do it."
+
+"Kiss? What do you kiss for?"
+
+"Why, that, you know, is to--well, they always do that."
+
+"Everybody?"
+
+"Why, yes, everybody that's in love with each other. Do you remember
+what I wrote on the slate?"
+
+"Ye--yes."
+
+"What was it?"
+
+"I sha'n't tell you."
+
+"Shall I tell YOU?"
+
+"Ye--yes--but some other time."
+
+"No, now."
+
+"No, not now--to-morrow."
+
+"Oh, no, NOW. Please, Becky--I'll whisper it, I'll whisper it ever so
+easy."
+
+Becky hesitating, Tom took silence for consent, and passed his arm
+about her waist and whispered the tale ever so softly, with his mouth
+close to her ear. And then he added:
+
+"Now you whisper it to me--just the same."
+
+She resisted, for a while, and then said:
+
+"You turn your face away so you can't see, and then I will. But you
+mustn't ever tell anybody--WILL you, Tom? Now you won't, WILL you?"
+
+"No, indeed, indeed I won't. Now, Becky."
+
+He turned his face away. She bent timidly around till her breath
+stirred his curls and whispered, "I--love--you!"
+
+Then she sprang away and ran around and around the desks and benches,
+with Tom after her, and took refuge in a corner at last, with her
+little white apron to her face. Tom clasped her about her neck and
+pleaded:
+
+"Now, Becky, it's all done--all over but the kiss. Don't you be afraid
+of that--it ain't anything at all. Please, Becky." And he tugged at her
+apron and the hands.
+
+By and by she gave up, and let her hands drop; her face, all glowing
+with the struggle, came up and submitted. Tom kissed the red lips and
+said:
+
+"Now it's all done, Becky. And always after this, you know, you ain't
+ever to love anybody but me, and you ain't ever to marry anybody but
+me, ever never and forever. Will you?"
+
+"No, I'll never love anybody but you, Tom, and I'll never marry
+anybody but you--and you ain't to ever marry anybody but me, either."
+
+"Certainly. Of course. That's PART of it. And always coming to school
+or when we're going home, you're to walk with me, when there ain't
+anybody looking--and you choose me and I choose you at parties, because
+that's the way you do when you're engaged."
+
+"It's so nice. I never heard of it before."
+
+"Oh, it's ever so gay! Why, me and Amy Lawrence--"
+
+The big eyes told Tom his blunder and he stopped, confused.
+
+"Oh, Tom! Then I ain't the first you've ever been engaged to!"
+
+The child began to cry. Tom said:
+
+"Oh, don't cry, Becky, I don't care for her any more."
+
+"Yes, you do, Tom--you know you do."
+
+Tom tried to put his arm about her neck, but she pushed him away and
+turned her face to the wall, and went on crying. Tom tried again, with
+soothing words in his mouth, and was repulsed again. Then his pride was
+up, and he strode away and went outside. He stood about, restless and
+uneasy, for a while, glancing at the door, every now and then, hoping
+she would repent and come to find him. But she did not. Then he began
+to feel badly and fear that he was in the wrong. It was a hard struggle
+with him to make new advances, now, but he nerved himself to it and
+entered. She was still standing back there in the corner, sobbing, with
+her face to the wall. Tom's heart smote him. He went to her and stood a
+moment, not knowing exactly how to proceed. Then he said hesitatingly:
+
+"Becky, I--I don't care for anybody but you."
+
+No reply--but sobs.
+
+"Becky"--pleadingly. "Becky, won't you say something?"
+
+More sobs.
+
+Tom got out his chiefest jewel, a brass knob from the top of an
+andiron, and passed it around her so that she could see it, and said:
+
+"Please, Becky, won't you take it?"
+
+She struck it to the floor. Then Tom marched out of the house and over
+the hills and far away, to return to school no more that day. Presently
+Becky began to suspect. She ran to the door; he was not in sight; she
+flew around to the play-yard; he was not there. Then she called:
+
+"Tom! Come back, Tom!"
+
+She listened intently, but there was no answer. She had no companions
+but silence and loneliness. So she sat down to cry again and upbraid
+herself; and by this time the scholars began to gather again, and she
+had to hide her griefs and still her broken heart and take up the cross
+of a long, dreary, aching afternoon, with none among the strangers
+about her to exchange sorrows with.
+
+
+
+CHAPTER VIII
+
+TOM dodged hither and thither through lanes until he was well out of
+the track of returning scholars, and then fell into a moody jog. He
+crossed a small "branch" two or three times, because of a prevailing
+juvenile superstition that to cross water baffled pursuit. Half an hour
+later he was disappearing behind the Douglas mansion on the summit of
+Cardiff Hill, and the schoolhouse was hardly distinguishable away off
+in the valley behind him. He entered a dense wood, picked his pathless
+way to the centre of it, and sat down on a mossy spot under a spreading
+oak. There was not even a zephyr stirring; the dead noonday heat had
+even stilled the songs of the birds; nature lay in a trance that was
+broken by no sound but the occasional far-off hammering of a
+woodpecker, and this seemed to render the pervading silence and sense
+of loneliness the more profound. The boy's soul was steeped in
+melancholy; his feelings were in happy accord with his surroundings. He
+sat long with his elbows on his knees and his chin in his hands,
+meditating. It seemed to him that life was but a trouble, at best, and
+he more than half envied Jimmy Hodges, so lately released; it must be
+very peaceful, he thought, to lie and slumber and dream forever and
+ever, with the wind whispering through the trees and caressing the
+grass and the flowers over the grave, and nothing to bother and grieve
+about, ever any more. If he only had a clean Sunday-school record he
+could be willing to go, and be done with it all. Now as to this girl.
+What had he done? Nothing. He had meant the best in the world, and been
+treated like a dog--like a very dog. She would be sorry some day--maybe
+when it was too late. Ah, if he could only die TEMPORARILY!
+
+But the elastic heart of youth cannot be compressed into one
+constrained shape long at a time. Tom presently began to drift
+insensibly back into the concerns of this life again. What if he turned
+his back, now, and disappeared mysteriously? What if he went away--ever
+so far away, into unknown countries beyond the seas--and never came
+back any more! How would she feel then! The idea of being a clown
+recurred to him now, only to fill him with disgust. For frivolity and
+jokes and spotted tights were an offense, when they intruded themselves
+upon a spirit that was exalted into the vague august realm of the
+romantic. No, he would be a soldier, and return after long years, all
+war-worn and illustrious. No--better still, he would join the Indians,
+and hunt buffaloes and go on the warpath in the mountain ranges and the
+trackless great plains of the Far West, and away in the future come
+back a great chief, bristling with feathers, hideous with paint, and
+prance into Sunday-school, some drowsy summer morning, with a
+bloodcurdling war-whoop, and sear the eyeballs of all his companions
+with unappeasable envy. But no, there was something gaudier even than
+this. He would be a pirate! That was it! NOW his future lay plain
+before him, and glowing with unimaginable splendor. How his name would
+fill the world, and make people shudder! How gloriously he would go
+plowing the dancing seas, in his long, low, black-hulled racer, the
+Spirit of the Storm, with his grisly flag flying at the fore! And at
+the zenith of his fame, how he would suddenly appear at the old village
+and stalk into church, brown and weather-beaten, in his black velvet
+doublet and trunks, his great jack-boots, his crimson sash, his belt
+bristling with horse-pistols, his crime-rusted cutlass at his side, his
+slouch hat with waving plumes, his black flag unfurled, with the skull
+and crossbones on it, and hear with swelling ecstasy the whisperings,
+"It's Tom Sawyer the Pirate!--the Black Avenger of the Spanish Main!"
+
+Yes, it was settled; his career was determined. He would run away from
+home and enter upon it. He would start the very next morning. Therefore
+he must now begin to get ready. He would collect his resources
+together. He went to a rotten log near at hand and began to dig under
+one end of it with his Barlow knife. He soon struck wood that sounded
+hollow. He put his hand there and uttered this incantation impressively:
+
+"What hasn't come here, come! What's here, stay here!"
+
+Then he scraped away the dirt, and exposed a pine shingle. He took it
+up and disclosed a shapely little treasure-house whose bottom and sides
+were of shingles. In it lay a marble. Tom's astonishment was boundless!
+He scratched his head with a perplexed air, and said:
+
+"Well, that beats anything!"
+
+Then he tossed the marble away pettishly, and stood cogitating. The
+truth was, that a superstition of his had failed, here, which he and
+all his comrades had always looked upon as infallible. If you buried a
+marble with certain necessary incantations, and left it alone a
+fortnight, and then opened the place with the incantation he had just
+used, you would find that all the marbles you had ever lost had
+gathered themselves together there, meantime, no matter how widely they
+had been separated. But now, this thing had actually and unquestionably
+failed. Tom's whole structure of faith was shaken to its foundations.
+He had many a time heard of this thing succeeding but never of its
+failing before. It did not occur to him that he had tried it several
+times before, himself, but could never find the hiding-places
+afterward. He puzzled over the matter some time, and finally decided
+that some witch had interfered and broken the charm. He thought he
+would satisfy himself on that point; so he searched around till he
+found a small sandy spot with a little funnel-shaped depression in it.
+He laid himself down and put his mouth close to this depression and
+called--
+
+"Doodle-bug, doodle-bug, tell me what I want to know! Doodle-bug,
+doodle-bug, tell me what I want to know!"
+
+The sand began to work, and presently a small black bug appeared for a
+second and then darted under again in a fright.
+
+"He dasn't tell! So it WAS a witch that done it. I just knowed it."
+
+He well knew the futility of trying to contend against witches, so he
+gave up discouraged. But it occurred to him that he might as well have
+the marble he had just thrown away, and therefore he went and made a
+patient search for it. But he could not find it. Now he went back to
+his treasure-house and carefully placed himself just as he had been
+standing when he tossed the marble away; then he took another marble
+from his pocket and tossed it in the same way, saying:
+
+"Brother, go find your brother!"
+
+He watched where it stopped, and went there and looked. But it must
+have fallen short or gone too far; so he tried twice more. The last
+repetition was successful. The two marbles lay within a foot of each
+other.
+
+Just here the blast of a toy tin trumpet came faintly down the green
+aisles of the forest. Tom flung off his jacket and trousers, turned a
+suspender into a belt, raked away some brush behind the rotten log,
+disclosing a rude bow and arrow, a lath sword and a tin trumpet, and in
+a moment had seized these things and bounded away, barelegged, with
+fluttering shirt. He presently halted under a great elm, blew an
+answering blast, and then began to tiptoe and look warily out, this way
+and that. He said cautiously--to an imaginary company:
+
+"Hold, my merry men! Keep hid till I blow."
+
+Now appeared Joe Harper, as airily clad and elaborately armed as Tom.
+Tom called:
+
+"Hold! Who comes here into Sherwood Forest without my pass?"
+
+"Guy of Guisborne wants no man's pass. Who art thou that--that--"
+
+"Dares to hold such language," said Tom, prompting--for they talked
+"by the book," from memory.
+
+"Who art thou that dares to hold such language?"
+
+"I, indeed! I am Robin Hood, as thy caitiff carcase soon shall know."
+
+"Then art thou indeed that famous outlaw? Right gladly will I dispute
+with thee the passes of the merry wood. Have at thee!"
+
+They took their lath swords, dumped their other traps on the ground,
+struck a fencing attitude, foot to foot, and began a grave, careful
+combat, "two up and two down." Presently Tom said:
+
+"Now, if you've got the hang, go it lively!"
+
+So they "went it lively," panting and perspiring with the work. By and
+by Tom shouted:
+
+"Fall! fall! Why don't you fall?"
+
+"I sha'n't! Why don't you fall yourself? You're getting the worst of
+it."
+
+"Why, that ain't anything. I can't fall; that ain't the way it is in
+the book. The book says, 'Then with one back-handed stroke he slew poor
+Guy of Guisborne.' You're to turn around and let me hit you in the
+back."
+
+There was no getting around the authorities, so Joe turned, received
+the whack and fell.
+
+"Now," said Joe, getting up, "you got to let me kill YOU. That's fair."
+
+"Why, I can't do that, it ain't in the book."
+
+"Well, it's blamed mean--that's all."
+
+"Well, say, Joe, you can be Friar Tuck or Much the miller's son, and
+lam me with a quarter-staff; or I'll be the Sheriff of Nottingham and
+you be Robin Hood a little while and kill me."
+
+This was satisfactory, and so these adventures were carried out. Then
+Tom became Robin Hood again, and was allowed by the treacherous nun to
+bleed his strength away through his neglected wound. And at last Joe,
+representing a whole tribe of weeping outlaws, dragged him sadly forth,
+gave his bow into his feeble hands, and Tom said, "Where this arrow
+falls, there bury poor Robin Hood under the greenwood tree." Then he
+shot the arrow and fell back and would have died, but he lit on a
+nettle and sprang up too gaily for a corpse.
+
+The boys dressed themselves, hid their accoutrements, and went off
+grieving that there were no outlaws any more, and wondering what modern
+civilization could claim to have done to compensate for their loss.
+They said they would rather be outlaws a year in Sherwood Forest than
+President of the United States forever.
+
+
+
+CHAPTER IX
+
+AT half-past nine, that night, Tom and Sid were sent to bed, as usual.
+They said their prayers, and Sid was soon asleep. Tom lay awake and
+waited, in restless impatience. When it seemed to him that it must be
+nearly daylight, he heard the clock strike ten! This was despair. He
+would have tossed and fidgeted, as his nerves demanded, but he was
+afraid he might wake Sid. So he lay still, and stared up into the dark.
+Everything was dismally still. By and by, out of the stillness, little,
+scarcely perceptible noises began to emphasize themselves. The ticking
+of the clock began to bring itself into notice. Old beams began to
+crack mysteriously. The stairs creaked faintly. Evidently spirits were
+abroad. A measured, muffled snore issued from Aunt Polly's chamber. And
+now the tiresome chirping of a cricket that no human ingenuity could
+locate, began. Next the ghastly ticking of a deathwatch in the wall at
+the bed's head made Tom shudder--it meant that somebody's days were
+numbered. Then the howl of a far-off dog rose on the night air, and was
+answered by a fainter howl from a remoter distance. Tom was in an
+agony. At last he was satisfied that time had ceased and eternity
+begun; he began to doze, in spite of himself; the clock chimed eleven,
+but he did not hear it. And then there came, mingling with his
+half-formed dreams, a most melancholy caterwauling. The raising of a
+neighboring window disturbed him. A cry of "Scat! you devil!" and the
+crash of an empty bottle against the back of his aunt's woodshed
+brought him wide awake, and a single minute later he was dressed and
+out of the window and creeping along the roof of the "ell" on all
+fours. He "meow'd" with caution once or twice, as he went; then jumped
+to the roof of the woodshed and thence to the ground. Huckleberry Finn
+was there, with his dead cat. The boys moved off and disappeared in the
+gloom. At the end of half an hour they were wading through the tall
+grass of the graveyard.
+
+It was a graveyard of the old-fashioned Western kind. It was on a
+hill, about a mile and a half from the village. It had a crazy board
+fence around it, which leaned inward in places, and outward the rest of
+the time, but stood upright nowhere. Grass and weeds grew rank over the
+whole cemetery. All the old graves were sunken in, there was not a
+tombstone on the place; round-topped, worm-eaten boards staggered over
+the graves, leaning for support and finding none. "Sacred to the memory
+of" So-and-So had been painted on them once, but it could no longer
+have been read, on the most of them, now, even if there had been light.
+
+A faint wind moaned through the trees, and Tom feared it might be the
+spirits of the dead, complaining at being disturbed. The boys talked
+little, and only under their breath, for the time and the place and the
+pervading solemnity and silence oppressed their spirits. They found the
+sharp new heap they were seeking, and ensconced themselves within the
+protection of three great elms that grew in a bunch within a few feet
+of the grave.
+
+Then they waited in silence for what seemed a long time. The hooting
+of a distant owl was all the sound that troubled the dead stillness.
+Tom's reflections grew oppressive. He must force some talk. So he said
+in a whisper:
+
+"Hucky, do you believe the dead people like it for us to be here?"
+
+Huckleberry whispered:
+
+"I wisht I knowed. It's awful solemn like, AIN'T it?"
+
+"I bet it is."
+
+There was a considerable pause, while the boys canvassed this matter
+inwardly. Then Tom whispered:
+
+"Say, Hucky--do you reckon Hoss Williams hears us talking?"
+
+"O' course he does. Least his sperrit does."
+
+Tom, after a pause:
+
+"I wish I'd said Mister Williams. But I never meant any harm.
+Everybody calls him Hoss."
+
+"A body can't be too partic'lar how they talk 'bout these-yer dead
+people, Tom."
+
+This was a damper, and conversation died again.
+
+Presently Tom seized his comrade's arm and said:
+
+"Sh!"
+
+"What is it, Tom?" And the two clung together with beating hearts.
+
+"Sh! There 'tis again! Didn't you hear it?"
+
+"I--"
+
+"There! Now you hear it."
+
+"Lord, Tom, they're coming! They're coming, sure. What'll we do?"
+
+"I dono. Think they'll see us?"
+
+"Oh, Tom, they can see in the dark, same as cats. I wisht I hadn't
+come."
+
+"Oh, don't be afeard. I don't believe they'll bother us. We ain't
+doing any harm. If we keep perfectly still, maybe they won't notice us
+at all."
+
+"I'll try to, Tom, but, Lord, I'm all of a shiver."
+
+"Listen!"
+
+The boys bent their heads together and scarcely breathed. A muffled
+sound of voices floated up from the far end of the graveyard.
+
+"Look! See there!" whispered Tom. "What is it?"
+
+"It's devil-fire. Oh, Tom, this is awful."
+
+Some vague figures approached through the gloom, swinging an
+old-fashioned tin lantern that freckled the ground with innumerable
+little spangles of light. Presently Huckleberry whispered with a
+shudder:
+
+"It's the devils sure enough. Three of 'em! Lordy, Tom, we're goners!
+Can you pray?"
+
+"I'll try, but don't you be afeard. They ain't going to hurt us. 'Now
+I lay me down to sleep, I--'"
+
+"Sh!"
+
+"What is it, Huck?"
+
+"They're HUMANS! One of 'em is, anyway. One of 'em's old Muff Potter's
+voice."
+
+"No--'tain't so, is it?"
+
+"I bet I know it. Don't you stir nor budge. He ain't sharp enough to
+notice us. Drunk, the same as usual, likely--blamed old rip!"
+
+"All right, I'll keep still. Now they're stuck. Can't find it. Here
+they come again. Now they're hot. Cold again. Hot again. Red hot!
+They're p'inted right, this time. Say, Huck, I know another o' them
+voices; it's Injun Joe."
+
+"That's so--that murderin' half-breed! I'd druther they was devils a
+dern sight. What kin they be up to?"
+
+The whisper died wholly out, now, for the three men had reached the
+grave and stood within a few feet of the boys' hiding-place.
+
+"Here it is," said the third voice; and the owner of it held the
+lantern up and revealed the face of young Doctor Robinson.
+
+Potter and Injun Joe were carrying a handbarrow with a rope and a
+couple of shovels on it. They cast down their load and began to open
+the grave. The doctor put the lantern at the head of the grave and came
+and sat down with his back against one of the elm trees. He was so
+close the boys could have touched him.
+
+"Hurry, men!" he said, in a low voice; "the moon might come out at any
+moment."
+
+They growled a response and went on digging. For some time there was
+no noise but the grating sound of the spades discharging their freight
+of mould and gravel. It was very monotonous. Finally a spade struck
+upon the coffin with a dull woody accent, and within another minute or
+two the men had hoisted it out on the ground. They pried off the lid
+with their shovels, got out the body and dumped it rudely on the
+ground. The moon drifted from behind the clouds and exposed the pallid
+face. The barrow was got ready and the corpse placed on it, covered
+with a blanket, and bound to its place with the rope. Potter took out a
+large spring-knife and cut off the dangling end of the rope and then
+said:
+
+"Now the cussed thing's ready, Sawbones, and you'll just out with
+another five, or here she stays."
+
+"That's the talk!" said Injun Joe.
+
+"Look here, what does this mean?" said the doctor. "You required your
+pay in advance, and I've paid you."
+
+"Yes, and you done more than that," said Injun Joe, approaching the
+doctor, who was now standing. "Five years ago you drove me away from
+your father's kitchen one night, when I come to ask for something to
+eat, and you said I warn't there for any good; and when I swore I'd get
+even with you if it took a hundred years, your father had me jailed for
+a vagrant. Did you think I'd forget? The Injun blood ain't in me for
+nothing. And now I've GOT you, and you got to SETTLE, you know!"
+
+He was threatening the doctor, with his fist in his face, by this
+time. The doctor struck out suddenly and stretched the ruffian on the
+ground. Potter dropped his knife, and exclaimed:
+
+"Here, now, don't you hit my pard!" and the next moment he had
+grappled with the doctor and the two were struggling with might and
+main, trampling the grass and tearing the ground with their heels.
+Injun Joe sprang to his feet, his eyes flaming with passion, snatched
+up Potter's knife, and went creeping, catlike and stooping, round and
+round about the combatants, seeking an opportunity. All at once the
+doctor flung himself free, seized the heavy headboard of Williams'
+grave and felled Potter to the earth with it--and in the same instant
+the half-breed saw his chance and drove the knife to the hilt in the
+young man's breast. He reeled and fell partly upon Potter, flooding him
+with his blood, and in the same moment the clouds blotted out the
+dreadful spectacle and the two frightened boys went speeding away in
+the dark.
+
+Presently, when the moon emerged again, Injun Joe was standing over
+the two forms, contemplating them. The doctor murmured inarticulately,
+gave a long gasp or two and was still. The half-breed muttered:
+
+"THAT score is settled--damn you."
+
+Then he robbed the body. After which he put the fatal knife in
+Potter's open right hand, and sat down on the dismantled coffin. Three
+--four--five minutes passed, and then Potter began to stir and moan. His
+hand closed upon the knife; he raised it, glanced at it, and let it
+fall, with a shudder. Then he sat up, pushing the body from him, and
+gazed at it, and then around him, confusedly. His eyes met Joe's.
+
+"Lord, how is this, Joe?" he said.
+
+"It's a dirty business," said Joe, without moving.
+
+"What did you do it for?"
+
+"I! I never done it!"
+
+"Look here! That kind of talk won't wash."
+
+Potter trembled and grew white.
+
+"I thought I'd got sober. I'd no business to drink to-night. But it's
+in my head yet--worse'n when we started here. I'm all in a muddle;
+can't recollect anything of it, hardly. Tell me, Joe--HONEST, now, old
+feller--did I do it? Joe, I never meant to--'pon my soul and honor, I
+never meant to, Joe. Tell me how it was, Joe. Oh, it's awful--and him
+so young and promising."
+
+"Why, you two was scuffling, and he fetched you one with the headboard
+and you fell flat; and then up you come, all reeling and staggering
+like, and snatched the knife and jammed it into him, just as he fetched
+you another awful clip--and here you've laid, as dead as a wedge til
+now."
+
+"Oh, I didn't know what I was a-doing. I wish I may die this minute if
+I did. It was all on account of the whiskey and the excitement, I
+reckon. I never used a weepon in my life before, Joe. I've fought, but
+never with weepons. They'll all say that. Joe, don't tell! Say you
+won't tell, Joe--that's a good feller. I always liked you, Joe, and
+stood up for you, too. Don't you remember? You WON'T tell, WILL you,
+Joe?" And the poor creature dropped on his knees before the stolid
+murderer, and clasped his appealing hands.
+
+"No, you've always been fair and square with me, Muff Potter, and I
+won't go back on you. There, now, that's as fair as a man can say."
+
+"Oh, Joe, you're an angel. I'll bless you for this the longest day I
+live." And Potter began to cry.
+
+"Come, now, that's enough of that. This ain't any time for blubbering.
+You be off yonder way and I'll go this. Move, now, and don't leave any
+tracks behind you."
+
+Potter started on a trot that quickly increased to a run. The
+half-breed stood looking after him. He muttered:
+
+"If he's as much stunned with the lick and fuddled with the rum as he
+had the look of being, he won't think of the knife till he's gone so
+far he'll be afraid to come back after it to such a place by himself
+--chicken-heart!"
+
+Two or three minutes later the murdered man, the blanketed corpse, the
+lidless coffin, and the open grave were under no inspection but the
+moon's. The stillness was complete again, too.
+
+
+
+CHAPTER X
+
+THE two boys flew on and on, toward the village, speechless with
+horror. They glanced backward over their shoulders from time to time,
+apprehensively, as if they feared they might be followed. Every stump
+that started up in their path seemed a man and an enemy, and made them
+catch their breath; and as they sped by some outlying cottages that lay
+near the village, the barking of the aroused watch-dogs seemed to give
+wings to their feet.
+
+"If we can only get to the old tannery before we break down!"
+whispered Tom, in short catches between breaths. "I can't stand it much
+longer."
+
+Huckleberry's hard pantings were his only reply, and the boys fixed
+their eyes on the goal of their hopes and bent to their work to win it.
+They gained steadily on it, and at last, breast to breast, they burst
+through the open door and fell grateful and exhausted in the sheltering
+shadows beyond. By and by their pulses slowed down, and Tom whispered:
+
+"Huckleberry, what do you reckon'll come of this?"
+
+"If Doctor Robinson dies, I reckon hanging'll come of it."
+
+"Do you though?"
+
+"Why, I KNOW it, Tom."
+
+Tom thought a while, then he said:
+
+"Who'll tell? We?"
+
+"What are you talking about? S'pose something happened and Injun Joe
+DIDN'T hang? Why, he'd kill us some time or other, just as dead sure as
+we're a laying here."
+
+"That's just what I was thinking to myself, Huck."
+
+"If anybody tells, let Muff Potter do it, if he's fool enough. He's
+generally drunk enough."
+
+Tom said nothing--went on thinking. Presently he whispered:
+
+"Huck, Muff Potter don't know it. How can he tell?"
+
+"What's the reason he don't know it?"
+
+"Because he'd just got that whack when Injun Joe done it. D'you reckon
+he could see anything? D'you reckon he knowed anything?"
+
+"By hokey, that's so, Tom!"
+
+"And besides, look-a-here--maybe that whack done for HIM!"
+
+"No, 'taint likely, Tom. He had liquor in him; I could see that; and
+besides, he always has. Well, when pap's full, you might take and belt
+him over the head with a church and you couldn't phase him. He says so,
+his own self. So it's the same with Muff Potter, of course. But if a
+man was dead sober, I reckon maybe that whack might fetch him; I dono."
+
+After another reflective silence, Tom said:
+
+"Hucky, you sure you can keep mum?"
+
+"Tom, we GOT to keep mum. You know that. That Injun devil wouldn't
+make any more of drownding us than a couple of cats, if we was to
+squeak 'bout this and they didn't hang him. Now, look-a-here, Tom, less
+take and swear to one another--that's what we got to do--swear to keep
+mum."
+
+"I'm agreed. It's the best thing. Would you just hold hands and swear
+that we--"
+
+"Oh no, that wouldn't do for this. That's good enough for little
+rubbishy common things--specially with gals, cuz THEY go back on you
+anyway, and blab if they get in a huff--but there orter be writing
+'bout a big thing like this. And blood."
+
+Tom's whole being applauded this idea. It was deep, and dark, and
+awful; the hour, the circumstances, the surroundings, were in keeping
+with it. He picked up a clean pine shingle that lay in the moonlight,
+took a little fragment of "red keel" out of his pocket, got the moon on
+his work, and painfully scrawled these lines, emphasizing each slow
+down-stroke by clamping his tongue between his teeth, and letting up
+the pressure on the up-strokes. [See next page.]
+
+ "Huck Finn and
+ Tom Sawyer swears
+ they will keep mum
+ about This and They
+ wish They may Drop
+ down dead in Their
+ Tracks if They ever
+ Tell and Rot."
+
+Huckleberry was filled with admiration of Tom's facility in writing,
+and the sublimity of his language. He at once took a pin from his lapel
+and was going to prick his flesh, but Tom said:
+
+"Hold on! Don't do that. A pin's brass. It might have verdigrease on
+it."
+
+"What's verdigrease?"
+
+"It's p'ison. That's what it is. You just swaller some of it once
+--you'll see."
+
+So Tom unwound the thread from one of his needles, and each boy
+pricked the ball of his thumb and squeezed out a drop of blood. In
+time, after many squeezes, Tom managed to sign his initials, using the
+ball of his little finger for a pen. Then he showed Huckleberry how to
+make an H and an F, and the oath was complete. They buried the shingle
+close to the wall, with some dismal ceremonies and incantations, and
+the fetters that bound their tongues were considered to be locked and
+the key thrown away.
+
+A figure crept stealthily through a break in the other end of the
+ruined building, now, but they did not notice it.
+
+"Tom," whispered Huckleberry, "does this keep us from EVER telling
+--ALWAYS?"
+
+"Of course it does. It don't make any difference WHAT happens, we got
+to keep mum. We'd drop down dead--don't YOU know that?"
+
+"Yes, I reckon that's so."
+
+They continued to whisper for some little time. Presently a dog set up
+a long, lugubrious howl just outside--within ten feet of them. The boys
+clasped each other suddenly, in an agony of fright.
+
+"Which of us does he mean?" gasped Huckleberry.
+
+"I dono--peep through the crack. Quick!"
+
+"No, YOU, Tom!"
+
+"I can't--I can't DO it, Huck!"
+
+"Please, Tom. There 'tis again!"
+
+"Oh, lordy, I'm thankful!" whispered Tom. "I know his voice. It's Bull
+Harbison." *
+
+[* If Mr. Harbison owned a slave named Bull, Tom would have spoken of
+him as "Harbison's Bull," but a son or a dog of that name was "Bull
+Harbison."]
+
+"Oh, that's good--I tell you, Tom, I was most scared to death; I'd a
+bet anything it was a STRAY dog."
+
+The dog howled again. The boys' hearts sank once more.
+
+"Oh, my! that ain't no Bull Harbison!" whispered Huckleberry. "DO, Tom!"
+
+Tom, quaking with fear, yielded, and put his eye to the crack. His
+whisper was hardly audible when he said:
+
+"Oh, Huck, IT S A STRAY DOG!"
+
+"Quick, Tom, quick! Who does he mean?"
+
+"Huck, he must mean us both--we're right together."
+
+"Oh, Tom, I reckon we're goners. I reckon there ain't no mistake 'bout
+where I'LL go to. I been so wicked."
+
+"Dad fetch it! This comes of playing hookey and doing everything a
+feller's told NOT to do. I might a been good, like Sid, if I'd a tried
+--but no, I wouldn't, of course. But if ever I get off this time, I lay
+I'll just WALLER in Sunday-schools!" And Tom began to snuffle a little.
+
+"YOU bad!" and Huckleberry began to snuffle too. "Consound it, Tom
+Sawyer, you're just old pie, 'longside o' what I am. Oh, LORDY, lordy,
+lordy, I wisht I only had half your chance."
+
+Tom choked off and whispered:
+
+"Look, Hucky, look! He's got his BACK to us!"
+
+Hucky looked, with joy in his heart.
+
+"Well, he has, by jingoes! Did he before?"
+
+"Yes, he did. But I, like a fool, never thought. Oh, this is bully,
+you know. NOW who can he mean?"
+
+The howling stopped. Tom pricked up his ears.
+
+"Sh! What's that?" he whispered.
+
+"Sounds like--like hogs grunting. No--it's somebody snoring, Tom."
+
+"That IS it! Where 'bouts is it, Huck?"
+
+"I bleeve it's down at 'tother end. Sounds so, anyway. Pap used to
+sleep there, sometimes, 'long with the hogs, but laws bless you, he
+just lifts things when HE snores. Besides, I reckon he ain't ever
+coming back to this town any more."
+
+The spirit of adventure rose in the boys' souls once more.
+
+"Hucky, do you das't to go if I lead?"
+
+"I don't like to, much. Tom, s'pose it's Injun Joe!"
+
+Tom quailed. But presently the temptation rose up strong again and the
+boys agreed to try, with the understanding that they would take to
+their heels if the snoring stopped. So they went tiptoeing stealthily
+down, the one behind the other. When they had got to within five steps
+of the snorer, Tom stepped on a stick, and it broke with a sharp snap.
+The man moaned, writhed a little, and his face came into the moonlight.
+It was Muff Potter. The boys' hearts had stood still, and their hopes
+too, when the man moved, but their fears passed away now. They tiptoed
+out, through the broken weather-boarding, and stopped at a little
+distance to exchange a parting word. That long, lugubrious howl rose on
+the night air again! They turned and saw the strange dog standing
+within a few feet of where Potter was lying, and FACING Potter, with
+his nose pointing heavenward.
+
+"Oh, geeminy, it's HIM!" exclaimed both boys, in a breath.
+
+"Say, Tom--they say a stray dog come howling around Johnny Miller's
+house, 'bout midnight, as much as two weeks ago; and a whippoorwill
+come in and lit on the banisters and sung, the very same evening; and
+there ain't anybody dead there yet."
+
+"Well, I know that. And suppose there ain't. Didn't Gracie Miller fall
+in the kitchen fire and burn herself terrible the very next Saturday?"
+
+"Yes, but she ain't DEAD. And what's more, she's getting better, too."
+
+"All right, you wait and see. She's a goner, just as dead sure as Muff
+Potter's a goner. That's what the niggers say, and they know all about
+these kind of things, Huck."
+
+Then they separated, cogitating. When Tom crept in at his bedroom
+window the night was almost spent. He undressed with excessive caution,
+and fell asleep congratulating himself that nobody knew of his
+escapade. He was not aware that the gently-snoring Sid was awake, and
+had been so for an hour.
+
+When Tom awoke, Sid was dressed and gone. There was a late look in the
+light, a late sense in the atmosphere. He was startled. Why had he not
+been called--persecuted till he was up, as usual? The thought filled
+him with bodings. Within five minutes he was dressed and down-stairs,
+feeling sore and drowsy. The family were still at table, but they had
+finished breakfast. There was no voice of rebuke; but there were
+averted eyes; there was a silence and an air of solemnity that struck a
+chill to the culprit's heart. He sat down and tried to seem gay, but it
+was up-hill work; it roused no smile, no response, and he lapsed into
+silence and let his heart sink down to the depths.
+
+After breakfast his aunt took him aside, and Tom almost brightened in
+the hope that he was going to be flogged; but it was not so. His aunt
+wept over him and asked him how he could go and break her old heart so;
+and finally told him to go on, and ruin himself and bring her gray
+hairs with sorrow to the grave, for it was no use for her to try any
+more. This was worse than a thousand whippings, and Tom's heart was
+sorer now than his body. He cried, he pleaded for forgiveness, promised
+to reform over and over again, and then received his dismissal, feeling
+that he had won but an imperfect forgiveness and established but a
+feeble confidence.
+
+He left the presence too miserable to even feel revengeful toward Sid;
+and so the latter's prompt retreat through the back gate was
+unnecessary. He moped to school gloomy and sad, and took his flogging,
+along with Joe Harper, for playing hookey the day before, with the air
+of one whose heart was busy with heavier woes and wholly dead to
+trifles. Then he betook himself to his seat, rested his elbows on his
+desk and his jaws in his hands, and stared at the wall with the stony
+stare of suffering that has reached the limit and can no further go.
+His elbow was pressing against some hard substance. After a long time
+he slowly and sadly changed his position, and took up this object with
+a sigh. It was in a paper. He unrolled it. A long, lingering, colossal
+sigh followed, and his heart broke. It was his brass andiron knob!
+
+This final feather broke the camel's back.
+
+
+
+CHAPTER XI
+
+CLOSE upon the hour of noon the whole village was suddenly electrified
+with the ghastly news. No need of the as yet undreamed-of telegraph;
+the tale flew from man to man, from group to group, from house to
+house, with little less than telegraphic speed. Of course the
+schoolmaster gave holiday for that afternoon; the town would have
+thought strangely of him if he had not.
+
+A gory knife had been found close to the murdered man, and it had been
+recognized by somebody as belonging to Muff Potter--so the story ran.
+And it was said that a belated citizen had come upon Potter washing
+himself in the "branch" about one or two o'clock in the morning, and
+that Potter had at once sneaked off--suspicious circumstances,
+especially the washing which was not a habit with Potter. It was also
+said that the town had been ransacked for this "murderer" (the public
+are not slow in the matter of sifting evidence and arriving at a
+verdict), but that he could not be found. Horsemen had departed down
+all the roads in every direction, and the Sheriff "was confident" that
+he would be captured before night.
+
+All the town was drifting toward the graveyard. Tom's heartbreak
+vanished and he joined the procession, not because he would not a
+thousand times rather go anywhere else, but because an awful,
+unaccountable fascination drew him on. Arrived at the dreadful place,
+he wormed his small body through the crowd and saw the dismal
+spectacle. It seemed to him an age since he was there before. Somebody
+pinched his arm. He turned, and his eyes met Huckleberry's. Then both
+looked elsewhere at once, and wondered if anybody had noticed anything
+in their mutual glance. But everybody was talking, and intent upon the
+grisly spectacle before them.
+
+"Poor fellow!" "Poor young fellow!" "This ought to be a lesson to
+grave robbers!" "Muff Potter'll hang for this if they catch him!" This
+was the drift of remark; and the minister said, "It was a judgment; His
+hand is here."
+
+Now Tom shivered from head to heel; for his eye fell upon the stolid
+face of Injun Joe. At this moment the crowd began to sway and struggle,
+and voices shouted, "It's him! it's him! he's coming himself!"
+
+"Who? Who?" from twenty voices.
+
+"Muff Potter!"
+
+"Hallo, he's stopped!--Look out, he's turning! Don't let him get away!"
+
+People in the branches of the trees over Tom's head said he wasn't
+trying to get away--he only looked doubtful and perplexed.
+
+"Infernal impudence!" said a bystander; "wanted to come and take a
+quiet look at his work, I reckon--didn't expect any company."
+
+The crowd fell apart, now, and the Sheriff came through,
+ostentatiously leading Potter by the arm. The poor fellow's face was
+haggard, and his eyes showed the fear that was upon him. When he stood
+before the murdered man, he shook as with a palsy, and he put his face
+in his hands and burst into tears.
+
+"I didn't do it, friends," he sobbed; "'pon my word and honor I never
+done it."
+
+"Who's accused you?" shouted a voice.
+
+This shot seemed to carry home. Potter lifted his face and looked
+around him with a pathetic hopelessness in his eyes. He saw Injun Joe,
+and exclaimed:
+
+"Oh, Injun Joe, you promised me you'd never--"
+
+"Is that your knife?" and it was thrust before him by the Sheriff.
+
+Potter would have fallen if they had not caught him and eased him to
+the ground. Then he said:
+
+"Something told me 't if I didn't come back and get--" He shuddered;
+then waved his nerveless hand with a vanquished gesture and said, "Tell
+'em, Joe, tell 'em--it ain't any use any more."
+
+Then Huckleberry and Tom stood dumb and staring, and heard the
+stony-hearted liar reel off his serene statement, they expecting every
+moment that the clear sky would deliver God's lightnings upon his head,
+and wondering to see how long the stroke was delayed. And when he had
+finished and still stood alive and whole, their wavering impulse to
+break their oath and save the poor betrayed prisoner's life faded and
+vanished away, for plainly this miscreant had sold himself to Satan and
+it would be fatal to meddle with the property of such a power as that.
+
+"Why didn't you leave? What did you want to come here for?" somebody
+said.
+
+"I couldn't help it--I couldn't help it," Potter moaned. "I wanted to
+run away, but I couldn't seem to come anywhere but here." And he fell
+to sobbing again.
+
+Injun Joe repeated his statement, just as calmly, a few minutes
+afterward on the inquest, under oath; and the boys, seeing that the
+lightnings were still withheld, were confirmed in their belief that Joe
+had sold himself to the devil. He was now become, to them, the most
+balefully interesting object they had ever looked upon, and they could
+not take their fascinated eyes from his face.
+
+They inwardly resolved to watch him nights, when opportunity should
+offer, in the hope of getting a glimpse of his dread master.
+
+Injun Joe helped to raise the body of the murdered man and put it in a
+wagon for removal; and it was whispered through the shuddering crowd
+that the wound bled a little! The boys thought that this happy
+circumstance would turn suspicion in the right direction; but they were
+disappointed, for more than one villager remarked:
+
+"It was within three feet of Muff Potter when it done it."
+
+Tom's fearful secret and gnawing conscience disturbed his sleep for as
+much as a week after this; and at breakfast one morning Sid said:
+
+"Tom, you pitch around and talk in your sleep so much that you keep me
+awake half the time."
+
+Tom blanched and dropped his eyes.
+
+"It's a bad sign," said Aunt Polly, gravely. "What you got on your
+mind, Tom?"
+
+"Nothing. Nothing 't I know of." But the boy's hand shook so that he
+spilled his coffee.
+
+"And you do talk such stuff," Sid said. "Last night you said, 'It's
+blood, it's blood, that's what it is!' You said that over and over. And
+you said, 'Don't torment me so--I'll tell!' Tell WHAT? What is it
+you'll tell?"
+
+Everything was swimming before Tom. There is no telling what might
+have happened, now, but luckily the concern passed out of Aunt Polly's
+face and she came to Tom's relief without knowing it. She said:
+
+"Sho! It's that dreadful murder. I dream about it most every night
+myself. Sometimes I dream it's me that done it."
+
+Mary said she had been affected much the same way. Sid seemed
+satisfied. Tom got out of the presence as quick as he plausibly could,
+and after that he complained of toothache for a week, and tied up his
+jaws every night. He never knew that Sid lay nightly watching, and
+frequently slipped the bandage free and then leaned on his elbow
+listening a good while at a time, and afterward slipped the bandage
+back to its place again. Tom's distress of mind wore off gradually and
+the toothache grew irksome and was discarded. If Sid really managed to
+make anything out of Tom's disjointed mutterings, he kept it to himself.
+
+It seemed to Tom that his schoolmates never would get done holding
+inquests on dead cats, and thus keeping his trouble present to his
+mind. Sid noticed that Tom never was coroner at one of these inquiries,
+though it had been his habit to take the lead in all new enterprises;
+he noticed, too, that Tom never acted as a witness--and that was
+strange; and Sid did not overlook the fact that Tom even showed a
+marked aversion to these inquests, and always avoided them when he
+could. Sid marvelled, but said nothing. However, even inquests went out
+of vogue at last, and ceased to torture Tom's conscience.
+
+Every day or two, during this time of sorrow, Tom watched his
+opportunity and went to the little grated jail-window and smuggled such
+small comforts through to the "murderer" as he could get hold of. The
+jail was a trifling little brick den that stood in a marsh at the edge
+of the village, and no guards were afforded for it; indeed, it was
+seldom occupied. These offerings greatly helped to ease Tom's
+conscience.
+
+The villagers had a strong desire to tar-and-feather Injun Joe and
+ride him on a rail, for body-snatching, but so formidable was his
+character that nobody could be found who was willing to take the lead
+in the matter, so it was dropped. He had been careful to begin both of
+his inquest-statements with the fight, without confessing the
+grave-robbery that preceded it; therefore it was deemed wisest not
+to try the case in the courts at present.
+
+
+
+CHAPTER XII
+
+ONE of the reasons why Tom's mind had drifted away from its secret
+troubles was, that it had found a new and weighty matter to interest
+itself about. Becky Thatcher had stopped coming to school. Tom had
+struggled with his pride a few days, and tried to "whistle her down the
+wind," but failed. He began to find himself hanging around her father's
+house, nights, and feeling very miserable. She was ill. What if she
+should die! There was distraction in the thought. He no longer took an
+interest in war, nor even in piracy. The charm of life was gone; there
+was nothing but dreariness left. He put his hoop away, and his bat;
+there was no joy in them any more. His aunt was concerned. She began to
+try all manner of remedies on him. She was one of those people who are
+infatuated with patent medicines and all new-fangled methods of
+producing health or mending it. She was an inveterate experimenter in
+these things. When something fresh in this line came out she was in a
+fever, right away, to try it; not on herself, for she was never ailing,
+but on anybody else that came handy. She was a subscriber for all the
+"Health" periodicals and phrenological frauds; and the solemn ignorance
+they were inflated with was breath to her nostrils. All the "rot" they
+contained about ventilation, and how to go to bed, and how to get up,
+and what to eat, and what to drink, and how much exercise to take, and
+what frame of mind to keep one's self in, and what sort of clothing to
+wear, was all gospel to her, and she never observed that her
+health-journals of the current month customarily upset everything they
+had recommended the month before. She was as simple-hearted and honest
+as the day was long, and so she was an easy victim. She gathered
+together her quack periodicals and her quack medicines, and thus armed
+with death, went about on her pale horse, metaphorically speaking, with
+"hell following after." But she never suspected that she was not an
+angel of healing and the balm of Gilead in disguise, to the suffering
+neighbors.
+
+The water treatment was new, now, and Tom's low condition was a
+windfall to her. She had him out at daylight every morning, stood him
+up in the woodshed and drowned him with a deluge of cold water; then
+she scrubbed him down with a towel like a file, and so brought him to;
+then she rolled him up in a wet sheet and put him away under blankets
+till she sweated his soul clean and "the yellow stains of it came
+through his pores"--as Tom said.
+
+Yet notwithstanding all this, the boy grew more and more melancholy
+and pale and dejected. She added hot baths, sitz baths, shower baths,
+and plunges. The boy remained as dismal as a hearse. She began to
+assist the water with a slim oatmeal diet and blister-plasters. She
+calculated his capacity as she would a jug's, and filled him up every
+day with quack cure-alls.
+
+Tom had become indifferent to persecution by this time. This phase
+filled the old lady's heart with consternation. This indifference must
+be broken up at any cost. Now she heard of Pain-killer for the first
+time. She ordered a lot at once. She tasted it and was filled with
+gratitude. It was simply fire in a liquid form. She dropped the water
+treatment and everything else, and pinned her faith to Pain-killer. She
+gave Tom a teaspoonful and watched with the deepest anxiety for the
+result. Her troubles were instantly at rest, her soul at peace again;
+for the "indifference" was broken up. The boy could not have shown a
+wilder, heartier interest, if she had built a fire under him.
+
+Tom felt that it was time to wake up; this sort of life might be
+romantic enough, in his blighted condition, but it was getting to have
+too little sentiment and too much distracting variety about it. So he
+thought over various plans for relief, and finally hit pon that of
+professing to be fond of Pain-killer. He asked for it so often that he
+became a nuisance, and his aunt ended by telling him to help himself
+and quit bothering her. If it had been Sid, she would have had no
+misgivings to alloy her delight; but since it was Tom, she watched the
+bottle clandestinely. She found that the medicine did really diminish,
+but it did not occur to her that the boy was mending the health of a
+crack in the sitting-room floor with it.
+
+One day Tom was in the act of dosing the crack when his aunt's yellow
+cat came along, purring, eying the teaspoon avariciously, and begging
+for a taste. Tom said:
+
+"Don't ask for it unless you want it, Peter."
+
+But Peter signified that he did want it.
+
+"You better make sure."
+
+Peter was sure.
+
+"Now you've asked for it, and I'll give it to you, because there ain't
+anything mean about me; but if you find you don't like it, you mustn't
+blame anybody but your own self."
+
+Peter was agreeable. So Tom pried his mouth open and poured down the
+Pain-killer. Peter sprang a couple of yards in the air, and then
+delivered a war-whoop and set off round and round the room, banging
+against furniture, upsetting flower-pots, and making general havoc.
+Next he rose on his hind feet and pranced around, in a frenzy of
+enjoyment, with his head over his shoulder and his voice proclaiming
+his unappeasable happiness. Then he went tearing around the house again
+spreading chaos and destruction in his path. Aunt Polly entered in time
+to see him throw a few double summersets, deliver a final mighty
+hurrah, and sail through the open window, carrying the rest of the
+flower-pots with him. The old lady stood petrified with astonishment,
+peering over her glasses; Tom lay on the floor expiring with laughter.
+
+"Tom, what on earth ails that cat?"
+
+"I don't know, aunt," gasped the boy.
+
+"Why, I never see anything like it. What did make him act so?"
+
+"Deed I don't know, Aunt Polly; cats always act so when they're having
+a good time."
+
+"They do, do they?" There was something in the tone that made Tom
+apprehensive.
+
+"Yes'm. That is, I believe they do."
+
+"You DO?"
+
+"Yes'm."
+
+The old lady was bending down, Tom watching, with interest emphasized
+by anxiety. Too late he divined her "drift." The handle of the telltale
+teaspoon was visible under the bed-valance. Aunt Polly took it, held it
+up. Tom winced, and dropped his eyes. Aunt Polly raised him by the
+usual handle--his ear--and cracked his head soundly with her thimble.
+
+"Now, sir, what did you want to treat that poor dumb beast so, for?"
+
+"I done it out of pity for him--because he hadn't any aunt."
+
+"Hadn't any aunt!--you numskull. What has that got to do with it?"
+
+"Heaps. Because if he'd had one she'd a burnt him out herself! She'd a
+roasted his bowels out of him 'thout any more feeling than if he was a
+human!"
+
+Aunt Polly felt a sudden pang of remorse. This was putting the thing
+in a new light; what was cruelty to a cat MIGHT be cruelty to a boy,
+too. She began to soften; she felt sorry. Her eyes watered a little,
+and she put her hand on Tom's head and said gently:
+
+"I was meaning for the best, Tom. And, Tom, it DID do you good."
+
+Tom looked up in her face with just a perceptible twinkle peeping
+through his gravity.
+
+"I know you was meaning for the best, aunty, and so was I with Peter.
+It done HIM good, too. I never see him get around so since--"
+
+"Oh, go 'long with you, Tom, before you aggravate me again. And you
+try and see if you can't be a good boy, for once, and you needn't take
+any more medicine."
+
+Tom reached school ahead of time. It was noticed that this strange
+thing had been occurring every day latterly. And now, as usual of late,
+he hung about the gate of the schoolyard instead of playing with his
+comrades. He was sick, he said, and he looked it. He tried to seem to
+be looking everywhere but whither he really was looking--down the road.
+Presently Jeff Thatcher hove in sight, and Tom's face lighted; he gazed
+a moment, and then turned sorrowfully away. When Jeff arrived, Tom
+accosted him; and "led up" warily to opportunities for remark about
+Becky, but the giddy lad never could see the bait. Tom watched and
+watched, hoping whenever a frisking frock came in sight, and hating the
+owner of it as soon as he saw she was not the right one. At last frocks
+ceased to appear, and he dropped hopelessly into the dumps; he entered
+the empty schoolhouse and sat down to suffer. Then one more frock
+passed in at the gate, and Tom's heart gave a great bound. The next
+instant he was out, and "going on" like an Indian; yelling, laughing,
+chasing boys, jumping over the fence at risk of life and limb, throwing
+handsprings, standing on his head--doing all the heroic things he could
+conceive of, and keeping a furtive eye out, all the while, to see if
+Becky Thatcher was noticing. But she seemed to be unconscious of it
+all; she never looked. Could it be possible that she was not aware that
+he was there? He carried his exploits to her immediate vicinity; came
+war-whooping around, snatched a boy's cap, hurled it to the roof of the
+schoolhouse, broke through a group of boys, tumbling them in every
+direction, and fell sprawling, himself, under Becky's nose, almost
+upsetting her--and she turned, with her nose in the air, and he heard
+her say: "Mf! some people think they're mighty smart--always showing
+off!"
+
+Tom's cheeks burned. He gathered himself up and sneaked off, crushed
+and crestfallen.
+
+
+
+CHAPTER XIII
+
+TOM'S mind was made up now. He was gloomy and desperate. He was a
+forsaken, friendless boy, he said; nobody loved him; when they found
+out what they had driven him to, perhaps they would be sorry; he had
+tried to do right and get along, but they would not let him; since
+nothing would do them but to be rid of him, let it be so; and let them
+blame HIM for the consequences--why shouldn't they? What right had the
+friendless to complain? Yes, they had forced him to it at last: he
+would lead a life of crime. There was no choice.
+
+By this time he was far down Meadow Lane, and the bell for school to
+"take up" tinkled faintly upon his ear. He sobbed, now, to think he
+should never, never hear that old familiar sound any more--it was very
+hard, but it was forced on him; since he was driven out into the cold
+world, he must submit--but he forgave them. Then the sobs came thick
+and fast.
+
+Just at this point he met his soul's sworn comrade, Joe Harper
+--hard-eyed, and with evidently a great and dismal purpose in his heart.
+Plainly here were "two souls with but a single thought." Tom, wiping
+his eyes with his sleeve, began to blubber out something about a
+resolution to escape from hard usage and lack of sympathy at home by
+roaming abroad into the great world never to return; and ended by
+hoping that Joe would not forget him.
+
+But it transpired that this was a request which Joe had just been
+going to make of Tom, and had come to hunt him up for that purpose. His
+mother had whipped him for drinking some cream which he had never
+tasted and knew nothing about; it was plain that she was tired of him
+and wished him to go; if she felt that way, there was nothing for him
+to do but succumb; he hoped she would be happy, and never regret having
+driven her poor boy out into the unfeeling world to suffer and die.
+
+As the two boys walked sorrowing along, they made a new compact to
+stand by each other and be brothers and never separate till death
+relieved them of their troubles. Then they began to lay their plans.
+Joe was for being a hermit, and living on crusts in a remote cave, and
+dying, some time, of cold and want and grief; but after listening to
+Tom, he conceded that there were some conspicuous advantages about a
+life of crime, and so he consented to be a pirate.
+
+Three miles below St. Petersburg, at a point where the Mississippi
+River was a trifle over a mile wide, there was a long, narrow, wooded
+island, with a shallow bar at the head of it, and this offered well as
+a rendezvous. It was not inhabited; it lay far over toward the further
+shore, abreast a dense and almost wholly unpeopled forest. So Jackson's
+Island was chosen. Who were to be the subjects of their piracies was a
+matter that did not occur to them. Then they hunted up Huckleberry
+Finn, and he joined them promptly, for all careers were one to him; he
+was indifferent. They presently separated to meet at a lonely spot on
+the river-bank two miles above the village at the favorite hour--which
+was midnight. There was a small log raft there which they meant to
+capture. Each would bring hooks and lines, and such provision as he
+could steal in the most dark and mysterious way--as became outlaws. And
+before the afternoon was done, they had all managed to enjoy the sweet
+glory of spreading the fact that pretty soon the town would "hear
+something." All who got this vague hint were cautioned to "be mum and
+wait."
+
+About midnight Tom arrived with a boiled ham and a few trifles,
+and stopped in a dense undergrowth on a small bluff overlooking the
+meeting-place. It was starlight, and very still. The mighty river lay
+like an ocean at rest. Tom listened a moment, but no sound disturbed the
+quiet. Then he gave a low, distinct whistle. It was answered from under
+the bluff. Tom whistled twice more; these signals were answered in the
+same way. Then a guarded voice said:
+
+"Who goes there?"
+
+"Tom Sawyer, the Black Avenger of the Spanish Main. Name your names."
+
+"Huck Finn the Red-Handed, and Joe Harper the Terror of the Seas." Tom
+had furnished these titles, from his favorite literature.
+
+"'Tis well. Give the countersign."
+
+Two hoarse whispers delivered the same awful word simultaneously to
+the brooding night:
+
+"BLOOD!"
+
+Then Tom tumbled his ham over the bluff and let himself down after it,
+tearing both skin and clothes to some extent in the effort. There was
+an easy, comfortable path along the shore under the bluff, but it
+lacked the advantages of difficulty and danger so valued by a pirate.
+
+The Terror of the Seas had brought a side of bacon, and had about worn
+himself out with getting it there. Finn the Red-Handed had stolen a
+skillet and a quantity of half-cured leaf tobacco, and had also brought
+a few corn-cobs to make pipes with. But none of the pirates smoked or
+"chewed" but himself. The Black Avenger of the Spanish Main said it
+would never do to start without some fire. That was a wise thought;
+matches were hardly known there in that day. They saw a fire
+smouldering upon a great raft a hundred yards above, and they went
+stealthily thither and helped themselves to a chunk. They made an
+imposing adventure of it, saying, "Hist!" every now and then, and
+suddenly halting with finger on lip; moving with hands on imaginary
+dagger-hilts; and giving orders in dismal whispers that if "the foe"
+stirred, to "let him have it to the hilt," because "dead men tell no
+tales." They knew well enough that the raftsmen were all down at the
+village laying in stores or having a spree, but still that was no
+excuse for their conducting this thing in an unpiratical way.
+
+They shoved off, presently, Tom in command, Huck at the after oar and
+Joe at the forward. Tom stood amidships, gloomy-browed, and with folded
+arms, and gave his orders in a low, stern whisper:
+
+"Luff, and bring her to the wind!"
+
+"Aye-aye, sir!"
+
+"Steady, steady-y-y-y!"
+
+"Steady it is, sir!"
+
+"Let her go off a point!"
+
+"Point it is, sir!"
+
+As the boys steadily and monotonously drove the raft toward mid-stream
+it was no doubt understood that these orders were given only for
+"style," and were not intended to mean anything in particular.
+
+"What sail's she carrying?"
+
+"Courses, tops'ls, and flying-jib, sir."
+
+"Send the r'yals up! Lay out aloft, there, half a dozen of ye
+--foretopmaststuns'l! Lively, now!"
+
+"Aye-aye, sir!"
+
+"Shake out that maintogalans'l! Sheets and braces! NOW my hearties!"
+
+"Aye-aye, sir!"
+
+"Hellum-a-lee--hard a port! Stand by to meet her when she comes! Port,
+port! NOW, men! With a will! Stead-y-y-y!"
+
+"Steady it is, sir!"
+
+The raft drew beyond the middle of the river; the boys pointed her
+head right, and then lay on their oars. The river was not high, so
+there was not more than a two or three mile current. Hardly a word was
+said during the next three-quarters of an hour. Now the raft was
+passing before the distant town. Two or three glimmering lights showed
+where it lay, peacefully sleeping, beyond the vague vast sweep of
+star-gemmed water, unconscious of the tremendous event that was happening.
+The Black Avenger stood still with folded arms, "looking his last" upon
+the scene of his former joys and his later sufferings, and wishing
+"she" could see him now, abroad on the wild sea, facing peril and death
+with dauntless heart, going to his doom with a grim smile on his lips.
+It was but a small strain on his imagination to remove Jackson's Island
+beyond eyeshot of the village, and so he "looked his last" with a
+broken and satisfied heart. The other pirates were looking their last,
+too; and they all looked so long that they came near letting the
+current drift them out of the range of the island. But they discovered
+the danger in time, and made shift to avert it. About two o'clock in
+the morning the raft grounded on the bar two hundred yards above the
+head of the island, and they waded back and forth until they had landed
+their freight. Part of the little raft's belongings consisted of an old
+sail, and this they spread over a nook in the bushes for a tent to
+shelter their provisions; but they themselves would sleep in the open
+air in good weather, as became outlaws.
+
+They built a fire against the side of a great log twenty or thirty
+steps within the sombre depths of the forest, and then cooked some
+bacon in the frying-pan for supper, and used up half of the corn "pone"
+stock they had brought. It seemed glorious sport to be feasting in that
+wild, free way in the virgin forest of an unexplored and uninhabited
+island, far from the haunts of men, and they said they never would
+return to civilization. The climbing fire lit up their faces and threw
+its ruddy glare upon the pillared tree-trunks of their forest temple,
+and upon the varnished foliage and festooning vines.
+
+When the last crisp slice of bacon was gone, and the last allowance of
+corn pone devoured, the boys stretched themselves out on the grass,
+filled with contentment. They could have found a cooler place, but they
+would not deny themselves such a romantic feature as the roasting
+camp-fire.
+
+"AIN'T it gay?" said Joe.
+
+"It's NUTS!" said Tom. "What would the boys say if they could see us?"
+
+"Say? Well, they'd just die to be here--hey, Hucky!"
+
+"I reckon so," said Huckleberry; "anyways, I'm suited. I don't want
+nothing better'n this. I don't ever get enough to eat, gen'ally--and
+here they can't come and pick at a feller and bullyrag him so."
+
+"It's just the life for me," said Tom. "You don't have to get up,
+mornings, and you don't have to go to school, and wash, and all that
+blame foolishness. You see a pirate don't have to do ANYTHING, Joe,
+when he's ashore, but a hermit HE has to be praying considerable, and
+then he don't have any fun, anyway, all by himself that way."
+
+"Oh yes, that's so," said Joe, "but I hadn't thought much about it,
+you know. I'd a good deal rather be a pirate, now that I've tried it."
+
+"You see," said Tom, "people don't go much on hermits, nowadays, like
+they used to in old times, but a pirate's always respected. And a
+hermit's got to sleep on the hardest place he can find, and put
+sackcloth and ashes on his head, and stand out in the rain, and--"
+
+"What does he put sackcloth and ashes on his head for?" inquired Huck.
+
+"I dono. But they've GOT to do it. Hermits always do. You'd have to do
+that if you was a hermit."
+
+"Dern'd if I would," said Huck.
+
+"Well, what would you do?"
+
+"I dono. But I wouldn't do that."
+
+"Why, Huck, you'd HAVE to. How'd you get around it?"
+
+"Why, I just wouldn't stand it. I'd run away."
+
+"Run away! Well, you WOULD be a nice old slouch of a hermit. You'd be
+a disgrace."
+
+The Red-Handed made no response, being better employed. He had
+finished gouging out a cob, and now he fitted a weed stem to it, loaded
+it with tobacco, and was pressing a coal to the charge and blowing a
+cloud of fragrant smoke--he was in the full bloom of luxurious
+contentment. The other pirates envied him this majestic vice, and
+secretly resolved to acquire it shortly. Presently Huck said:
+
+"What does pirates have to do?"
+
+Tom said:
+
+"Oh, they have just a bully time--take ships and burn them, and get
+the money and bury it in awful places in their island where there's
+ghosts and things to watch it, and kill everybody in the ships--make
+'em walk a plank."
+
+"And they carry the women to the island," said Joe; "they don't kill
+the women."
+
+"No," assented Tom, "they don't kill the women--they're too noble. And
+the women's always beautiful, too.
+
+"And don't they wear the bulliest clothes! Oh no! All gold and silver
+and di'monds," said Joe, with enthusiasm.
+
+"Who?" said Huck.
+
+"Why, the pirates."
+
+Huck scanned his own clothing forlornly.
+
+"I reckon I ain't dressed fitten for a pirate," said he, with a
+regretful pathos in his voice; "but I ain't got none but these."
+
+But the other boys told him the fine clothes would come fast enough,
+after they should have begun their adventures. They made him understand
+that his poor rags would do to begin with, though it was customary for
+wealthy pirates to start with a proper wardrobe.
+
+Gradually their talk died out and drowsiness began to steal upon the
+eyelids of the little waifs. The pipe dropped from the fingers of the
+Red-Handed, and he slept the sleep of the conscience-free and the
+weary. The Terror of the Seas and the Black Avenger of the Spanish Main
+had more difficulty in getting to sleep. They said their prayers
+inwardly, and lying down, since there was nobody there with authority
+to make them kneel and recite aloud; in truth, they had a mind not to
+say them at all, but they were afraid to proceed to such lengths as
+that, lest they might call down a sudden and special thunderbolt from
+heaven. Then at once they reached and hovered upon the imminent verge
+of sleep--but an intruder came, now, that would not "down." It was
+conscience. They began to feel a vague fear that they had been doing
+wrong to run away; and next they thought of the stolen meat, and then
+the real torture came. They tried to argue it away by reminding
+conscience that they had purloined sweetmeats and apples scores of
+times; but conscience was not to be appeased by such thin
+plausibilities; it seemed to them, in the end, that there was no
+getting around the stubborn fact that taking sweetmeats was only
+"hooking," while taking bacon and hams and such valuables was plain
+simple stealing--and there was a command against that in the Bible. So
+they inwardly resolved that so long as they remained in the business,
+their piracies should not again be sullied with the crime of stealing.
+Then conscience granted a truce, and these curiously inconsistent
+pirates fell peacefully to sleep.
+
+
+
+CHAPTER XIV
+
+WHEN Tom awoke in the morning, he wondered where he was. He sat up and
+rubbed his eyes and looked around. Then he comprehended. It was the
+cool gray dawn, and there was a delicious sense of repose and peace in
+the deep pervading calm and silence of the woods. Not a leaf stirred;
+not a sound obtruded upon great Nature's meditation. Beaded dewdrops
+stood upon the leaves and grasses. A white layer of ashes covered the
+fire, and a thin blue breath of smoke rose straight into the air. Joe
+and Huck still slept.
+
+Now, far away in the woods a bird called; another answered; presently
+the hammering of a woodpecker was heard. Gradually the cool dim gray of
+the morning whitened, and as gradually sounds multiplied and life
+manifested itself. The marvel of Nature shaking off sleep and going to
+work unfolded itself to the musing boy. A little green worm came
+crawling over a dewy leaf, lifting two-thirds of his body into the air
+from time to time and "sniffing around," then proceeding again--for he
+was measuring, Tom said; and when the worm approached him, of its own
+accord, he sat as still as a stone, with his hopes rising and falling,
+by turns, as the creature still came toward him or seemed inclined to
+go elsewhere; and when at last it considered a painful moment with its
+curved body in the air and then came decisively down upon Tom's leg and
+began a journey over him, his whole heart was glad--for that meant that
+he was going to have a new suit of clothes--without the shadow of a
+doubt a gaudy piratical uniform. Now a procession of ants appeared,
+from nowhere in particular, and went about their labors; one struggled
+manfully by with a dead spider five times as big as itself in its arms,
+and lugged it straight up a tree-trunk. A brown spotted lady-bug
+climbed the dizzy height of a grass blade, and Tom bent down close to
+it and said, "Lady-bug, lady-bug, fly away home, your house is on fire,
+your children's alone," and she took wing and went off to see about it
+--which did not surprise the boy, for he knew of old that this insect was
+credulous about conflagrations, and he had practised upon its
+simplicity more than once. A tumblebug came next, heaving sturdily at
+its ball, and Tom touched the creature, to see it shut its legs against
+its body and pretend to be dead. The birds were fairly rioting by this
+time. A catbird, the Northern mocker, lit in a tree over Tom's head,
+and trilled out her imitations of her neighbors in a rapture of
+enjoyment; then a shrill jay swept down, a flash of blue flame, and
+stopped on a twig almost within the boy's reach, cocked his head to one
+side and eyed the strangers with a consuming curiosity; a gray squirrel
+and a big fellow of the "fox" kind came skurrying along, sitting up at
+intervals to inspect and chatter at the boys, for the wild things had
+probably never seen a human being before and scarcely knew whether to
+be afraid or not. All Nature was wide awake and stirring, now; long
+lances of sunlight pierced down through the dense foliage far and near,
+and a few butterflies came fluttering upon the scene.
+
+Tom stirred up the other pirates and they all clattered away with a
+shout, and in a minute or two were stripped and chasing after and
+tumbling over each other in the shallow limpid water of the white
+sandbar. They felt no longing for the little village sleeping in the
+distance beyond the majestic waste of water. A vagrant current or a
+slight rise in the river had carried off their raft, but this only
+gratified them, since its going was something like burning the bridge
+between them and civilization.
+
+They came back to camp wonderfully refreshed, glad-hearted, and
+ravenous; and they soon had the camp-fire blazing up again. Huck found
+a spring of clear cold water close by, and the boys made cups of broad
+oak or hickory leaves, and felt that water, sweetened with such a
+wildwood charm as that, would be a good enough substitute for coffee.
+While Joe was slicing bacon for breakfast, Tom and Huck asked him to
+hold on a minute; they stepped to a promising nook in the river-bank
+and threw in their lines; almost immediately they had reward. Joe had
+not had time to get impatient before they were back again with some
+handsome bass, a couple of sun-perch and a small catfish--provisions
+enough for quite a family. They fried the fish with the bacon, and were
+astonished; for no fish had ever seemed so delicious before. They did
+not know that the quicker a fresh-water fish is on the fire after he is
+caught the better he is; and they reflected little upon what a sauce
+open-air sleeping, open-air exercise, bathing, and a large ingredient
+of hunger make, too.
+
+They lay around in the shade, after breakfast, while Huck had a smoke,
+and then went off through the woods on an exploring expedition. They
+tramped gayly along, over decaying logs, through tangled underbrush,
+among solemn monarchs of the forest, hung from their crowns to the
+ground with a drooping regalia of grape-vines. Now and then they came
+upon snug nooks carpeted with grass and jeweled with flowers.
+
+They found plenty of things to be delighted with, but nothing to be
+astonished at. They discovered that the island was about three miles
+long and a quarter of a mile wide, and that the shore it lay closest to
+was only separated from it by a narrow channel hardly two hundred yards
+wide. They took a swim about every hour, so it was close upon the
+middle of the afternoon when they got back to camp. They were too
+hungry to stop to fish, but they fared sumptuously upon cold ham, and
+then threw themselves down in the shade to talk. But the talk soon
+began to drag, and then died. The stillness, the solemnity that brooded
+in the woods, and the sense of loneliness, began to tell upon the
+spirits of the boys. They fell to thinking. A sort of undefined longing
+crept upon them. This took dim shape, presently--it was budding
+homesickness. Even Finn the Red-Handed was dreaming of his doorsteps
+and empty hogsheads. But they were all ashamed of their weakness, and
+none was brave enough to speak his thought.
+
+For some time, now, the boys had been dully conscious of a peculiar
+sound in the distance, just as one sometimes is of the ticking of a
+clock which he takes no distinct note of. But now this mysterious sound
+became more pronounced, and forced a recognition. The boys started,
+glanced at each other, and then each assumed a listening attitude.
+There was a long silence, profound and unbroken; then a deep, sullen
+boom came floating down out of the distance.
+
+"What is it!" exclaimed Joe, under his breath.
+
+"I wonder," said Tom in a whisper.
+
+"'Tain't thunder," said Huckleberry, in an awed tone, "becuz thunder--"
+
+"Hark!" said Tom. "Listen--don't talk."
+
+They waited a time that seemed an age, and then the same muffled boom
+troubled the solemn hush.
+
+"Let's go and see."
+
+They sprang to their feet and hurried to the shore toward the town.
+They parted the bushes on the bank and peered out over the water. The
+little steam ferryboat was about a mile below the village, drifting
+with the current. Her broad deck seemed crowded with people. There were
+a great many skiffs rowing about or floating with the stream in the
+neighborhood of the ferryboat, but the boys could not determine what
+the men in them were doing. Presently a great jet of white smoke burst
+from the ferryboat's side, and as it expanded and rose in a lazy cloud,
+that same dull throb of sound was borne to the listeners again.
+
+"I know now!" exclaimed Tom; "somebody's drownded!"
+
+"That's it!" said Huck; "they done that last summer, when Bill Turner
+got drownded; they shoot a cannon over the water, and that makes him
+come up to the top. Yes, and they take loaves of bread and put
+quicksilver in 'em and set 'em afloat, and wherever there's anybody
+that's drownded, they'll float right there and stop."
+
+"Yes, I've heard about that," said Joe. "I wonder what makes the bread
+do that."
+
+"Oh, it ain't the bread, so much," said Tom; "I reckon it's mostly
+what they SAY over it before they start it out."
+
+"But they don't say anything over it," said Huck. "I've seen 'em and
+they don't."
+
+"Well, that's funny," said Tom. "But maybe they say it to themselves.
+Of COURSE they do. Anybody might know that."
+
+The other boys agreed that there was reason in what Tom said, because
+an ignorant lump of bread, uninstructed by an incantation, could not be
+expected to act very intelligently when set upon an errand of such
+gravity.
+
+"By jings, I wish I was over there, now," said Joe.
+
+"I do too" said Huck "I'd give heaps to know who it is."
+
+The boys still listened and watched. Presently a revealing thought
+flashed through Tom's mind, and he exclaimed:
+
+"Boys, I know who's drownded--it's us!"
+
+They felt like heroes in an instant. Here was a gorgeous triumph; they
+were missed; they were mourned; hearts were breaking on their account;
+tears were being shed; accusing memories of unkindness to these poor
+lost lads were rising up, and unavailing regrets and remorse were being
+indulged; and best of all, the departed were the talk of the whole
+town, and the envy of all the boys, as far as this dazzling notoriety
+was concerned. This was fine. It was worth while to be a pirate, after
+all.
+
+As twilight drew on, the ferryboat went back to her accustomed
+business and the skiffs disappeared. The pirates returned to camp. They
+were jubilant with vanity over their new grandeur and the illustrious
+trouble they were making. They caught fish, cooked supper and ate it,
+and then fell to guessing at what the village was thinking and saying
+about them; and the pictures they drew of the public distress on their
+account were gratifying to look upon--from their point of view. But
+when the shadows of night closed them in, they gradually ceased to
+talk, and sat gazing into the fire, with their minds evidently
+wandering elsewhere. The excitement was gone, now, and Tom and Joe
+could not keep back thoughts of certain persons at home who were not
+enjoying this fine frolic as much as they were. Misgivings came; they
+grew troubled and unhappy; a sigh or two escaped, unawares. By and by
+Joe timidly ventured upon a roundabout "feeler" as to how the others
+might look upon a return to civilization--not right now, but--
+
+Tom withered him with derision! Huck, being uncommitted as yet, joined
+in with Tom, and the waverer quickly "explained," and was glad to get
+out of the scrape with as little taint of chicken-hearted homesickness
+clinging to his garments as he could. Mutiny was effectually laid to
+rest for the moment.
+
+As the night deepened, Huck began to nod, and presently to snore. Joe
+followed next. Tom lay upon his elbow motionless, for some time,
+watching the two intently. At last he got up cautiously, on his knees,
+and went searching among the grass and the flickering reflections flung
+by the camp-fire. He picked up and inspected several large
+semi-cylinders of the thin white bark of a sycamore, and finally chose
+two which seemed to suit him. Then he knelt by the fire and painfully
+wrote something upon each of these with his "red keel"; one he rolled up
+and put in his jacket pocket, and the other he put in Joe's hat and
+removed it to a little distance from the owner. And he also put into the
+hat certain schoolboy treasures of almost inestimable value--among them
+a lump of chalk, an India-rubber ball, three fishhooks, and one of that
+kind of marbles known as a "sure 'nough crystal." Then he tiptoed his
+way cautiously among the trees till he felt that he was out of hearing,
+and straightway broke into a keen run in the direction of the sandbar.
+
+
+
+CHAPTER XV
+
+A FEW minutes later Tom was in the shoal water of the bar, wading
+toward the Illinois shore. Before the depth reached his middle he was
+half-way over; the current would permit no more wading, now, so he
+struck out confidently to swim the remaining hundred yards. He swam
+quartering upstream, but still was swept downward rather faster than he
+had expected. However, he reached the shore finally, and drifted along
+till he found a low place and drew himself out. He put his hand on his
+jacket pocket, found his piece of bark safe, and then struck through
+the woods, following the shore, with streaming garments. Shortly before
+ten o'clock he came out into an open place opposite the village, and
+saw the ferryboat lying in the shadow of the trees and the high bank.
+Everything was quiet under the blinking stars. He crept down the bank,
+watching with all his eyes, slipped into the water, swam three or four
+strokes and climbed into the skiff that did "yawl" duty at the boat's
+stern. He laid himself down under the thwarts and waited, panting.
+
+Presently the cracked bell tapped and a voice gave the order to "cast
+off." A minute or two later the skiff's head was standing high up,
+against the boat's swell, and the voyage was begun. Tom felt happy in
+his success, for he knew it was the boat's last trip for the night. At
+the end of a long twelve or fifteen minutes the wheels stopped, and Tom
+slipped overboard and swam ashore in the dusk, landing fifty yards
+downstream, out of danger of possible stragglers.
+
+He flew along unfrequented alleys, and shortly found himself at his
+aunt's back fence. He climbed over, approached the "ell," and looked in
+at the sitting-room window, for a light was burning there. There sat
+Aunt Polly, Sid, Mary, and Joe Harper's mother, grouped together,
+talking. They were by the bed, and the bed was between them and the
+door. Tom went to the door and began to softly lift the latch; then he
+pressed gently and the door yielded a crack; he continued pushing
+cautiously, and quaking every time it creaked, till he judged he might
+squeeze through on his knees; so he put his head through and began,
+warily.
+
+"What makes the candle blow so?" said Aunt Polly. Tom hurried up.
+"Why, that door's open, I believe. Why, of course it is. No end of
+strange things now. Go 'long and shut it, Sid."
+
+Tom disappeared under the bed just in time. He lay and "breathed"
+himself for a time, and then crept to where he could almost touch his
+aunt's foot.
+
+"But as I was saying," said Aunt Polly, "he warn't BAD, so to say
+--only mischEEvous. Only just giddy, and harum-scarum, you know. He
+warn't any more responsible than a colt. HE never meant any harm, and
+he was the best-hearted boy that ever was"--and she began to cry.
+
+"It was just so with my Joe--always full of his devilment, and up to
+every kind of mischief, but he was just as unselfish and kind as he
+could be--and laws bless me, to think I went and whipped him for taking
+that cream, never once recollecting that I throwed it out myself
+because it was sour, and I never to see him again in this world, never,
+never, never, poor abused boy!" And Mrs. Harper sobbed as if her heart
+would break.
+
+"I hope Tom's better off where he is," said Sid, "but if he'd been
+better in some ways--"
+
+"SID!" Tom felt the glare of the old lady's eye, though he could not
+see it. "Not a word against my Tom, now that he's gone! God'll take
+care of HIM--never you trouble YOURself, sir! Oh, Mrs. Harper, I don't
+know how to give him up! I don't know how to give him up! He was such a
+comfort to me, although he tormented my old heart out of me, 'most."
+
+"The Lord giveth and the Lord hath taken away--Blessed be the name of
+the Lord! But it's so hard--Oh, it's so hard! Only last Saturday my
+Joe busted a firecracker right under my nose and I knocked him
+sprawling. Little did I know then, how soon--Oh, if it was to do over
+again I'd hug him and bless him for it."
+
+"Yes, yes, yes, I know just how you feel, Mrs. Harper, I know just
+exactly how you feel. No longer ago than yesterday noon, my Tom took
+and filled the cat full of Pain-killer, and I did think the cretur
+would tear the house down. And God forgive me, I cracked Tom's head
+with my thimble, poor boy, poor dead boy. But he's out of all his
+troubles now. And the last words I ever heard him say was to reproach--"
+
+But this memory was too much for the old lady, and she broke entirely
+down. Tom was snuffling, now, himself--and more in pity of himself than
+anybody else. He could hear Mary crying, and putting in a kindly word
+for him from time to time. He began to have a nobler opinion of himself
+than ever before. Still, he was sufficiently touched by his aunt's
+grief to long to rush out from under the bed and overwhelm her with
+joy--and the theatrical gorgeousness of the thing appealed strongly to
+his nature, too, but he resisted and lay still.
+
+He went on listening, and gathered by odds and ends that it was
+conjectured at first that the boys had got drowned while taking a swim;
+then the small raft had been missed; next, certain boys said the
+missing lads had promised that the village should "hear something"
+soon; the wise-heads had "put this and that together" and decided that
+the lads had gone off on that raft and would turn up at the next town
+below, presently; but toward noon the raft had been found, lodged
+against the Missouri shore some five or six miles below the village
+--and then hope perished; they must be drowned, else hunger would have
+driven them home by nightfall if not sooner. It was believed that the
+search for the bodies had been a fruitless effort merely because the
+drowning must have occurred in mid-channel, since the boys, being good
+swimmers, would otherwise have escaped to shore. This was Wednesday
+night. If the bodies continued missing until Sunday, all hope would be
+given over, and the funerals would be preached on that morning. Tom
+shuddered.
+
+Mrs. Harper gave a sobbing good-night and turned to go. Then with a
+mutual impulse the two bereaved women flung themselves into each
+other's arms and had a good, consoling cry, and then parted. Aunt Polly
+was tender far beyond her wont, in her good-night to Sid and Mary. Sid
+snuffled a bit and Mary went off crying with all her heart.
+
+Aunt Polly knelt down and prayed for Tom so touchingly, so
+appealingly, and with such measureless love in her words and her old
+trembling voice, that he was weltering in tears again, long before she
+was through.
+
+He had to keep still long after she went to bed, for she kept making
+broken-hearted ejaculations from time to time, tossing unrestfully, and
+turning over. But at last she was still, only moaning a little in her
+sleep. Now the boy stole out, rose gradually by the bedside, shaded the
+candle-light with his hand, and stood regarding her. His heart was full
+of pity for her. He took out his sycamore scroll and placed it by the
+candle. But something occurred to him, and he lingered considering. His
+face lighted with a happy solution of his thought; he put the bark
+hastily in his pocket. Then he bent over and kissed the faded lips, and
+straightway made his stealthy exit, latching the door behind him.
+
+He threaded his way back to the ferry landing, found nobody at large
+there, and walked boldly on board the boat, for he knew she was
+tenantless except that there was a watchman, who always turned in and
+slept like a graven image. He untied the skiff at the stern, slipped
+into it, and was soon rowing cautiously upstream. When he had pulled a
+mile above the village, he started quartering across and bent himself
+stoutly to his work. He hit the landing on the other side neatly, for
+this was a familiar bit of work to him. He was moved to capture the
+skiff, arguing that it might be considered a ship and therefore
+legitimate prey for a pirate, but he knew a thorough search would be
+made for it and that might end in revelations. So he stepped ashore and
+entered the woods.
+
+He sat down and took a long rest, torturing himself meanwhile to keep
+awake, and then started warily down the home-stretch. The night was far
+spent. It was broad daylight before he found himself fairly abreast the
+island bar. He rested again until the sun was well up and gilding the
+great river with its splendor, and then he plunged into the stream. A
+little later he paused, dripping, upon the threshold of the camp, and
+heard Joe say:
+
+"No, Tom's true-blue, Huck, and he'll come back. He won't desert. He
+knows that would be a disgrace to a pirate, and Tom's too proud for
+that sort of thing. He's up to something or other. Now I wonder what?"
+
+"Well, the things is ours, anyway, ain't they?"
+
+"Pretty near, but not yet, Huck. The writing says they are if he ain't
+back here to breakfast."
+
+"Which he is!" exclaimed Tom, with fine dramatic effect, stepping
+grandly into camp.
+
+A sumptuous breakfast of bacon and fish was shortly provided, and as
+the boys set to work upon it, Tom recounted (and adorned) his
+adventures. They were a vain and boastful company of heroes when the
+tale was done. Then Tom hid himself away in a shady nook to sleep till
+noon, and the other pirates got ready to fish and explore.
+
+
+
+CHAPTER XVI
+
+AFTER dinner all the gang turned out to hunt for turtle eggs on the
+bar. They went about poking sticks into the sand, and when they found a
+soft place they went down on their knees and dug with their hands.
+Sometimes they would take fifty or sixty eggs out of one hole. They
+were perfectly round white things a trifle smaller than an English
+walnut. They had a famous fried-egg feast that night, and another on
+Friday morning.
+
+After breakfast they went whooping and prancing out on the bar, and
+chased each other round and round, shedding clothes as they went, until
+they were naked, and then continued the frolic far away up the shoal
+water of the bar, against the stiff current, which latter tripped their
+legs from under them from time to time and greatly increased the fun.
+And now and then they stooped in a group and splashed water in each
+other's faces with their palms, gradually approaching each other, with
+averted faces to avoid the strangling sprays, and finally gripping and
+struggling till the best man ducked his neighbor, and then they all
+went under in a tangle of white legs and arms and came up blowing,
+sputtering, laughing, and gasping for breath at one and the same time.
+
+When they were well exhausted, they would run out and sprawl on the
+dry, hot sand, and lie there and cover themselves up with it, and by
+and by break for the water again and go through the original
+performance once more. Finally it occurred to them that their naked
+skin represented flesh-colored "tights" very fairly; so they drew a
+ring in the sand and had a circus--with three clowns in it, for none
+would yield this proudest post to his neighbor.
+
+Next they got their marbles and played "knucks" and "ring-taw" and
+"keeps" till that amusement grew stale. Then Joe and Huck had another
+swim, but Tom would not venture, because he found that in kicking off
+his trousers he had kicked his string of rattlesnake rattles off his
+ankle, and he wondered how he had escaped cramp so long without the
+protection of this mysterious charm. He did not venture again until he
+had found it, and by that time the other boys were tired and ready to
+rest. They gradually wandered apart, dropped into the "dumps," and fell
+to gazing longingly across the wide river to where the village lay
+drowsing in the sun. Tom found himself writing "BECKY" in the sand with
+his big toe; he scratched it out, and was angry with himself for his
+weakness. But he wrote it again, nevertheless; he could not help it. He
+erased it once more and then took himself out of temptation by driving
+the other boys together and joining them.
+
+But Joe's spirits had gone down almost beyond resurrection. He was so
+homesick that he could hardly endure the misery of it. The tears lay
+very near the surface. Huck was melancholy, too. Tom was downhearted,
+but tried hard not to show it. He had a secret which he was not ready
+to tell, yet, but if this mutinous depression was not broken up soon,
+he would have to bring it out. He said, with a great show of
+cheerfulness:
+
+"I bet there's been pirates on this island before, boys. We'll explore
+it again. They've hid treasures here somewhere. How'd you feel to light
+on a rotten chest full of gold and silver--hey?"
+
+But it roused only faint enthusiasm, which faded out, with no reply.
+Tom tried one or two other seductions; but they failed, too. It was
+discouraging work. Joe sat poking up the sand with a stick and looking
+very gloomy. Finally he said:
+
+"Oh, boys, let's give it up. I want to go home. It's so lonesome."
+
+"Oh no, Joe, you'll feel better by and by," said Tom. "Just think of
+the fishing that's here."
+
+"I don't care for fishing. I want to go home."
+
+"But, Joe, there ain't such another swimming-place anywhere."
+
+"Swimming's no good. I don't seem to care for it, somehow, when there
+ain't anybody to say I sha'n't go in. I mean to go home."
+
+"Oh, shucks! Baby! You want to see your mother, I reckon."
+
+"Yes, I DO want to see my mother--and you would, too, if you had one.
+I ain't any more baby than you are." And Joe snuffled a little.
+
+"Well, we'll let the cry-baby go home to his mother, won't we, Huck?
+Poor thing--does it want to see its mother? And so it shall. You like
+it here, don't you, Huck? We'll stay, won't we?"
+
+Huck said, "Y-e-s"--without any heart in it.
+
+"I'll never speak to you again as long as I live," said Joe, rising.
+"There now!" And he moved moodily away and began to dress himself.
+
+"Who cares!" said Tom. "Nobody wants you to. Go 'long home and get
+laughed at. Oh, you're a nice pirate. Huck and me ain't cry-babies.
+We'll stay, won't we, Huck? Let him go if he wants to. I reckon we can
+get along without him, per'aps."
+
+But Tom was uneasy, nevertheless, and was alarmed to see Joe go
+sullenly on with his dressing. And then it was discomforting to see
+Huck eying Joe's preparations so wistfully, and keeping up such an
+ominous silence. Presently, without a parting word, Joe began to wade
+off toward the Illinois shore. Tom's heart began to sink. He glanced at
+Huck. Huck could not bear the look, and dropped his eyes. Then he said:
+
+"I want to go, too, Tom. It was getting so lonesome anyway, and now
+it'll be worse. Let's us go, too, Tom."
+
+"I won't! You can all go, if you want to. I mean to stay."
+
+"Tom, I better go."
+
+"Well, go 'long--who's hendering you."
+
+Huck began to pick up his scattered clothes. He said:
+
+"Tom, I wisht you'd come, too. Now you think it over. We'll wait for
+you when we get to shore."
+
+"Well, you'll wait a blame long time, that's all."
+
+Huck started sorrowfully away, and Tom stood looking after him, with a
+strong desire tugging at his heart to yield his pride and go along too.
+He hoped the boys would stop, but they still waded slowly on. It
+suddenly dawned on Tom that it was become very lonely and still. He
+made one final struggle with his pride, and then darted after his
+comrades, yelling:
+
+"Wait! Wait! I want to tell you something!"
+
+They presently stopped and turned around. When he got to where they
+were, he began unfolding his secret, and they listened moodily till at
+last they saw the "point" he was driving at, and then they set up a
+war-whoop of applause and said it was "splendid!" and said if he had
+told them at first, they wouldn't have started away. He made a plausible
+excuse; but his real reason had been the fear that not even the secret
+would keep them with him any very great length of time, and so he had
+meant to hold it in reserve as a last seduction.
+
+The lads came gayly back and went at their sports again with a will,
+chattering all the time about Tom's stupendous plan and admiring the
+genius of it. After a dainty egg and fish dinner, Tom said he wanted to
+learn to smoke, now. Joe caught at the idea and said he would like to
+try, too. So Huck made pipes and filled them. These novices had never
+smoked anything before but cigars made of grape-vine, and they "bit"
+the tongue, and were not considered manly anyway.
+
+Now they stretched themselves out on their elbows and began to puff,
+charily, and with slender confidence. The smoke had an unpleasant
+taste, and they gagged a little, but Tom said:
+
+"Why, it's just as easy! If I'd a knowed this was all, I'd a learnt
+long ago."
+
+"So would I," said Joe. "It's just nothing."
+
+"Why, many a time I've looked at people smoking, and thought well I
+wish I could do that; but I never thought I could," said Tom.
+
+"That's just the way with me, hain't it, Huck? You've heard me talk
+just that way--haven't you, Huck? I'll leave it to Huck if I haven't."
+
+"Yes--heaps of times," said Huck.
+
+"Well, I have too," said Tom; "oh, hundreds of times. Once down by the
+slaughter-house. Don't you remember, Huck? Bob Tanner was there, and
+Johnny Miller, and Jeff Thatcher, when I said it. Don't you remember,
+Huck, 'bout me saying that?"
+
+"Yes, that's so," said Huck. "That was the day after I lost a white
+alley. No, 'twas the day before."
+
+"There--I told you so," said Tom. "Huck recollects it."
+
+"I bleeve I could smoke this pipe all day," said Joe. "I don't feel
+sick."
+
+"Neither do I," said Tom. "I could smoke it all day. But I bet you
+Jeff Thatcher couldn't."
+
+"Jeff Thatcher! Why, he'd keel over just with two draws. Just let him
+try it once. HE'D see!"
+
+"I bet he would. And Johnny Miller--I wish could see Johnny Miller
+tackle it once."
+
+"Oh, don't I!" said Joe. "Why, I bet you Johnny Miller couldn't any
+more do this than nothing. Just one little snifter would fetch HIM."
+
+"'Deed it would, Joe. Say--I wish the boys could see us now."
+
+"So do I."
+
+"Say--boys, don't say anything about it, and some time when they're
+around, I'll come up to you and say, 'Joe, got a pipe? I want a smoke.'
+And you'll say, kind of careless like, as if it warn't anything, you'll
+say, 'Yes, I got my OLD pipe, and another one, but my tobacker ain't
+very good.' And I'll say, 'Oh, that's all right, if it's STRONG
+enough.' And then you'll out with the pipes, and we'll light up just as
+ca'm, and then just see 'em look!"
+
+"By jings, that'll be gay, Tom! I wish it was NOW!"
+
+"So do I! And when we tell 'em we learned when we was off pirating,
+won't they wish they'd been along?"
+
+"Oh, I reckon not! I'll just BET they will!"
+
+So the talk ran on. But presently it began to flag a trifle, and grow
+disjointed. The silences widened; the expectoration marvellously
+increased. Every pore inside the boys' cheeks became a spouting
+fountain; they could scarcely bail out the cellars under their tongues
+fast enough to prevent an inundation; little overflowings down their
+throats occurred in spite of all they could do, and sudden retchings
+followed every time. Both boys were looking very pale and miserable,
+now. Joe's pipe dropped from his nerveless fingers. Tom's followed.
+Both fountains were going furiously and both pumps bailing with might
+and main. Joe said feebly:
+
+"I've lost my knife. I reckon I better go and find it."
+
+Tom said, with quivering lips and halting utterance:
+
+"I'll help you. You go over that way and I'll hunt around by the
+spring. No, you needn't come, Huck--we can find it."
+
+So Huck sat down again, and waited an hour. Then he found it lonesome,
+and went to find his comrades. They were wide apart in the woods, both
+very pale, both fast asleep. But something informed him that if they
+had had any trouble they had got rid of it.
+
+They were not talkative at supper that night. They had a humble look,
+and when Huck prepared his pipe after the meal and was going to prepare
+theirs, they said no, they were not feeling very well--something they
+ate at dinner had disagreed with them.
+
+About midnight Joe awoke, and called the boys. There was a brooding
+oppressiveness in the air that seemed to bode something. The boys
+huddled themselves together and sought the friendly companionship of
+the fire, though the dull dead heat of the breathless atmosphere was
+stifling. They sat still, intent and waiting. The solemn hush
+continued. Beyond the light of the fire everything was swallowed up in
+the blackness of darkness. Presently there came a quivering glow that
+vaguely revealed the foliage for a moment and then vanished. By and by
+another came, a little stronger. Then another. Then a faint moan came
+sighing through the branches of the forest and the boys felt a fleeting
+breath upon their cheeks, and shuddered with the fancy that the Spirit
+of the Night had gone by. There was a pause. Now a weird flash turned
+night into day and showed every little grass-blade, separate and
+distinct, that grew about their feet. And it showed three white,
+startled faces, too. A deep peal of thunder went rolling and tumbling
+down the heavens and lost itself in sullen rumblings in the distance. A
+sweep of chilly air passed by, rustling all the leaves and snowing the
+flaky ashes broadcast about the fire. Another fierce glare lit up the
+forest and an instant crash followed that seemed to rend the tree-tops
+right over the boys' heads. They clung together in terror, in the thick
+gloom that followed. A few big rain-drops fell pattering upon the
+leaves.
+
+"Quick! boys, go for the tent!" exclaimed Tom.
+
+They sprang away, stumbling over roots and among vines in the dark, no
+two plunging in the same direction. A furious blast roared through the
+trees, making everything sing as it went. One blinding flash after
+another came, and peal on peal of deafening thunder. And now a
+drenching rain poured down and the rising hurricane drove it in sheets
+along the ground. The boys cried out to each other, but the roaring
+wind and the booming thunder-blasts drowned their voices utterly.
+However, one by one they straggled in at last and took shelter under
+the tent, cold, scared, and streaming with water; but to have company
+in misery seemed something to be grateful for. They could not talk, the
+old sail flapped so furiously, even if the other noises would have
+allowed them. The tempest rose higher and higher, and presently the
+sail tore loose from its fastenings and went winging away on the blast.
+The boys seized each others' hands and fled, with many tumblings and
+bruises, to the shelter of a great oak that stood upon the river-bank.
+Now the battle was at its highest. Under the ceaseless conflagration of
+lightning that flamed in the skies, everything below stood out in
+clean-cut and shadowless distinctness: the bending trees, the billowy
+river, white with foam, the driving spray of spume-flakes, the dim
+outlines of the high bluffs on the other side, glimpsed through the
+drifting cloud-rack and the slanting veil of rain. Every little while
+some giant tree yielded the fight and fell crashing through the younger
+growth; and the unflagging thunder-peals came now in ear-splitting
+explosive bursts, keen and sharp, and unspeakably appalling. The storm
+culminated in one matchless effort that seemed likely to tear the island
+to pieces, burn it up, drown it to the tree-tops, blow it away, and
+deafen every creature in it, all at one and the same moment. It was a
+wild night for homeless young heads to be out in.
+
+But at last the battle was done, and the forces retired with weaker
+and weaker threatenings and grumblings, and peace resumed her sway. The
+boys went back to camp, a good deal awed; but they found there was
+still something to be thankful for, because the great sycamore, the
+shelter of their beds, was a ruin, now, blasted by the lightnings, and
+they were not under it when the catastrophe happened.
+
+Everything in camp was drenched, the camp-fire as well; for they were
+but heedless lads, like their generation, and had made no provision
+against rain. Here was matter for dismay, for they were soaked through
+and chilled. They were eloquent in their distress; but they presently
+discovered that the fire had eaten so far up under the great log it had
+been built against (where it curved upward and separated itself from
+the ground), that a handbreadth or so of it had escaped wetting; so
+they patiently wrought until, with shreds and bark gathered from the
+under sides of sheltered logs, they coaxed the fire to burn again. Then
+they piled on great dead boughs till they had a roaring furnace, and
+were glad-hearted once more. They dried their boiled ham and had a
+feast, and after that they sat by the fire and expanded and glorified
+their midnight adventure until morning, for there was not a dry spot to
+sleep on, anywhere around.
+
+As the sun began to steal in upon the boys, drowsiness came over them,
+and they went out on the sandbar and lay down to sleep. They got
+scorched out by and by, and drearily set about getting breakfast. After
+the meal they felt rusty, and stiff-jointed, and a little homesick once
+more. Tom saw the signs, and fell to cheering up the pirates as well as
+he could. But they cared nothing for marbles, or circus, or swimming,
+or anything. He reminded them of the imposing secret, and raised a ray
+of cheer. While it lasted, he got them interested in a new device. This
+was to knock off being pirates, for a while, and be Indians for a
+change. They were attracted by this idea; so it was not long before
+they were stripped, and striped from head to heel with black mud, like
+so many zebras--all of them chiefs, of course--and then they went
+tearing through the woods to attack an English settlement.
+
+By and by they separated into three hostile tribes, and darted upon
+each other from ambush with dreadful war-whoops, and killed and scalped
+each other by thousands. It was a gory day. Consequently it was an
+extremely satisfactory one.
+
+They assembled in camp toward supper-time, hungry and happy; but now a
+difficulty arose--hostile Indians could not break the bread of
+hospitality together without first making peace, and this was a simple
+impossibility without smoking a pipe of peace. There was no other
+process that ever they had heard of. Two of the savages almost wished
+they had remained pirates. However, there was no other way; so with
+such show of cheerfulness as they could muster they called for the pipe
+and took their whiff as it passed, in due form.
+
+And behold, they were glad they had gone into savagery, for they had
+gained something; they found that they could now smoke a little without
+having to go and hunt for a lost knife; they did not get sick enough to
+be seriously uncomfortable. They were not likely to fool away this high
+promise for lack of effort. No, they practised cautiously, after
+supper, with right fair success, and so they spent a jubilant evening.
+They were prouder and happier in their new acquirement than they would
+have been in the scalping and skinning of the Six Nations. We will
+leave them to smoke and chatter and brag, since we have no further use
+for them at present.
+
+
+
+CHAPTER XVII
+
+BUT there was no hilarity in the little town that same tranquil
+Saturday afternoon. The Harpers, and Aunt Polly's family, were being
+put into mourning, with great grief and many tears. An unusual quiet
+possessed the village, although it was ordinarily quiet enough, in all
+conscience. The villagers conducted their concerns with an absent air,
+and talked little; but they sighed often. The Saturday holiday seemed a
+burden to the children. They had no heart in their sports, and
+gradually gave them up.
+
+In the afternoon Becky Thatcher found herself moping about the
+deserted schoolhouse yard, and feeling very melancholy. But she found
+nothing there to comfort her. She soliloquized:
+
+"Oh, if I only had a brass andiron-knob again! But I haven't got
+anything now to remember him by." And she choked back a little sob.
+
+Presently she stopped, and said to herself:
+
+"It was right here. Oh, if it was to do over again, I wouldn't say
+that--I wouldn't say it for the whole world. But he's gone now; I'll
+never, never, never see him any more."
+
+This thought broke her down, and she wandered away, with tears rolling
+down her cheeks. Then quite a group of boys and girls--playmates of
+Tom's and Joe's--came by, and stood looking over the paling fence and
+talking in reverent tones of how Tom did so-and-so the last time they
+saw him, and how Joe said this and that small trifle (pregnant with
+awful prophecy, as they could easily see now!)--and each speaker
+pointed out the exact spot where the lost lads stood at the time, and
+then added something like "and I was a-standing just so--just as I am
+now, and as if you was him--I was as close as that--and he smiled, just
+this way--and then something seemed to go all over me, like--awful, you
+know--and I never thought what it meant, of course, but I can see now!"
+
+Then there was a dispute about who saw the dead boys last in life, and
+many claimed that dismal distinction, and offered evidences, more or
+less tampered with by the witness; and when it was ultimately decided
+who DID see the departed last, and exchanged the last words with them,
+the lucky parties took upon themselves a sort of sacred importance, and
+were gaped at and envied by all the rest. One poor chap, who had no
+other grandeur to offer, said with tolerably manifest pride in the
+remembrance:
+
+"Well, Tom Sawyer he licked me once."
+
+But that bid for glory was a failure. Most of the boys could say that,
+and so that cheapened the distinction too much. The group loitered
+away, still recalling memories of the lost heroes, in awed voices.
+
+When the Sunday-school hour was finished, the next morning, the bell
+began to toll, instead of ringing in the usual way. It was a very still
+Sabbath, and the mournful sound seemed in keeping with the musing hush
+that lay upon nature. The villagers began to gather, loitering a moment
+in the vestibule to converse in whispers about the sad event. But there
+was no whispering in the house; only the funereal rustling of dresses
+as the women gathered to their seats disturbed the silence there. None
+could remember when the little church had been so full before. There
+was finally a waiting pause, an expectant dumbness, and then Aunt Polly
+entered, followed by Sid and Mary, and they by the Harper family, all
+in deep black, and the whole congregation, the old minister as well,
+rose reverently and stood until the mourners were seated in the front
+pew. There was another communing silence, broken at intervals by
+muffled sobs, and then the minister spread his hands abroad and prayed.
+A moving hymn was sung, and the text followed: "I am the Resurrection
+and the Life."
+
+As the service proceeded, the clergyman drew such pictures of the
+graces, the winning ways, and the rare promise of the lost lads that
+every soul there, thinking he recognized these pictures, felt a pang in
+remembering that he had persistently blinded himself to them always
+before, and had as persistently seen only faults and flaws in the poor
+boys. The minister related many a touching incident in the lives of the
+departed, too, which illustrated their sweet, generous natures, and the
+people could easily see, now, how noble and beautiful those episodes
+were, and remembered with grief that at the time they occurred they had
+seemed rank rascalities, well deserving of the cowhide. The
+congregation became more and more moved, as the pathetic tale went on,
+till at last the whole company broke down and joined the weeping
+mourners in a chorus of anguished sobs, the preacher himself giving way
+to his feelings, and crying in the pulpit.
+
+There was a rustle in the gallery, which nobody noticed; a moment
+later the church door creaked; the minister raised his streaming eyes
+above his handkerchief, and stood transfixed! First one and then
+another pair of eyes followed the minister's, and then almost with one
+impulse the congregation rose and stared while the three dead boys came
+marching up the aisle, Tom in the lead, Joe next, and Huck, a ruin of
+drooping rags, sneaking sheepishly in the rear! They had been hid in
+the unused gallery listening to their own funeral sermon!
+
+Aunt Polly, Mary, and the Harpers threw themselves upon their restored
+ones, smothered them with kisses and poured out thanksgivings, while
+poor Huck stood abashed and uncomfortable, not knowing exactly what to
+do or where to hide from so many unwelcoming eyes. He wavered, and
+started to slink away, but Tom seized him and said:
+
+"Aunt Polly, it ain't fair. Somebody's got to be glad to see Huck."
+
+"And so they shall. I'm glad to see him, poor motherless thing!" And
+the loving attentions Aunt Polly lavished upon him were the one thing
+capable of making him more uncomfortable than he was before.
+
+Suddenly the minister shouted at the top of his voice: "Praise God
+from whom all blessings flow--SING!--and put your hearts in it!"
+
+And they did. Old Hundred swelled up with a triumphant burst, and
+while it shook the rafters Tom Sawyer the Pirate looked around upon the
+envying juveniles about him and confessed in his heart that this was
+the proudest moment of his life.
+
+As the "sold" congregation trooped out they said they would almost be
+willing to be made ridiculous again to hear Old Hundred sung like that
+once more.
+
+Tom got more cuffs and kisses that day--according to Aunt Polly's
+varying moods--than he had earned before in a year; and he hardly knew
+which expressed the most gratefulness to God and affection for himself.
+
+
+
+CHAPTER XVIII
+
+THAT was Tom's great secret--the scheme to return home with his
+brother pirates and attend their own funerals. They had paddled over to
+the Missouri shore on a log, at dusk on Saturday, landing five or six
+miles below the village; they had slept in the woods at the edge of the
+town till nearly daylight, and had then crept through back lanes and
+alleys and finished their sleep in the gallery of the church among a
+chaos of invalided benches.
+
+At breakfast, Monday morning, Aunt Polly and Mary were very loving to
+Tom, and very attentive to his wants. There was an unusual amount of
+talk. In the course of it Aunt Polly said:
+
+"Well, I don't say it wasn't a fine joke, Tom, to keep everybody
+suffering 'most a week so you boys had a good time, but it is a pity
+you could be so hard-hearted as to let me suffer so. If you could come
+over on a log to go to your funeral, you could have come over and give
+me a hint some way that you warn't dead, but only run off."
+
+"Yes, you could have done that, Tom," said Mary; "and I believe you
+would if you had thought of it."
+
+"Would you, Tom?" said Aunt Polly, her face lighting wistfully. "Say,
+now, would you, if you'd thought of it?"
+
+"I--well, I don't know. 'Twould 'a' spoiled everything."
+
+"Tom, I hoped you loved me that much," said Aunt Polly, with a grieved
+tone that discomforted the boy. "It would have been something if you'd
+cared enough to THINK of it, even if you didn't DO it."
+
+"Now, auntie, that ain't any harm," pleaded Mary; "it's only Tom's
+giddy way--he is always in such a rush that he never thinks of
+anything."
+
+"More's the pity. Sid would have thought. And Sid would have come and
+DONE it, too. Tom, you'll look back, some day, when it's too late, and
+wish you'd cared a little more for me when it would have cost you so
+little."
+
+"Now, auntie, you know I do care for you," said Tom.
+
+"I'd know it better if you acted more like it."
+
+"I wish now I'd thought," said Tom, with a repentant tone; "but I
+dreamt about you, anyway. That's something, ain't it?"
+
+"It ain't much--a cat does that much--but it's better than nothing.
+What did you dream?"
+
+"Why, Wednesday night I dreamt that you was sitting over there by the
+bed, and Sid was sitting by the woodbox, and Mary next to him."
+
+"Well, so we did. So we always do. I'm glad your dreams could take
+even that much trouble about us."
+
+"And I dreamt that Joe Harper's mother was here."
+
+"Why, she was here! Did you dream any more?"
+
+"Oh, lots. But it's so dim, now."
+
+"Well, try to recollect--can't you?"
+
+"Somehow it seems to me that the wind--the wind blowed the--the--"
+
+"Try harder, Tom! The wind did blow something. Come!"
+
+Tom pressed his fingers on his forehead an anxious minute, and then
+said:
+
+"I've got it now! I've got it now! It blowed the candle!"
+
+"Mercy on us! Go on, Tom--go on!"
+
+"And it seems to me that you said, 'Why, I believe that that door--'"
+
+"Go ON, Tom!"
+
+"Just let me study a moment--just a moment. Oh, yes--you said you
+believed the door was open."
+
+"As I'm sitting here, I did! Didn't I, Mary! Go on!"
+
+"And then--and then--well I won't be certain, but it seems like as if
+you made Sid go and--and--"
+
+"Well? Well? What did I make him do, Tom? What did I make him do?"
+
+"You made him--you--Oh, you made him shut it."
+
+"Well, for the land's sake! I never heard the beat of that in all my
+days! Don't tell ME there ain't anything in dreams, any more. Sereny
+Harper shall know of this before I'm an hour older. I'd like to see her
+get around THIS with her rubbage 'bout superstition. Go on, Tom!"
+
+"Oh, it's all getting just as bright as day, now. Next you said I
+warn't BAD, only mischeevous and harum-scarum, and not any more
+responsible than--than--I think it was a colt, or something."
+
+"And so it was! Well, goodness gracious! Go on, Tom!"
+
+"And then you began to cry."
+
+"So I did. So I did. Not the first time, neither. And then--"
+
+"Then Mrs. Harper she began to cry, and said Joe was just the same,
+and she wished she hadn't whipped him for taking cream when she'd
+throwed it out her own self--"
+
+"Tom! The sperrit was upon you! You was a prophesying--that's what you
+was doing! Land alive, go on, Tom!"
+
+"Then Sid he said--he said--"
+
+"I don't think I said anything," said Sid.
+
+"Yes you did, Sid," said Mary.
+
+"Shut your heads and let Tom go on! What did he say, Tom?"
+
+"He said--I THINK he said he hoped I was better off where I was gone
+to, but if I'd been better sometimes--"
+
+"THERE, d'you hear that! It was his very words!"
+
+"And you shut him up sharp."
+
+"I lay I did! There must 'a' been an angel there. There WAS an angel
+there, somewheres!"
+
+"And Mrs. Harper told about Joe scaring her with a firecracker, and
+you told about Peter and the Painkiller--"
+
+"Just as true as I live!"
+
+"And then there was a whole lot of talk 'bout dragging the river for
+us, and 'bout having the funeral Sunday, and then you and old Miss
+Harper hugged and cried, and she went."
+
+"It happened just so! It happened just so, as sure as I'm a-sitting in
+these very tracks. Tom, you couldn't told it more like if you'd 'a'
+seen it! And then what? Go on, Tom!"
+
+"Then I thought you prayed for me--and I could see you and hear every
+word you said. And you went to bed, and I was so sorry that I took and
+wrote on a piece of sycamore bark, 'We ain't dead--we are only off
+being pirates,' and put it on the table by the candle; and then you
+looked so good, laying there asleep, that I thought I went and leaned
+over and kissed you on the lips."
+
+"Did you, Tom, DID you! I just forgive you everything for that!" And
+she seized the boy in a crushing embrace that made him feel like the
+guiltiest of villains.
+
+"It was very kind, even though it was only a--dream," Sid soliloquized
+just audibly.
+
+"Shut up, Sid! A body does just the same in a dream as he'd do if he
+was awake. Here's a big Milum apple I've been saving for you, Tom, if
+you was ever found again--now go 'long to school. I'm thankful to the
+good God and Father of us all I've got you back, that's long-suffering
+and merciful to them that believe on Him and keep His word, though
+goodness knows I'm unworthy of it, but if only the worthy ones got His
+blessings and had His hand to help them over the rough places, there's
+few enough would smile here or ever enter into His rest when the long
+night comes. Go 'long Sid, Mary, Tom--take yourselves off--you've
+hendered me long enough."
+
+The children left for school, and the old lady to call on Mrs. Harper
+and vanquish her realism with Tom's marvellous dream. Sid had better
+judgment than to utter the thought that was in his mind as he left the
+house. It was this: "Pretty thin--as long a dream as that, without any
+mistakes in it!"
+
+What a hero Tom was become, now! He did not go skipping and prancing,
+but moved with a dignified swagger as became a pirate who felt that the
+public eye was on him. And indeed it was; he tried not to seem to see
+the looks or hear the remarks as he passed along, but they were food
+and drink to him. Smaller boys than himself flocked at his heels, as
+proud to be seen with him, and tolerated by him, as if he had been the
+drummer at the head of a procession or the elephant leading a menagerie
+into town. Boys of his own size pretended not to know he had been away
+at all; but they were consuming with envy, nevertheless. They would
+have given anything to have that swarthy suntanned skin of his, and his
+glittering notoriety; and Tom would not have parted with either for a
+circus.
+
+At school the children made so much of him and of Joe, and delivered
+such eloquent admiration from their eyes, that the two heroes were not
+long in becoming insufferably "stuck-up." They began to tell their
+adventures to hungry listeners--but they only began; it was not a thing
+likely to have an end, with imaginations like theirs to furnish
+material. And finally, when they got out their pipes and went serenely
+puffing around, the very summit of glory was reached.
+
+Tom decided that he could be independent of Becky Thatcher now. Glory
+was sufficient. He would live for glory. Now that he was distinguished,
+maybe she would be wanting to "make up." Well, let her--she should see
+that he could be as indifferent as some other people. Presently she
+arrived. Tom pretended not to see her. He moved away and joined a group
+of boys and girls and began to talk. Soon he observed that she was
+tripping gayly back and forth with flushed face and dancing eyes,
+pretending to be busy chasing schoolmates, and screaming with laughter
+when she made a capture; but he noticed that she always made her
+captures in his vicinity, and that she seemed to cast a conscious eye
+in his direction at such times, too. It gratified all the vicious
+vanity that was in him; and so, instead of winning him, it only "set
+him up" the more and made him the more diligent to avoid betraying that
+he knew she was about. Presently she gave over skylarking, and moved
+irresolutely about, sighing once or twice and glancing furtively and
+wistfully toward Tom. Then she observed that now Tom was talking more
+particularly to Amy Lawrence than to any one else. She felt a sharp
+pang and grew disturbed and uneasy at once. She tried to go away, but
+her feet were treacherous, and carried her to the group instead. She
+said to a girl almost at Tom's elbow--with sham vivacity:
+
+"Why, Mary Austin! you bad girl, why didn't you come to Sunday-school?"
+
+"I did come--didn't you see me?"
+
+"Why, no! Did you? Where did you sit?"
+
+"I was in Miss Peters' class, where I always go. I saw YOU."
+
+"Did you? Why, it's funny I didn't see you. I wanted to tell you about
+the picnic."
+
+"Oh, that's jolly. Who's going to give it?"
+
+"My ma's going to let me have one."
+
+"Oh, goody; I hope she'll let ME come."
+
+"Well, she will. The picnic's for me. She'll let anybody come that I
+want, and I want you."
+
+"That's ever so nice. When is it going to be?"
+
+"By and by. Maybe about vacation."
+
+"Oh, won't it be fun! You going to have all the girls and boys?"
+
+"Yes, every one that's friends to me--or wants to be"; and she glanced
+ever so furtively at Tom, but he talked right along to Amy Lawrence
+about the terrible storm on the island, and how the lightning tore the
+great sycamore tree "all to flinders" while he was "standing within
+three feet of it."
+
+"Oh, may I come?" said Grace Miller.
+
+"Yes."
+
+"And me?" said Sally Rogers.
+
+"Yes."
+
+"And me, too?" said Susy Harper. "And Joe?"
+
+"Yes."
+
+And so on, with clapping of joyful hands till all the group had begged
+for invitations but Tom and Amy. Then Tom turned coolly away, still
+talking, and took Amy with him. Becky's lips trembled and the tears
+came to her eyes; she hid these signs with a forced gayety and went on
+chattering, but the life had gone out of the picnic, now, and out of
+everything else; she got away as soon as she could and hid herself and
+had what her sex call "a good cry." Then she sat moody, with wounded
+pride, till the bell rang. She roused up, now, with a vindictive cast
+in her eye, and gave her plaited tails a shake and said she knew what
+SHE'D do.
+
+At recess Tom continued his flirtation with Amy with jubilant
+self-satisfaction. And he kept drifting about to find Becky and lacerate
+her with the performance. At last he spied her, but there was a sudden
+falling of his mercury. She was sitting cosily on a little bench behind
+the schoolhouse looking at a picture-book with Alfred Temple--and so
+absorbed were they, and their heads so close together over the book,
+that they did not seem to be conscious of anything in the world besides.
+Jealousy ran red-hot through Tom's veins. He began to hate himself for
+throwing away the chance Becky had offered for a reconciliation. He
+called himself a fool, and all the hard names he could think of. He
+wanted to cry with vexation. Amy chatted happily along, as they walked,
+for her heart was singing, but Tom's tongue had lost its function. He
+did not hear what Amy was saying, and whenever she paused expectantly he
+could only stammer an awkward assent, which was as often misplaced as
+otherwise. He kept drifting to the rear of the schoolhouse, again and
+again, to sear his eyeballs with the hateful spectacle there. He could
+not help it. And it maddened him to see, as he thought he saw, that
+Becky Thatcher never once suspected that he was even in the land of the
+living. But she did see, nevertheless; and she knew she was winning her
+fight, too, and was glad to see him suffer as she had suffered.
+
+Amy's happy prattle became intolerable. Tom hinted at things he had to
+attend to; things that must be done; and time was fleeting. But in
+vain--the girl chirped on. Tom thought, "Oh, hang her, ain't I ever
+going to get rid of her?" At last he must be attending to those
+things--and she said artlessly that she would be "around" when school
+let out. And he hastened away, hating her for it.
+
+"Any other boy!" Tom thought, grating his teeth. "Any boy in the whole
+town but that Saint Louis smarty that thinks he dresses so fine and is
+aristocracy! Oh, all right, I licked you the first day you ever saw
+this town, mister, and I'll lick you again! You just wait till I catch
+you out! I'll just take and--"
+
+And he went through the motions of thrashing an imaginary boy
+--pummelling the air, and kicking and gouging. "Oh, you do, do you? You
+holler 'nough, do you? Now, then, let that learn you!" And so the
+imaginary flogging was finished to his satisfaction.
+
+Tom fled home at noon. His conscience could not endure any more of
+Amy's grateful happiness, and his jealousy could bear no more of the
+other distress. Becky resumed her picture inspections with Alfred, but
+as the minutes dragged along and no Tom came to suffer, her triumph
+began to cloud and she lost interest; gravity and absent-mindedness
+followed, and then melancholy; two or three times she pricked up her
+ear at a footstep, but it was a false hope; no Tom came. At last she
+grew entirely miserable and wished she hadn't carried it so far. When
+poor Alfred, seeing that he was losing her, he did not know how, kept
+exclaiming: "Oh, here's a jolly one! look at this!" she lost patience
+at last, and said, "Oh, don't bother me! I don't care for them!" and
+burst into tears, and got up and walked away.
+
+Alfred dropped alongside and was going to try to comfort her, but she
+said:
+
+"Go away and leave me alone, can't you! I hate you!"
+
+So the boy halted, wondering what he could have done--for she had said
+she would look at pictures all through the nooning--and she walked on,
+crying. Then Alfred went musing into the deserted schoolhouse. He was
+humiliated and angry. He easily guessed his way to the truth--the girl
+had simply made a convenience of him to vent her spite upon Tom Sawyer.
+He was far from hating Tom the less when this thought occurred to him.
+He wished there was some way to get that boy into trouble without much
+risk to himself. Tom's spelling-book fell under his eye. Here was his
+opportunity. He gratefully opened to the lesson for the afternoon and
+poured ink upon the page.
+
+Becky, glancing in at a window behind him at the moment, saw the act,
+and moved on, without discovering herself. She started homeward, now,
+intending to find Tom and tell him; Tom would be thankful and their
+troubles would be healed. Before she was half way home, however, she
+had changed her mind. The thought of Tom's treatment of her when she
+was talking about her picnic came scorching back and filled her with
+shame. She resolved to let him get whipped on the damaged
+spelling-book's account, and to hate him forever, into the bargain.
+
+
+
+CHAPTER XIX
+
+TOM arrived at home in a dreary mood, and the first thing his aunt
+said to him showed him that he had brought his sorrows to an
+unpromising market:
+
+"Tom, I've a notion to skin you alive!"
+
+"Auntie, what have I done?"
+
+"Well, you've done enough. Here I go over to Sereny Harper, like an
+old softy, expecting I'm going to make her believe all that rubbage
+about that dream, when lo and behold you she'd found out from Joe that
+you was over here and heard all the talk we had that night. Tom, I
+don't know what is to become of a boy that will act like that. It makes
+me feel so bad to think you could let me go to Sereny Harper and make
+such a fool of myself and never say a word."
+
+This was a new aspect of the thing. His smartness of the morning had
+seemed to Tom a good joke before, and very ingenious. It merely looked
+mean and shabby now. He hung his head and could not think of anything
+to say for a moment. Then he said:
+
+"Auntie, I wish I hadn't done it--but I didn't think."
+
+"Oh, child, you never think. You never think of anything but your own
+selfishness. You could think to come all the way over here from
+Jackson's Island in the night to laugh at our troubles, and you could
+think to fool me with a lie about a dream; but you couldn't ever think
+to pity us and save us from sorrow."
+
+"Auntie, I know now it was mean, but I didn't mean to be mean. I
+didn't, honest. And besides, I didn't come over here to laugh at you
+that night."
+
+"What did you come for, then?"
+
+"It was to tell you not to be uneasy about us, because we hadn't got
+drownded."
+
+"Tom, Tom, I would be the thankfullest soul in this world if I could
+believe you ever had as good a thought as that, but you know you never
+did--and I know it, Tom."
+
+"Indeed and 'deed I did, auntie--I wish I may never stir if I didn't."
+
+"Oh, Tom, don't lie--don't do it. It only makes things a hundred times
+worse."
+
+"It ain't a lie, auntie; it's the truth. I wanted to keep you from
+grieving--that was all that made me come."
+
+"I'd give the whole world to believe that--it would cover up a power
+of sins, Tom. I'd 'most be glad you'd run off and acted so bad. But it
+ain't reasonable; because, why didn't you tell me, child?"
+
+"Why, you see, when you got to talking about the funeral, I just got
+all full of the idea of our coming and hiding in the church, and I
+couldn't somehow bear to spoil it. So I just put the bark back in my
+pocket and kept mum."
+
+"What bark?"
+
+"The bark I had wrote on to tell you we'd gone pirating. I wish, now,
+you'd waked up when I kissed you--I do, honest."
+
+The hard lines in his aunt's face relaxed and a sudden tenderness
+dawned in her eyes.
+
+"DID you kiss me, Tom?"
+
+"Why, yes, I did."
+
+"Are you sure you did, Tom?"
+
+"Why, yes, I did, auntie--certain sure."
+
+"What did you kiss me for, Tom?"
+
+"Because I loved you so, and you laid there moaning and I was so sorry."
+
+The words sounded like truth. The old lady could not hide a tremor in
+her voice when she said:
+
+"Kiss me again, Tom!--and be off with you to school, now, and don't
+bother me any more."
+
+The moment he was gone, she ran to a closet and got out the ruin of a
+jacket which Tom had gone pirating in. Then she stopped, with it in her
+hand, and said to herself:
+
+"No, I don't dare. Poor boy, I reckon he's lied about it--but it's a
+blessed, blessed lie, there's such a comfort come from it. I hope the
+Lord--I KNOW the Lord will forgive him, because it was such
+goodheartedness in him to tell it. But I don't want to find out it's a
+lie. I won't look."
+
+She put the jacket away, and stood by musing a minute. Twice she put
+out her hand to take the garment again, and twice she refrained. Once
+more she ventured, and this time she fortified herself with the
+thought: "It's a good lie--it's a good lie--I won't let it grieve me."
+So she sought the jacket pocket. A moment later she was reading Tom's
+piece of bark through flowing tears and saying: "I could forgive the
+boy, now, if he'd committed a million sins!"
+
+
+
+CHAPTER XX
+
+THERE was something about Aunt Polly's manner, when she kissed Tom,
+that swept away his low spirits and made him lighthearted and happy
+again. He started to school and had the luck of coming upon Becky
+Thatcher at the head of Meadow Lane. His mood always determined his
+manner. Without a moment's hesitation he ran to her and said:
+
+"I acted mighty mean to-day, Becky, and I'm so sorry. I won't ever,
+ever do that way again, as long as ever I live--please make up, won't
+you?"
+
+The girl stopped and looked him scornfully in the face:
+
+"I'll thank you to keep yourself TO yourself, Mr. Thomas Sawyer. I'll
+never speak to you again."
+
+She tossed her head and passed on. Tom was so stunned that he had not
+even presence of mind enough to say "Who cares, Miss Smarty?" until the
+right time to say it had gone by. So he said nothing. But he was in a
+fine rage, nevertheless. He moped into the schoolyard wishing she were
+a boy, and imagining how he would trounce her if she were. He presently
+encountered her and delivered a stinging remark as he passed. She
+hurled one in return, and the angry breach was complete. It seemed to
+Becky, in her hot resentment, that she could hardly wait for school to
+"take in," she was so impatient to see Tom flogged for the injured
+spelling-book. If she had had any lingering notion of exposing Alfred
+Temple, Tom's offensive fling had driven it entirely away.
+
+Poor girl, she did not know how fast she was nearing trouble herself.
+The master, Mr. Dobbins, had reached middle age with an unsatisfied
+ambition. The darling of his desires was, to be a doctor, but poverty
+had decreed that he should be nothing higher than a village
+schoolmaster. Every day he took a mysterious book out of his desk and
+absorbed himself in it at times when no classes were reciting. He kept
+that book under lock and key. There was not an urchin in school but was
+perishing to have a glimpse of it, but the chance never came. Every boy
+and girl had a theory about the nature of that book; but no two
+theories were alike, and there was no way of getting at the facts in
+the case. Now, as Becky was passing by the desk, which stood near the
+door, she noticed that the key was in the lock! It was a precious
+moment. She glanced around; found herself alone, and the next instant
+she had the book in her hands. The title-page--Professor Somebody's
+ANATOMY--carried no information to her mind; so she began to turn the
+leaves. She came at once upon a handsomely engraved and colored
+frontispiece--a human figure, stark naked. At that moment a shadow fell
+on the page and Tom Sawyer stepped in at the door and caught a glimpse
+of the picture. Becky snatched at the book to close it, and had the
+hard luck to tear the pictured page half down the middle. She thrust
+the volume into the desk, turned the key, and burst out crying with
+shame and vexation.
+
+"Tom Sawyer, you are just as mean as you can be, to sneak up on a
+person and look at what they're looking at."
+
+"How could I know you was looking at anything?"
+
+"You ought to be ashamed of yourself, Tom Sawyer; you know you're
+going to tell on me, and oh, what shall I do, what shall I do! I'll be
+whipped, and I never was whipped in school."
+
+Then she stamped her little foot and said:
+
+"BE so mean if you want to! I know something that's going to happen.
+You just wait and you'll see! Hateful, hateful, hateful!"--and she
+flung out of the house with a new explosion of crying.
+
+Tom stood still, rather flustered by this onslaught. Presently he said
+to himself:
+
+"What a curious kind of a fool a girl is! Never been licked in school!
+Shucks! What's a licking! That's just like a girl--they're so
+thin-skinned and chicken-hearted. Well, of course I ain't going to tell
+old Dobbins on this little fool, because there's other ways of getting
+even on her, that ain't so mean; but what of it? Old Dobbins will ask
+who it was tore his book. Nobody'll answer. Then he'll do just the way
+he always does--ask first one and then t'other, and when he comes to the
+right girl he'll know it, without any telling. Girls' faces always tell
+on them. They ain't got any backbone. She'll get licked. Well, it's a
+kind of a tight place for Becky Thatcher, because there ain't any way
+out of it." Tom conned the thing a moment longer, and then added: "All
+right, though; she'd like to see me in just such a fix--let her sweat it
+out!"
+
+Tom joined the mob of skylarking scholars outside. In a few moments
+the master arrived and school "took in." Tom did not feel a strong
+interest in his studies. Every time he stole a glance at the girls'
+side of the room Becky's face troubled him. Considering all things, he
+did not want to pity her, and yet it was all he could do to help it. He
+could get up no exultation that was really worthy the name. Presently
+the spelling-book discovery was made, and Tom's mind was entirely full
+of his own matters for a while after that. Becky roused up from her
+lethargy of distress and showed good interest in the proceedings. She
+did not expect that Tom could get out of his trouble by denying that he
+spilt the ink on the book himself; and she was right. The denial only
+seemed to make the thing worse for Tom. Becky supposed she would be
+glad of that, and she tried to believe she was glad of it, but she
+found she was not certain. When the worst came to the worst, she had an
+impulse to get up and tell on Alfred Temple, but she made an effort and
+forced herself to keep still--because, said she to herself, "he'll tell
+about me tearing the picture sure. I wouldn't say a word, not to save
+his life!"
+
+Tom took his whipping and went back to his seat not at all
+broken-hearted, for he thought it was possible that he had unknowingly
+upset the ink on the spelling-book himself, in some skylarking bout--he
+had denied it for form's sake and because it was custom, and had stuck
+to the denial from principle.
+
+A whole hour drifted by, the master sat nodding in his throne, the air
+was drowsy with the hum of study. By and by, Mr. Dobbins straightened
+himself up, yawned, then unlocked his desk, and reached for his book,
+but seemed undecided whether to take it out or leave it. Most of the
+pupils glanced up languidly, but there were two among them that watched
+his movements with intent eyes. Mr. Dobbins fingered his book absently
+for a while, then took it out and settled himself in his chair to read!
+Tom shot a glance at Becky. He had seen a hunted and helpless rabbit
+look as she did, with a gun levelled at its head. Instantly he forgot
+his quarrel with her. Quick--something must be done! done in a flash,
+too! But the very imminence of the emergency paralyzed his invention.
+Good!--he had an inspiration! He would run and snatch the book, spring
+through the door and fly. But his resolution shook for one little
+instant, and the chance was lost--the master opened the volume. If Tom
+only had the wasted opportunity back again! Too late. There was no help
+for Becky now, he said. The next moment the master faced the school.
+Every eye sank under his gaze. There was that in it which smote even
+the innocent with fear. There was silence while one might count ten
+--the master was gathering his wrath. Then he spoke: "Who tore this book?"
+
+There was not a sound. One could have heard a pin drop. The stillness
+continued; the master searched face after face for signs of guilt.
+
+"Benjamin Rogers, did you tear this book?"
+
+A denial. Another pause.
+
+"Joseph Harper, did you?"
+
+Another denial. Tom's uneasiness grew more and more intense under the
+slow torture of these proceedings. The master scanned the ranks of
+boys--considered a while, then turned to the girls:
+
+"Amy Lawrence?"
+
+A shake of the head.
+
+"Gracie Miller?"
+
+The same sign.
+
+"Susan Harper, did you do this?"
+
+Another negative. The next girl was Becky Thatcher. Tom was trembling
+from head to foot with excitement and a sense of the hopelessness of
+the situation.
+
+"Rebecca Thatcher" [Tom glanced at her face--it was white with terror]
+--"did you tear--no, look me in the face" [her hands rose in appeal]
+--"did you tear this book?"
+
+A thought shot like lightning through Tom's brain. He sprang to his
+feet and shouted--"I done it!"
+
+The school stared in perplexity at this incredible folly. Tom stood a
+moment, to gather his dismembered faculties; and when he stepped
+forward to go to his punishment the surprise, the gratitude, the
+adoration that shone upon him out of poor Becky's eyes seemed pay
+enough for a hundred floggings. Inspired by the splendor of his own
+act, he took without an outcry the most merciless flaying that even Mr.
+Dobbins had ever administered; and also received with indifference the
+added cruelty of a command to remain two hours after school should be
+dismissed--for he knew who would wait for him outside till his
+captivity was done, and not count the tedious time as loss, either.
+
+Tom went to bed that night planning vengeance against Alfred Temple;
+for with shame and repentance Becky had told him all, not forgetting
+her own treachery; but even the longing for vengeance had to give way,
+soon, to pleasanter musings, and he fell asleep at last with Becky's
+latest words lingering dreamily in his ear--
+
+"Tom, how COULD you be so noble!"
+
+
+
+CHAPTER XXI
+
+VACATION was approaching. The schoolmaster, always severe, grew
+severer and more exacting than ever, for he wanted the school to make a
+good showing on "Examination" day. His rod and his ferule were seldom
+idle now--at least among the smaller pupils. Only the biggest boys, and
+young ladies of eighteen and twenty, escaped lashing. Mr. Dobbins'
+lashings were very vigorous ones, too; for although he carried, under
+his wig, a perfectly bald and shiny head, he had only reached middle
+age, and there was no sign of feebleness in his muscle. As the great
+day approached, all the tyranny that was in him came to the surface; he
+seemed to take a vindictive pleasure in punishing the least
+shortcomings. The consequence was, that the smaller boys spent their
+days in terror and suffering and their nights in plotting revenge. They
+threw away no opportunity to do the master a mischief. But he kept
+ahead all the time. The retribution that followed every vengeful
+success was so sweeping and majestic that the boys always retired from
+the field badly worsted. At last they conspired together and hit upon a
+plan that promised a dazzling victory. They swore in the sign-painter's
+boy, told him the scheme, and asked his help. He had his own reasons
+for being delighted, for the master boarded in his father's family and
+had given the boy ample cause to hate him. The master's wife would go
+on a visit to the country in a few days, and there would be nothing to
+interfere with the plan; the master always prepared himself for great
+occasions by getting pretty well fuddled, and the sign-painter's boy
+said that when the dominie had reached the proper condition on
+Examination Evening he would "manage the thing" while he napped in his
+chair; then he would have him awakened at the right time and hurried
+away to school.
+
+In the fulness of time the interesting occasion arrived. At eight in
+the evening the schoolhouse was brilliantly lighted, and adorned with
+wreaths and festoons of foliage and flowers. The master sat throned in
+his great chair upon a raised platform, with his blackboard behind him.
+He was looking tolerably mellow. Three rows of benches on each side and
+six rows in front of him were occupied by the dignitaries of the town
+and by the parents of the pupils. To his left, back of the rows of
+citizens, was a spacious temporary platform upon which were seated the
+scholars who were to take part in the exercises of the evening; rows of
+small boys, washed and dressed to an intolerable state of discomfort;
+rows of gawky big boys; snowbanks of girls and young ladies clad in
+lawn and muslin and conspicuously conscious of their bare arms, their
+grandmothers' ancient trinkets, their bits of pink and blue ribbon and
+the flowers in their hair. All the rest of the house was filled with
+non-participating scholars.
+
+The exercises began. A very little boy stood up and sheepishly
+recited, "You'd scarce expect one of my age to speak in public on the
+stage," etc.--accompanying himself with the painfully exact and
+spasmodic gestures which a machine might have used--supposing the
+machine to be a trifle out of order. But he got through safely, though
+cruelly scared, and got a fine round of applause when he made his
+manufactured bow and retired.
+
+A little shamefaced girl lisped, "Mary had a little lamb," etc.,
+performed a compassion-inspiring curtsy, got her meed of applause, and
+sat down flushed and happy.
+
+Tom Sawyer stepped forward with conceited confidence and soared into
+the unquenchable and indestructible "Give me liberty or give me death"
+speech, with fine fury and frantic gesticulation, and broke down in the
+middle of it. A ghastly stage-fright seized him, his legs quaked under
+him and he was like to choke. True, he had the manifest sympathy of the
+house but he had the house's silence, too, which was even worse than
+its sympathy. The master frowned, and this completed the disaster. Tom
+struggled awhile and then retired, utterly defeated. There was a weak
+attempt at applause, but it died early.
+
+"The Boy Stood on the Burning Deck" followed; also "The Assyrian Came
+Down," and other declamatory gems. Then there were reading exercises,
+and a spelling fight. The meagre Latin class recited with honor. The
+prime feature of the evening was in order, now--original "compositions"
+by the young ladies. Each in her turn stepped forward to the edge of
+the platform, cleared her throat, held up her manuscript (tied with
+dainty ribbon), and proceeded to read, with labored attention to
+"expression" and punctuation. The themes were the same that had been
+illuminated upon similar occasions by their mothers before them, their
+grandmothers, and doubtless all their ancestors in the female line
+clear back to the Crusades. "Friendship" was one; "Memories of Other
+Days"; "Religion in History"; "Dream Land"; "The Advantages of
+Culture"; "Forms of Political Government Compared and Contrasted";
+"Melancholy"; "Filial Love"; "Heart Longings," etc., etc.
+
+A prevalent feature in these compositions was a nursed and petted
+melancholy; another was a wasteful and opulent gush of "fine language";
+another was a tendency to lug in by the ears particularly prized words
+and phrases until they were worn entirely out; and a peculiarity that
+conspicuously marked and marred them was the inveterate and intolerable
+sermon that wagged its crippled tail at the end of each and every one
+of them. No matter what the subject might be, a brain-racking effort
+was made to squirm it into some aspect or other that the moral and
+religious mind could contemplate with edification. The glaring
+insincerity of these sermons was not sufficient to compass the
+banishment of the fashion from the schools, and it is not sufficient
+to-day; it never will be sufficient while the world stands, perhaps.
+There is no school in all our land where the young ladies do not feel
+obliged to close their compositions with a sermon; and you will find
+that the sermon of the most frivolous and the least religious girl in
+the school is always the longest and the most relentlessly pious. But
+enough of this. Homely truth is unpalatable.
+
+Let us return to the "Examination." The first composition that was
+read was one entitled "Is this, then, Life?" Perhaps the reader can
+endure an extract from it:
+
+ "In the common walks of life, with what delightful
+ emotions does the youthful mind look forward to some
+ anticipated scene of festivity! Imagination is busy
+ sketching rose-tinted pictures of joy. In fancy, the
+ voluptuous votary of fashion sees herself amid the
+ festive throng, 'the observed of all observers.' Her
+ graceful form, arrayed in snowy robes, is whirling
+ through the mazes of the joyous dance; her eye is
+ brightest, her step is lightest in the gay assembly.
+
+ "In such delicious fancies time quickly glides by,
+ and the welcome hour arrives for her entrance into
+ the Elysian world, of which she has had such bright
+ dreams. How fairy-like does everything appear to
+ her enchanted vision! Each new scene is more charming
+ than the last. But after a while she finds that
+ beneath this goodly exterior, all is vanity, the
+ flattery which once charmed her soul, now grates
+ harshly upon her ear; the ball-room has lost its
+ charms; and with wasted health and imbittered heart,
+ she turns away with the conviction that earthly
+ pleasures cannot satisfy the longings of the soul!"
+
+And so forth and so on. There was a buzz of gratification from time to
+time during the reading, accompanied by whispered ejaculations of "How
+sweet!" "How eloquent!" "So true!" etc., and after the thing had closed
+with a peculiarly afflicting sermon the applause was enthusiastic.
+
+Then arose a slim, melancholy girl, whose face had the "interesting"
+paleness that comes of pills and indigestion, and read a "poem." Two
+stanzas of it will do:
+
+ "A MISSOURI MAIDEN'S FAREWELL TO ALABAMA
+
+ "Alabama, good-bye! I love thee well!
+ But yet for a while do I leave thee now!
+ Sad, yes, sad thoughts of thee my heart doth swell,
+ And burning recollections throng my brow!
+ For I have wandered through thy flowery woods;
+ Have roamed and read near Tallapoosa's stream;
+ Have listened to Tallassee's warring floods,
+ And wooed on Coosa's side Aurora's beam.
+
+ "Yet shame I not to bear an o'er-full heart,
+ Nor blush to turn behind my tearful eyes;
+ 'Tis from no stranger land I now must part,
+ 'Tis to no strangers left I yield these sighs.
+ Welcome and home were mine within this State,
+ Whose vales I leave--whose spires fade fast from me
+ And cold must be mine eyes, and heart, and tete,
+ When, dear Alabama! they turn cold on thee!"
+
+There were very few there who knew what "tete" meant, but the poem was
+very satisfactory, nevertheless.
+
+Next appeared a dark-complexioned, black-eyed, black-haired young
+lady, who paused an impressive moment, assumed a tragic expression, and
+began to read in a measured, solemn tone:
+
+ "A VISION
+
+ "Dark and tempestuous was night. Around the
+ throne on high not a single star quivered; but
+ the deep intonations of the heavy thunder
+ constantly vibrated upon the ear; whilst the
+ terrific lightning revelled in angry mood
+ through the cloudy chambers of heaven, seeming
+ to scorn the power exerted over its terror by
+ the illustrious Franklin! Even the boisterous
+ winds unanimously came forth from their mystic
+ homes, and blustered about as if to enhance by
+ their aid the wildness of the scene.
+
+ "At such a time, so dark, so dreary, for human
+ sympathy my very spirit sighed; but instead thereof,
+
+ "'My dearest friend, my counsellor, my comforter
+ and guide--My joy in grief, my second bliss
+ in joy,' came to my side. She moved like one of
+ those bright beings pictured in the sunny walks
+ of fancy's Eden by the romantic and young, a
+ queen of beauty unadorned save by her own
+ transcendent loveliness. So soft was her step, it
+ failed to make even a sound, and but for the
+ magical thrill imparted by her genial touch, as
+ other unobtrusive beauties, she would have glided
+ away un-perceived--unsought. A strange sadness
+ rested upon her features, like icy tears upon
+ the robe of December, as she pointed to the
+ contending elements without, and bade me contemplate
+ the two beings presented."
+
+This nightmare occupied some ten pages of manuscript and wound up with
+a sermon so destructive of all hope to non-Presbyterians that it took
+the first prize. This composition was considered to be the very finest
+effort of the evening. The mayor of the village, in delivering the
+prize to the author of it, made a warm speech in which he said that it
+was by far the most "eloquent" thing he had ever listened to, and that
+Daniel Webster himself might well be proud of it.
+
+It may be remarked, in passing, that the number of compositions in
+which the word "beauteous" was over-fondled, and human experience
+referred to as "life's page," was up to the usual average.
+
+Now the master, mellow almost to the verge of geniality, put his chair
+aside, turned his back to the audience, and began to draw a map of
+America on the blackboard, to exercise the geography class upon. But he
+made a sad business of it with his unsteady hand, and a smothered
+titter rippled over the house. He knew what the matter was, and set
+himself to right it. He sponged out lines and remade them; but he only
+distorted them more than ever, and the tittering was more pronounced.
+He threw his entire attention upon his work, now, as if determined not
+to be put down by the mirth. He felt that all eyes were fastened upon
+him; he imagined he was succeeding, and yet the tittering continued; it
+even manifestly increased. And well it might. There was a garret above,
+pierced with a scuttle over his head; and down through this scuttle
+came a cat, suspended around the haunches by a string; she had a rag
+tied about her head and jaws to keep her from mewing; as she slowly
+descended she curved upward and clawed at the string, she swung
+downward and clawed at the intangible air. The tittering rose higher
+and higher--the cat was within six inches of the absorbed teacher's
+head--down, down, a little lower, and she grabbed his wig with her
+desperate claws, clung to it, and was snatched up into the garret in an
+instant with her trophy still in her possession! And how the light did
+blaze abroad from the master's bald pate--for the sign-painter's boy
+had GILDED it!
+
+That broke up the meeting. The boys were avenged. Vacation had come.
+
+ NOTE:--The pretended "compositions" quoted in
+ this chapter are taken without alteration from a
+ volume entitled "Prose and Poetry, by a Western
+ Lady"--but they are exactly and precisely after
+ the schoolgirl pattern, and hence are much
+ happier than any mere imitations could be.
+
+
+
+CHAPTER XXII
+
+TOM joined the new order of Cadets of Temperance, being attracted by
+the showy character of their "regalia." He promised to abstain from
+smoking, chewing, and profanity as long as he remained a member. Now he
+found out a new thing--namely, that to promise not to do a thing is the
+surest way in the world to make a body want to go and do that very
+thing. Tom soon found himself tormented with a desire to drink and
+swear; the desire grew to be so intense that nothing but the hope of a
+chance to display himself in his red sash kept him from withdrawing
+from the order. Fourth of July was coming; but he soon gave that up
+--gave it up before he had worn his shackles over forty-eight hours--and
+fixed his hopes upon old Judge Frazer, justice of the peace, who was
+apparently on his deathbed and would have a big public funeral, since
+he was so high an official. During three days Tom was deeply concerned
+about the Judge's condition and hungry for news of it. Sometimes his
+hopes ran high--so high that he would venture to get out his regalia
+and practise before the looking-glass. But the Judge had a most
+discouraging way of fluctuating. At last he was pronounced upon the
+mend--and then convalescent. Tom was disgusted; and felt a sense of
+injury, too. He handed in his resignation at once--and that night the
+Judge suffered a relapse and died. Tom resolved that he would never
+trust a man like that again.
+
+The funeral was a fine thing. The Cadets paraded in a style calculated
+to kill the late member with envy. Tom was a free boy again, however
+--there was something in that. He could drink and swear, now--but found
+to his surprise that he did not want to. The simple fact that he could,
+took the desire away, and the charm of it.
+
+Tom presently wondered to find that his coveted vacation was beginning
+to hang a little heavily on his hands.
+
+He attempted a diary--but nothing happened during three days, and so
+he abandoned it.
+
+The first of all the negro minstrel shows came to town, and made a
+sensation. Tom and Joe Harper got up a band of performers and were
+happy for two days.
+
+Even the Glorious Fourth was in some sense a failure, for it rained
+hard, there was no procession in consequence, and the greatest man in
+the world (as Tom supposed), Mr. Benton, an actual United States
+Senator, proved an overwhelming disappointment--for he was not
+twenty-five feet high, nor even anywhere in the neighborhood of it.
+
+A circus came. The boys played circus for three days afterward in
+tents made of rag carpeting--admission, three pins for boys, two for
+girls--and then circusing was abandoned.
+
+A phrenologist and a mesmerizer came--and went again and left the
+village duller and drearier than ever.
+
+There were some boys-and-girls' parties, but they were so few and so
+delightful that they only made the aching voids between ache the harder.
+
+Becky Thatcher was gone to her Constantinople home to stay with her
+parents during vacation--so there was no bright side to life anywhere.
+
+The dreadful secret of the murder was a chronic misery. It was a very
+cancer for permanency and pain.
+
+Then came the measles.
+
+During two long weeks Tom lay a prisoner, dead to the world and its
+happenings. He was very ill, he was interested in nothing. When he got
+upon his feet at last and moved feebly down-town, a melancholy change
+had come over everything and every creature. There had been a
+"revival," and everybody had "got religion," not only the adults, but
+even the boys and girls. Tom went about, hoping against hope for the
+sight of one blessed sinful face, but disappointment crossed him
+everywhere. He found Joe Harper studying a Testament, and turned sadly
+away from the depressing spectacle. He sought Ben Rogers, and found him
+visiting the poor with a basket of tracts. He hunted up Jim Hollis, who
+called his attention to the precious blessing of his late measles as a
+warning. Every boy he encountered added another ton to his depression;
+and when, in desperation, he flew for refuge at last to the bosom of
+Huckleberry Finn and was received with a Scriptural quotation, his
+heart broke and he crept home and to bed realizing that he alone of all
+the town was lost, forever and forever.
+
+And that night there came on a terrific storm, with driving rain,
+awful claps of thunder and blinding sheets of lightning. He covered his
+head with the bedclothes and waited in a horror of suspense for his
+doom; for he had not the shadow of a doubt that all this hubbub was
+about him. He believed he had taxed the forbearance of the powers above
+to the extremity of endurance and that this was the result. It might
+have seemed to him a waste of pomp and ammunition to kill a bug with a
+battery of artillery, but there seemed nothing incongruous about the
+getting up such an expensive thunderstorm as this to knock the turf
+from under an insect like himself.
+
+By and by the tempest spent itself and died without accomplishing its
+object. The boy's first impulse was to be grateful, and reform. His
+second was to wait--for there might not be any more storms.
+
+The next day the doctors were back; Tom had relapsed. The three weeks
+he spent on his back this time seemed an entire age. When he got abroad
+at last he was hardly grateful that he had been spared, remembering how
+lonely was his estate, how companionless and forlorn he was. He drifted
+listlessly down the street and found Jim Hollis acting as judge in a
+juvenile court that was trying a cat for murder, in the presence of her
+victim, a bird. He found Joe Harper and Huck Finn up an alley eating a
+stolen melon. Poor lads! they--like Tom--had suffered a relapse.
+
+
+
+CHAPTER XXIII
+
+AT last the sleepy atmosphere was stirred--and vigorously: the murder
+trial came on in the court. It became the absorbing topic of village
+talk immediately. Tom could not get away from it. Every reference to
+the murder sent a shudder to his heart, for his troubled conscience and
+fears almost persuaded him that these remarks were put forth in his
+hearing as "feelers"; he did not see how he could be suspected of
+knowing anything about the murder, but still he could not be
+comfortable in the midst of this gossip. It kept him in a cold shiver
+all the time. He took Huck to a lonely place to have a talk with him.
+It would be some relief to unseal his tongue for a little while; to
+divide his burden of distress with another sufferer. Moreover, he
+wanted to assure himself that Huck had remained discreet.
+
+"Huck, have you ever told anybody about--that?"
+
+"'Bout what?"
+
+"You know what."
+
+"Oh--'course I haven't."
+
+"Never a word?"
+
+"Never a solitary word, so help me. What makes you ask?"
+
+"Well, I was afeard."
+
+"Why, Tom Sawyer, we wouldn't be alive two days if that got found out.
+YOU know that."
+
+Tom felt more comfortable. After a pause:
+
+"Huck, they couldn't anybody get you to tell, could they?"
+
+"Get me to tell? Why, if I wanted that half-breed devil to drownd me
+they could get me to tell. They ain't no different way."
+
+"Well, that's all right, then. I reckon we're safe as long as we keep
+mum. But let's swear again, anyway. It's more surer."
+
+"I'm agreed."
+
+So they swore again with dread solemnities.
+
+"What is the talk around, Huck? I've heard a power of it."
+
+"Talk? Well, it's just Muff Potter, Muff Potter, Muff Potter all the
+time. It keeps me in a sweat, constant, so's I want to hide som'ers."
+
+"That's just the same way they go on round me. I reckon he's a goner.
+Don't you feel sorry for him, sometimes?"
+
+"Most always--most always. He ain't no account; but then he hain't
+ever done anything to hurt anybody. Just fishes a little, to get money
+to get drunk on--and loafs around considerable; but lord, we all do
+that--leastways most of us--preachers and such like. But he's kind of
+good--he give me half a fish, once, when there warn't enough for two;
+and lots of times he's kind of stood by me when I was out of luck."
+
+"Well, he's mended kites for me, Huck, and knitted hooks on to my
+line. I wish we could get him out of there."
+
+"My! we couldn't get him out, Tom. And besides, 'twouldn't do any
+good; they'd ketch him again."
+
+"Yes--so they would. But I hate to hear 'em abuse him so like the
+dickens when he never done--that."
+
+"I do too, Tom. Lord, I hear 'em say he's the bloodiest looking
+villain in this country, and they wonder he wasn't ever hung before."
+
+"Yes, they talk like that, all the time. I've heard 'em say that if he
+was to get free they'd lynch him."
+
+"And they'd do it, too."
+
+The boys had a long talk, but it brought them little comfort. As the
+twilight drew on, they found themselves hanging about the neighborhood
+of the little isolated jail, perhaps with an undefined hope that
+something would happen that might clear away their difficulties. But
+nothing happened; there seemed to be no angels or fairies interested in
+this luckless captive.
+
+The boys did as they had often done before--went to the cell grating
+and gave Potter some tobacco and matches. He was on the ground floor
+and there were no guards.
+
+His gratitude for their gifts had always smote their consciences
+before--it cut deeper than ever, this time. They felt cowardly and
+treacherous to the last degree when Potter said:
+
+"You've been mighty good to me, boys--better'n anybody else in this
+town. And I don't forget it, I don't. Often I says to myself, says I,
+'I used to mend all the boys' kites and things, and show 'em where the
+good fishin' places was, and befriend 'em what I could, and now they've
+all forgot old Muff when he's in trouble; but Tom don't, and Huck
+don't--THEY don't forget him, says I, 'and I don't forget them.' Well,
+boys, I done an awful thing--drunk and crazy at the time--that's the
+only way I account for it--and now I got to swing for it, and it's
+right. Right, and BEST, too, I reckon--hope so, anyway. Well, we won't
+talk about that. I don't want to make YOU feel bad; you've befriended
+me. But what I want to say, is, don't YOU ever get drunk--then you won't
+ever get here. Stand a litter furder west--so--that's it; it's a prime
+comfort to see faces that's friendly when a body's in such a muck of
+trouble, and there don't none come here but yourn. Good friendly
+faces--good friendly faces. Git up on one another's backs and let me
+touch 'em. That's it. Shake hands--yourn'll come through the bars, but
+mine's too big. Little hands, and weak--but they've helped Muff Potter
+a power, and they'd help him more if they could."
+
+Tom went home miserable, and his dreams that night were full of
+horrors. The next day and the day after, he hung about the court-room,
+drawn by an almost irresistible impulse to go in, but forcing himself
+to stay out. Huck was having the same experience. They studiously
+avoided each other. Each wandered away, from time to time, but the same
+dismal fascination always brought them back presently. Tom kept his
+ears open when idlers sauntered out of the court-room, but invariably
+heard distressing news--the toils were closing more and more
+relentlessly around poor Potter. At the end of the second day the
+village talk was to the effect that Injun Joe's evidence stood firm and
+unshaken, and that there was not the slightest question as to what the
+jury's verdict would be.
+
+Tom was out late, that night, and came to bed through the window. He
+was in a tremendous state of excitement. It was hours before he got to
+sleep. All the village flocked to the court-house the next morning, for
+this was to be the great day. Both sexes were about equally represented
+in the packed audience. After a long wait the jury filed in and took
+their places; shortly afterward, Potter, pale and haggard, timid and
+hopeless, was brought in, with chains upon him, and seated where all
+the curious eyes could stare at him; no less conspicuous was Injun Joe,
+stolid as ever. There was another pause, and then the judge arrived and
+the sheriff proclaimed the opening of the court. The usual whisperings
+among the lawyers and gathering together of papers followed. These
+details and accompanying delays worked up an atmosphere of preparation
+that was as impressive as it was fascinating.
+
+Now a witness was called who testified that he found Muff Potter
+washing in the brook, at an early hour of the morning that the murder
+was discovered, and that he immediately sneaked away. After some
+further questioning, counsel for the prosecution said:
+
+"Take the witness."
+
+The prisoner raised his eyes for a moment, but dropped them again when
+his own counsel said:
+
+"I have no questions to ask him."
+
+The next witness proved the finding of the knife near the corpse.
+Counsel for the prosecution said:
+
+"Take the witness."
+
+"I have no questions to ask him," Potter's lawyer replied.
+
+A third witness swore he had often seen the knife in Potter's
+possession.
+
+"Take the witness."
+
+Counsel for Potter declined to question him. The faces of the audience
+began to betray annoyance. Did this attorney mean to throw away his
+client's life without an effort?
+
+Several witnesses deposed concerning Potter's guilty behavior when
+brought to the scene of the murder. They were allowed to leave the
+stand without being cross-questioned.
+
+Every detail of the damaging circumstances that occurred in the
+graveyard upon that morning which all present remembered so well was
+brought out by credible witnesses, but none of them were cross-examined
+by Potter's lawyer. The perplexity and dissatisfaction of the house
+expressed itself in murmurs and provoked a reproof from the bench.
+Counsel for the prosecution now said:
+
+"By the oaths of citizens whose simple word is above suspicion, we
+have fastened this awful crime, beyond all possibility of question,
+upon the unhappy prisoner at the bar. We rest our case here."
+
+A groan escaped from poor Potter, and he put his face in his hands and
+rocked his body softly to and fro, while a painful silence reigned in
+the court-room. Many men were moved, and many women's compassion
+testified itself in tears. Counsel for the defence rose and said:
+
+"Your honor, in our remarks at the opening of this trial, we
+foreshadowed our purpose to prove that our client did this fearful deed
+while under the influence of a blind and irresponsible delirium
+produced by drink. We have changed our mind. We shall not offer that
+plea." [Then to the clerk:] "Call Thomas Sawyer!"
+
+A puzzled amazement awoke in every face in the house, not even
+excepting Potter's. Every eye fastened itself with wondering interest
+upon Tom as he rose and took his place upon the stand. The boy looked
+wild enough, for he was badly scared. The oath was administered.
+
+"Thomas Sawyer, where were you on the seventeenth of June, about the
+hour of midnight?"
+
+Tom glanced at Injun Joe's iron face and his tongue failed him. The
+audience listened breathless, but the words refused to come. After a
+few moments, however, the boy got a little of his strength back, and
+managed to put enough of it into his voice to make part of the house
+hear:
+
+"In the graveyard!"
+
+"A little bit louder, please. Don't be afraid. You were--"
+
+"In the graveyard."
+
+A contemptuous smile flitted across Injun Joe's face.
+
+"Were you anywhere near Horse Williams' grave?"
+
+"Yes, sir."
+
+"Speak up--just a trifle louder. How near were you?"
+
+"Near as I am to you."
+
+"Were you hidden, or not?"
+
+"I was hid."
+
+"Where?"
+
+"Behind the elms that's on the edge of the grave."
+
+Injun Joe gave a barely perceptible start.
+
+"Any one with you?"
+
+"Yes, sir. I went there with--"
+
+"Wait--wait a moment. Never mind mentioning your companion's name. We
+will produce him at the proper time. Did you carry anything there with
+you."
+
+Tom hesitated and looked confused.
+
+"Speak out, my boy--don't be diffident. The truth is always
+respectable. What did you take there?"
+
+"Only a--a--dead cat."
+
+There was a ripple of mirth, which the court checked.
+
+"We will produce the skeleton of that cat. Now, my boy, tell us
+everything that occurred--tell it in your own way--don't skip anything,
+and don't be afraid."
+
+Tom began--hesitatingly at first, but as he warmed to his subject his
+words flowed more and more easily; in a little while every sound ceased
+but his own voice; every eye fixed itself upon him; with parted lips
+and bated breath the audience hung upon his words, taking no note of
+time, rapt in the ghastly fascinations of the tale. The strain upon
+pent emotion reached its climax when the boy said:
+
+"--and as the doctor fetched the board around and Muff Potter fell,
+Injun Joe jumped with the knife and--"
+
+Crash! Quick as lightning the half-breed sprang for a window, tore his
+way through all opposers, and was gone!
+
+
+
+CHAPTER XXIV
+
+TOM was a glittering hero once more--the pet of the old, the envy of
+the young. His name even went into immortal print, for the village
+paper magnified him. There were some that believed he would be
+President, yet, if he escaped hanging.
+
+As usual, the fickle, unreasoning world took Muff Potter to its bosom
+and fondled him as lavishly as it had abused him before. But that sort
+of conduct is to the world's credit; therefore it is not well to find
+fault with it.
+
+Tom's days were days of splendor and exultation to him, but his nights
+were seasons of horror. Injun Joe infested all his dreams, and always
+with doom in his eye. Hardly any temptation could persuade the boy to
+stir abroad after nightfall. Poor Huck was in the same state of
+wretchedness and terror, for Tom had told the whole story to the lawyer
+the night before the great day of the trial, and Huck was sore afraid
+that his share in the business might leak out, yet, notwithstanding
+Injun Joe's flight had saved him the suffering of testifying in court.
+The poor fellow had got the attorney to promise secrecy, but what of
+that? Since Tom's harassed conscience had managed to drive him to the
+lawyer's house by night and wring a dread tale from lips that had been
+sealed with the dismalest and most formidable of oaths, Huck's
+confidence in the human race was well-nigh obliterated.
+
+Daily Muff Potter's gratitude made Tom glad he had spoken; but nightly
+he wished he had sealed up his tongue.
+
+Half the time Tom was afraid Injun Joe would never be captured; the
+other half he was afraid he would be. He felt sure he never could draw
+a safe breath again until that man was dead and he had seen the corpse.
+
+Rewards had been offered, the country had been scoured, but no Injun
+Joe was found. One of those omniscient and awe-inspiring marvels, a
+detective, came up from St. Louis, moused around, shook his head,
+looked wise, and made that sort of astounding success which members of
+that craft usually achieve. That is to say, he "found a clew." But you
+can't hang a "clew" for murder, and so after that detective had got
+through and gone home, Tom felt just as insecure as he was before.
+
+The slow days drifted on, and each left behind it a slightly lightened
+weight of apprehension.
+
+
+
+CHAPTER XXV
+
+THERE comes a time in every rightly-constructed boy's life when he has
+a raging desire to go somewhere and dig for hidden treasure. This
+desire suddenly came upon Tom one day. He sallied out to find Joe
+Harper, but failed of success. Next he sought Ben Rogers; he had gone
+fishing. Presently he stumbled upon Huck Finn the Red-Handed. Huck
+would answer. Tom took him to a private place and opened the matter to
+him confidentially. Huck was willing. Huck was always willing to take a
+hand in any enterprise that offered entertainment and required no
+capital, for he had a troublesome superabundance of that sort of time
+which is not money. "Where'll we dig?" said Huck.
+
+"Oh, most anywhere."
+
+"Why, is it hid all around?"
+
+"No, indeed it ain't. It's hid in mighty particular places, Huck
+--sometimes on islands, sometimes in rotten chests under the end of a
+limb of an old dead tree, just where the shadow falls at midnight; but
+mostly under the floor in ha'nted houses."
+
+"Who hides it?"
+
+"Why, robbers, of course--who'd you reckon? Sunday-school
+sup'rintendents?"
+
+"I don't know. If 'twas mine I wouldn't hide it; I'd spend it and have
+a good time."
+
+"So would I. But robbers don't do that way. They always hide it and
+leave it there."
+
+"Don't they come after it any more?"
+
+"No, they think they will, but they generally forget the marks, or
+else they die. Anyway, it lays there a long time and gets rusty; and by
+and by somebody finds an old yellow paper that tells how to find the
+marks--a paper that's got to be ciphered over about a week because it's
+mostly signs and hy'roglyphics."
+
+"Hyro--which?"
+
+"Hy'roglyphics--pictures and things, you know, that don't seem to mean
+anything."
+
+"Have you got one of them papers, Tom?"
+
+"No."
+
+"Well then, how you going to find the marks?"
+
+"I don't want any marks. They always bury it under a ha'nted house or
+on an island, or under a dead tree that's got one limb sticking out.
+Well, we've tried Jackson's Island a little, and we can try it again
+some time; and there's the old ha'nted house up the Still-House branch,
+and there's lots of dead-limb trees--dead loads of 'em."
+
+"Is it under all of them?"
+
+"How you talk! No!"
+
+"Then how you going to know which one to go for?"
+
+"Go for all of 'em!"
+
+"Why, Tom, it'll take all summer."
+
+"Well, what of that? Suppose you find a brass pot with a hundred
+dollars in it, all rusty and gray, or rotten chest full of di'monds.
+How's that?"
+
+Huck's eyes glowed.
+
+"That's bully. Plenty bully enough for me. Just you gimme the hundred
+dollars and I don't want no di'monds."
+
+"All right. But I bet you I ain't going to throw off on di'monds. Some
+of 'em's worth twenty dollars apiece--there ain't any, hardly, but's
+worth six bits or a dollar."
+
+"No! Is that so?"
+
+"Cert'nly--anybody'll tell you so. Hain't you ever seen one, Huck?"
+
+"Not as I remember."
+
+"Oh, kings have slathers of them."
+
+"Well, I don' know no kings, Tom."
+
+"I reckon you don't. But if you was to go to Europe you'd see a raft
+of 'em hopping around."
+
+"Do they hop?"
+
+"Hop?--your granny! No!"
+
+"Well, what did you say they did, for?"
+
+"Shucks, I only meant you'd SEE 'em--not hopping, of course--what do
+they want to hop for?--but I mean you'd just see 'em--scattered around,
+you know, in a kind of a general way. Like that old humpbacked Richard."
+
+"Richard? What's his other name?"
+
+"He didn't have any other name. Kings don't have any but a given name."
+
+"No?"
+
+"But they don't."
+
+"Well, if they like it, Tom, all right; but I don't want to be a king
+and have only just a given name, like a nigger. But say--where you
+going to dig first?"
+
+"Well, I don't know. S'pose we tackle that old dead-limb tree on the
+hill t'other side of Still-House branch?"
+
+"I'm agreed."
+
+So they got a crippled pick and a shovel, and set out on their
+three-mile tramp. They arrived hot and panting, and threw themselves
+down in the shade of a neighboring elm to rest and have a smoke.
+
+"I like this," said Tom.
+
+"So do I."
+
+"Say, Huck, if we find a treasure here, what you going to do with your
+share?"
+
+"Well, I'll have pie and a glass of soda every day, and I'll go to
+every circus that comes along. I bet I'll have a gay time."
+
+"Well, ain't you going to save any of it?"
+
+"Save it? What for?"
+
+"Why, so as to have something to live on, by and by."
+
+"Oh, that ain't any use. Pap would come back to thish-yer town some
+day and get his claws on it if I didn't hurry up, and I tell you he'd
+clean it out pretty quick. What you going to do with yourn, Tom?"
+
+"I'm going to buy a new drum, and a sure-'nough sword, and a red
+necktie and a bull pup, and get married."
+
+"Married!"
+
+"That's it."
+
+"Tom, you--why, you ain't in your right mind."
+
+"Wait--you'll see."
+
+"Well, that's the foolishest thing you could do. Look at pap and my
+mother. Fight! Why, they used to fight all the time. I remember, mighty
+well."
+
+"That ain't anything. The girl I'm going to marry won't fight."
+
+"Tom, I reckon they're all alike. They'll all comb a body. Now you
+better think 'bout this awhile. I tell you you better. What's the name
+of the gal?"
+
+"It ain't a gal at all--it's a girl."
+
+"It's all the same, I reckon; some says gal, some says girl--both's
+right, like enough. Anyway, what's her name, Tom?"
+
+"I'll tell you some time--not now."
+
+"All right--that'll do. Only if you get married I'll be more lonesomer
+than ever."
+
+"No you won't. You'll come and live with me. Now stir out of this and
+we'll go to digging."
+
+They worked and sweated for half an hour. No result. They toiled
+another half-hour. Still no result. Huck said:
+
+"Do they always bury it as deep as this?"
+
+"Sometimes--not always. Not generally. I reckon we haven't got the
+right place."
+
+So they chose a new spot and began again. The labor dragged a little,
+but still they made progress. They pegged away in silence for some
+time. Finally Huck leaned on his shovel, swabbed the beaded drops from
+his brow with his sleeve, and said:
+
+"Where you going to dig next, after we get this one?"
+
+"I reckon maybe we'll tackle the old tree that's over yonder on
+Cardiff Hill back of the widow's."
+
+"I reckon that'll be a good one. But won't the widow take it away from
+us, Tom? It's on her land."
+
+"SHE take it away! Maybe she'd like to try it once. Whoever finds one
+of these hid treasures, it belongs to him. It don't make any difference
+whose land it's on."
+
+That was satisfactory. The work went on. By and by Huck said:
+
+"Blame it, we must be in the wrong place again. What do you think?"
+
+"It is mighty curious, Huck. I don't understand it. Sometimes witches
+interfere. I reckon maybe that's what's the trouble now."
+
+"Shucks! Witches ain't got no power in the daytime."
+
+"Well, that's so. I didn't think of that. Oh, I know what the matter
+is! What a blamed lot of fools we are! You got to find out where the
+shadow of the limb falls at midnight, and that's where you dig!"
+
+"Then consound it, we've fooled away all this work for nothing. Now
+hang it all, we got to come back in the night. It's an awful long way.
+Can you get out?"
+
+"I bet I will. We've got to do it to-night, too, because if somebody
+sees these holes they'll know in a minute what's here and they'll go
+for it."
+
+"Well, I'll come around and maow to-night."
+
+"All right. Let's hide the tools in the bushes."
+
+The boys were there that night, about the appointed time. They sat in
+the shadow waiting. It was a lonely place, and an hour made solemn by
+old traditions. Spirits whispered in the rustling leaves, ghosts lurked
+in the murky nooks, the deep baying of a hound floated up out of the
+distance, an owl answered with his sepulchral note. The boys were
+subdued by these solemnities, and talked little. By and by they judged
+that twelve had come; they marked where the shadow fell, and began to
+dig. Their hopes commenced to rise. Their interest grew stronger, and
+their industry kept pace with it. The hole deepened and still deepened,
+but every time their hearts jumped to hear the pick strike upon
+something, they only suffered a new disappointment. It was only a stone
+or a chunk. At last Tom said:
+
+"It ain't any use, Huck, we're wrong again."
+
+"Well, but we CAN'T be wrong. We spotted the shadder to a dot."
+
+"I know it, but then there's another thing."
+
+"What's that?".
+
+"Why, we only guessed at the time. Like enough it was too late or too
+early."
+
+Huck dropped his shovel.
+
+"That's it," said he. "That's the very trouble. We got to give this
+one up. We can't ever tell the right time, and besides this kind of
+thing's too awful, here this time of night with witches and ghosts
+a-fluttering around so. I feel as if something's behind me all the time;
+and I'm afeard to turn around, becuz maybe there's others in front
+a-waiting for a chance. I been creeping all over, ever since I got here."
+
+"Well, I've been pretty much so, too, Huck. They most always put in a
+dead man when they bury a treasure under a tree, to look out for it."
+
+"Lordy!"
+
+"Yes, they do. I've always heard that."
+
+"Tom, I don't like to fool around much where there's dead people. A
+body's bound to get into trouble with 'em, sure."
+
+"I don't like to stir 'em up, either. S'pose this one here was to
+stick his skull out and say something!"
+
+"Don't Tom! It's awful."
+
+"Well, it just is. Huck, I don't feel comfortable a bit."
+
+"Say, Tom, let's give this place up, and try somewheres else."
+
+"All right, I reckon we better."
+
+"What'll it be?"
+
+Tom considered awhile; and then said:
+
+"The ha'nted house. That's it!"
+
+"Blame it, I don't like ha'nted houses, Tom. Why, they're a dern sight
+worse'n dead people. Dead people might talk, maybe, but they don't come
+sliding around in a shroud, when you ain't noticing, and peep over your
+shoulder all of a sudden and grit their teeth, the way a ghost does. I
+couldn't stand such a thing as that, Tom--nobody could."
+
+"Yes, but, Huck, ghosts don't travel around only at night. They won't
+hender us from digging there in the daytime."
+
+"Well, that's so. But you know mighty well people don't go about that
+ha'nted house in the day nor the night."
+
+"Well, that's mostly because they don't like to go where a man's been
+murdered, anyway--but nothing's ever been seen around that house except
+in the night--just some blue lights slipping by the windows--no regular
+ghosts."
+
+"Well, where you see one of them blue lights flickering around, Tom,
+you can bet there's a ghost mighty close behind it. It stands to
+reason. Becuz you know that they don't anybody but ghosts use 'em."
+
+"Yes, that's so. But anyway they don't come around in the daytime, so
+what's the use of our being afeard?"
+
+"Well, all right. We'll tackle the ha'nted house if you say so--but I
+reckon it's taking chances."
+
+They had started down the hill by this time. There in the middle of
+the moonlit valley below them stood the "ha'nted" house, utterly
+isolated, its fences gone long ago, rank weeds smothering the very
+doorsteps, the chimney crumbled to ruin, the window-sashes vacant, a
+corner of the roof caved in. The boys gazed awhile, half expecting to
+see a blue light flit past a window; then talking in a low tone, as
+befitted the time and the circumstances, they struck far off to the
+right, to give the haunted house a wide berth, and took their way
+homeward through the woods that adorned the rearward side of Cardiff
+Hill.
+
+
+
+CHAPTER XXVI
+
+ABOUT noon the next day the boys arrived at the dead tree; they had
+come for their tools. Tom was impatient to go to the haunted house;
+Huck was measurably so, also--but suddenly said:
+
+"Lookyhere, Tom, do you know what day it is?"
+
+Tom mentally ran over the days of the week, and then quickly lifted
+his eyes with a startled look in them--
+
+"My! I never once thought of it, Huck!"
+
+"Well, I didn't neither, but all at once it popped onto me that it was
+Friday."
+
+"Blame it, a body can't be too careful, Huck. We might 'a' got into an
+awful scrape, tackling such a thing on a Friday."
+
+"MIGHT! Better say we WOULD! There's some lucky days, maybe, but
+Friday ain't."
+
+"Any fool knows that. I don't reckon YOU was the first that found it
+out, Huck."
+
+"Well, I never said I was, did I? And Friday ain't all, neither. I had
+a rotten bad dream last night--dreampt about rats."
+
+"No! Sure sign of trouble. Did they fight?"
+
+"No."
+
+"Well, that's good, Huck. When they don't fight it's only a sign that
+there's trouble around, you know. All we got to do is to look mighty
+sharp and keep out of it. We'll drop this thing for to-day, and play.
+Do you know Robin Hood, Huck?"
+
+"No. Who's Robin Hood?"
+
+"Why, he was one of the greatest men that was ever in England--and the
+best. He was a robber."
+
+"Cracky, I wisht I was. Who did he rob?"
+
+"Only sheriffs and bishops and rich people and kings, and such like.
+But he never bothered the poor. He loved 'em. He always divided up with
+'em perfectly square."
+
+"Well, he must 'a' been a brick."
+
+"I bet you he was, Huck. Oh, he was the noblest man that ever was.
+They ain't any such men now, I can tell you. He could lick any man in
+England, with one hand tied behind him; and he could take his yew bow
+and plug a ten-cent piece every time, a mile and a half."
+
+"What's a YEW bow?"
+
+"I don't know. It's some kind of a bow, of course. And if he hit that
+dime only on the edge he would set down and cry--and curse. But we'll
+play Robin Hood--it's nobby fun. I'll learn you."
+
+"I'm agreed."
+
+So they played Robin Hood all the afternoon, now and then casting a
+yearning eye down upon the haunted house and passing a remark about the
+morrow's prospects and possibilities there. As the sun began to sink
+into the west they took their way homeward athwart the long shadows of
+the trees and soon were buried from sight in the forests of Cardiff
+Hill.
+
+On Saturday, shortly after noon, the boys were at the dead tree again.
+They had a smoke and a chat in the shade, and then dug a little in
+their last hole, not with great hope, but merely because Tom said there
+were so many cases where people had given up a treasure after getting
+down within six inches of it, and then somebody else had come along and
+turned it up with a single thrust of a shovel. The thing failed this
+time, however, so the boys shouldered their tools and went away feeling
+that they had not trifled with fortune, but had fulfilled all the
+requirements that belong to the business of treasure-hunting.
+
+When they reached the haunted house there was something so weird and
+grisly about the dead silence that reigned there under the baking sun,
+and something so depressing about the loneliness and desolation of the
+place, that they were afraid, for a moment, to venture in. Then they
+crept to the door and took a trembling peep. They saw a weed-grown,
+floorless room, unplastered, an ancient fireplace, vacant windows, a
+ruinous staircase; and here, there, and everywhere hung ragged and
+abandoned cobwebs. They presently entered, softly, with quickened
+pulses, talking in whispers, ears alert to catch the slightest sound,
+and muscles tense and ready for instant retreat.
+
+In a little while familiarity modified their fears and they gave the
+place a critical and interested examination, rather admiring their own
+boldness, and wondering at it, too. Next they wanted to look up-stairs.
+This was something like cutting off retreat, but they got to daring
+each other, and of course there could be but one result--they threw
+their tools into a corner and made the ascent. Up there were the same
+signs of decay. In one corner they found a closet that promised
+mystery, but the promise was a fraud--there was nothing in it. Their
+courage was up now and well in hand. They were about to go down and
+begin work when--
+
+"Sh!" said Tom.
+
+"What is it?" whispered Huck, blanching with fright.
+
+"Sh!... There!... Hear it?"
+
+"Yes!... Oh, my! Let's run!"
+
+"Keep still! Don't you budge! They're coming right toward the door."
+
+The boys stretched themselves upon the floor with their eyes to
+knot-holes in the planking, and lay waiting, in a misery of fear.
+
+"They've stopped.... No--coming.... Here they are. Don't whisper
+another word, Huck. My goodness, I wish I was out of this!"
+
+Two men entered. Each boy said to himself: "There's the old deaf and
+dumb Spaniard that's been about town once or twice lately--never saw
+t'other man before."
+
+"T'other" was a ragged, unkempt creature, with nothing very pleasant
+in his face. The Spaniard was wrapped in a serape; he had bushy white
+whiskers; long white hair flowed from under his sombrero, and he wore
+green goggles. When they came in, "t'other" was talking in a low voice;
+they sat down on the ground, facing the door, with their backs to the
+wall, and the speaker continued his remarks. His manner became less
+guarded and his words more distinct as he proceeded:
+
+"No," said he, "I've thought it all over, and I don't like it. It's
+dangerous."
+
+"Dangerous!" grunted the "deaf and dumb" Spaniard--to the vast
+surprise of the boys. "Milksop!"
+
+This voice made the boys gasp and quake. It was Injun Joe's! There was
+silence for some time. Then Joe said:
+
+"What's any more dangerous than that job up yonder--but nothing's come
+of it."
+
+"That's different. Away up the river so, and not another house about.
+'Twon't ever be known that we tried, anyway, long as we didn't succeed."
+
+"Well, what's more dangerous than coming here in the daytime!--anybody
+would suspicion us that saw us."
+
+"I know that. But there warn't any other place as handy after that
+fool of a job. I want to quit this shanty. I wanted to yesterday, only
+it warn't any use trying to stir out of here, with those infernal boys
+playing over there on the hill right in full view."
+
+"Those infernal boys" quaked again under the inspiration of this
+remark, and thought how lucky it was that they had remembered it was
+Friday and concluded to wait a day. They wished in their hearts they
+had waited a year.
+
+The two men got out some food and made a luncheon. After a long and
+thoughtful silence, Injun Joe said:
+
+"Look here, lad--you go back up the river where you belong. Wait there
+till you hear from me. I'll take the chances on dropping into this town
+just once more, for a look. We'll do that 'dangerous' job after I've
+spied around a little and think things look well for it. Then for
+Texas! We'll leg it together!"
+
+This was satisfactory. Both men presently fell to yawning, and Injun
+Joe said:
+
+"I'm dead for sleep! It's your turn to watch."
+
+He curled down in the weeds and soon began to snore. His comrade
+stirred him once or twice and he became quiet. Presently the watcher
+began to nod; his head drooped lower and lower, both men began to snore
+now.
+
+The boys drew a long, grateful breath. Tom whispered:
+
+"Now's our chance--come!"
+
+Huck said:
+
+"I can't--I'd die if they was to wake."
+
+Tom urged--Huck held back. At last Tom rose slowly and softly, and
+started alone. But the first step he made wrung such a hideous creak
+from the crazy floor that he sank down almost dead with fright. He
+never made a second attempt. The boys lay there counting the dragging
+moments till it seemed to them that time must be done and eternity
+growing gray; and then they were grateful to note that at last the sun
+was setting.
+
+Now one snore ceased. Injun Joe sat up, stared around--smiled grimly
+upon his comrade, whose head was drooping upon his knees--stirred him
+up with his foot and said:
+
+"Here! YOU'RE a watchman, ain't you! All right, though--nothing's
+happened."
+
+"My! have I been asleep?"
+
+"Oh, partly, partly. Nearly time for us to be moving, pard. What'll we
+do with what little swag we've got left?"
+
+"I don't know--leave it here as we've always done, I reckon. No use to
+take it away till we start south. Six hundred and fifty in silver's
+something to carry."
+
+"Well--all right--it won't matter to come here once more."
+
+"No--but I'd say come in the night as we used to do--it's better."
+
+"Yes: but look here; it may be a good while before I get the right
+chance at that job; accidents might happen; 'tain't in such a very good
+place; we'll just regularly bury it--and bury it deep."
+
+"Good idea," said the comrade, who walked across the room, knelt down,
+raised one of the rearward hearth-stones and took out a bag that
+jingled pleasantly. He subtracted from it twenty or thirty dollars for
+himself and as much for Injun Joe, and passed the bag to the latter,
+who was on his knees in the corner, now, digging with his bowie-knife.
+
+The boys forgot all their fears, all their miseries in an instant.
+With gloating eyes they watched every movement. Luck!--the splendor of
+it was beyond all imagination! Six hundred dollars was money enough to
+make half a dozen boys rich! Here was treasure-hunting under the
+happiest auspices--there would not be any bothersome uncertainty as to
+where to dig. They nudged each other every moment--eloquent nudges and
+easily understood, for they simply meant--"Oh, but ain't you glad NOW
+we're here!"
+
+Joe's knife struck upon something.
+
+"Hello!" said he.
+
+"What is it?" said his comrade.
+
+"Half-rotten plank--no, it's a box, I believe. Here--bear a hand and
+we'll see what it's here for. Never mind, I've broke a hole."
+
+He reached his hand in and drew it out--
+
+"Man, it's money!"
+
+The two men examined the handful of coins. They were gold. The boys
+above were as excited as themselves, and as delighted.
+
+Joe's comrade said:
+
+"We'll make quick work of this. There's an old rusty pick over amongst
+the weeds in the corner the other side of the fireplace--I saw it a
+minute ago."
+
+He ran and brought the boys' pick and shovel. Injun Joe took the pick,
+looked it over critically, shook his head, muttered something to
+himself, and then began to use it. The box was soon unearthed. It was
+not very large; it was iron bound and had been very strong before the
+slow years had injured it. The men contemplated the treasure awhile in
+blissful silence.
+
+"Pard, there's thousands of dollars here," said Injun Joe.
+
+"'Twas always said that Murrel's gang used to be around here one
+summer," the stranger observed.
+
+"I know it," said Injun Joe; "and this looks like it, I should say."
+
+"Now you won't need to do that job."
+
+The half-breed frowned. Said he:
+
+"You don't know me. Least you don't know all about that thing. 'Tain't
+robbery altogether--it's REVENGE!" and a wicked light flamed in his
+eyes. "I'll need your help in it. When it's finished--then Texas. Go
+home to your Nance and your kids, and stand by till you hear from me."
+
+"Well--if you say so; what'll we do with this--bury it again?"
+
+"Yes. [Ravishing delight overhead.] NO! by the great Sachem, no!
+[Profound distress overhead.] I'd nearly forgot. That pick had fresh
+earth on it! [The boys were sick with terror in a moment.] What
+business has a pick and a shovel here? What business with fresh earth
+on them? Who brought them here--and where are they gone? Have you heard
+anybody?--seen anybody? What! bury it again and leave them to come and
+see the ground disturbed? Not exactly--not exactly. We'll take it to my
+den."
+
+"Why, of course! Might have thought of that before. You mean Number
+One?"
+
+"No--Number Two--under the cross. The other place is bad--too common."
+
+"All right. It's nearly dark enough to start."
+
+Injun Joe got up and went about from window to window cautiously
+peeping out. Presently he said:
+
+"Who could have brought those tools here? Do you reckon they can be
+up-stairs?"
+
+The boys' breath forsook them. Injun Joe put his hand on his knife,
+halted a moment, undecided, and then turned toward the stairway. The
+boys thought of the closet, but their strength was gone. The steps came
+creaking up the stairs--the intolerable distress of the situation woke
+the stricken resolution of the lads--they were about to spring for the
+closet, when there was a crash of rotten timbers and Injun Joe landed
+on the ground amid the debris of the ruined stairway. He gathered
+himself up cursing, and his comrade said:
+
+"Now what's the use of all that? If it's anybody, and they're up
+there, let them STAY there--who cares? If they want to jump down, now,
+and get into trouble, who objects? It will be dark in fifteen minutes
+--and then let them follow us if they want to. I'm willing. In my
+opinion, whoever hove those things in here caught a sight of us and
+took us for ghosts or devils or something. I'll bet they're running
+yet."
+
+Joe grumbled awhile; then he agreed with his friend that what daylight
+was left ought to be economized in getting things ready for leaving.
+Shortly afterward they slipped out of the house in the deepening
+twilight, and moved toward the river with their precious box.
+
+Tom and Huck rose up, weak but vastly relieved, and stared after them
+through the chinks between the logs of the house. Follow? Not they.
+They were content to reach ground again without broken necks, and take
+the townward track over the hill. They did not talk much. They were too
+much absorbed in hating themselves--hating the ill luck that made them
+take the spade and the pick there. But for that, Injun Joe never would
+have suspected. He would have hidden the silver with the gold to wait
+there till his "revenge" was satisfied, and then he would have had the
+misfortune to find that money turn up missing. Bitter, bitter luck that
+the tools were ever brought there!
+
+They resolved to keep a lookout for that Spaniard when he should come
+to town spying out for chances to do his revengeful job, and follow him
+to "Number Two," wherever that might be. Then a ghastly thought
+occurred to Tom.
+
+"Revenge? What if he means US, Huck!"
+
+"Oh, don't!" said Huck, nearly fainting.
+
+They talked it all over, and as they entered town they agreed to
+believe that he might possibly mean somebody else--at least that he
+might at least mean nobody but Tom, since only Tom had testified.
+
+Very, very small comfort it was to Tom to be alone in danger! Company
+would be a palpable improvement, he thought.
+
+
+
+CHAPTER XXVII
+
+THE adventure of the day mightily tormented Tom's dreams that night.
+Four times he had his hands on that rich treasure and four times it
+wasted to nothingness in his fingers as sleep forsook him and
+wakefulness brought back the hard reality of his misfortune. As he lay
+in the early morning recalling the incidents of his great adventure, he
+noticed that they seemed curiously subdued and far away--somewhat as if
+they had happened in another world, or in a time long gone by. Then it
+occurred to him that the great adventure itself must be a dream! There
+was one very strong argument in favor of this idea--namely, that the
+quantity of coin he had seen was too vast to be real. He had never seen
+as much as fifty dollars in one mass before, and he was like all boys
+of his age and station in life, in that he imagined that all references
+to "hundreds" and "thousands" were mere fanciful forms of speech, and
+that no such sums really existed in the world. He never had supposed
+for a moment that so large a sum as a hundred dollars was to be found
+in actual money in any one's possession. If his notions of hidden
+treasure had been analyzed, they would have been found to consist of a
+handful of real dimes and a bushel of vague, splendid, ungraspable
+dollars.
+
+But the incidents of his adventure grew sensibly sharper and clearer
+under the attrition of thinking them over, and so he presently found
+himself leaning to the impression that the thing might not have been a
+dream, after all. This uncertainty must be swept away. He would snatch
+a hurried breakfast and go and find Huck. Huck was sitting on the
+gunwale of a flatboat, listlessly dangling his feet in the water and
+looking very melancholy. Tom concluded to let Huck lead up to the
+subject. If he did not do it, then the adventure would be proved to
+have been only a dream.
+
+"Hello, Huck!"
+
+"Hello, yourself."
+
+Silence, for a minute.
+
+"Tom, if we'd 'a' left the blame tools at the dead tree, we'd 'a' got
+the money. Oh, ain't it awful!"
+
+"'Tain't a dream, then, 'tain't a dream! Somehow I most wish it was.
+Dog'd if I don't, Huck."
+
+"What ain't a dream?"
+
+"Oh, that thing yesterday. I been half thinking it was."
+
+"Dream! If them stairs hadn't broke down you'd 'a' seen how much dream
+it was! I've had dreams enough all night--with that patch-eyed Spanish
+devil going for me all through 'em--rot him!"
+
+"No, not rot him. FIND him! Track the money!"
+
+"Tom, we'll never find him. A feller don't have only one chance for
+such a pile--and that one's lost. I'd feel mighty shaky if I was to see
+him, anyway."
+
+"Well, so'd I; but I'd like to see him, anyway--and track him out--to
+his Number Two."
+
+"Number Two--yes, that's it. I been thinking 'bout that. But I can't
+make nothing out of it. What do you reckon it is?"
+
+"I dono. It's too deep. Say, Huck--maybe it's the number of a house!"
+
+"Goody!... No, Tom, that ain't it. If it is, it ain't in this
+one-horse town. They ain't no numbers here."
+
+"Well, that's so. Lemme think a minute. Here--it's the number of a
+room--in a tavern, you know!"
+
+"Oh, that's the trick! They ain't only two taverns. We can find out
+quick."
+
+"You stay here, Huck, till I come."
+
+Tom was off at once. He did not care to have Huck's company in public
+places. He was gone half an hour. He found that in the best tavern, No.
+2 had long been occupied by a young lawyer, and was still so occupied.
+In the less ostentatious house, No. 2 was a mystery. The
+tavern-keeper's young son said it was kept locked all the time, and he
+never saw anybody go into it or come out of it except at night; he did
+not know any particular reason for this state of things; had had some
+little curiosity, but it was rather feeble; had made the most of the
+mystery by entertaining himself with the idea that that room was
+"ha'nted"; had noticed that there was a light in there the night before.
+
+"That's what I've found out, Huck. I reckon that's the very No. 2
+we're after."
+
+"I reckon it is, Tom. Now what you going to do?"
+
+"Lemme think."
+
+Tom thought a long time. Then he said:
+
+"I'll tell you. The back door of that No. 2 is the door that comes out
+into that little close alley between the tavern and the old rattle trap
+of a brick store. Now you get hold of all the door-keys you can find,
+and I'll nip all of auntie's, and the first dark night we'll go there
+and try 'em. And mind you, keep a lookout for Injun Joe, because he
+said he was going to drop into town and spy around once more for a
+chance to get his revenge. If you see him, you just follow him; and if
+he don't go to that No. 2, that ain't the place."
+
+"Lordy, I don't want to foller him by myself!"
+
+"Why, it'll be night, sure. He mightn't ever see you--and if he did,
+maybe he'd never think anything."
+
+"Well, if it's pretty dark I reckon I'll track him. I dono--I dono.
+I'll try."
+
+"You bet I'll follow him, if it's dark, Huck. Why, he might 'a' found
+out he couldn't get his revenge, and be going right after that money."
+
+"It's so, Tom, it's so. I'll foller him; I will, by jingoes!"
+
+"Now you're TALKING! Don't you ever weaken, Huck, and I won't."
+
+
+
+CHAPTER XXVIII
+
+THAT night Tom and Huck were ready for their adventure. They hung
+about the neighborhood of the tavern until after nine, one watching the
+alley at a distance and the other the tavern door. Nobody entered the
+alley or left it; nobody resembling the Spaniard entered or left the
+tavern door. The night promised to be a fair one; so Tom went home with
+the understanding that if a considerable degree of darkness came on,
+Huck was to come and "maow," whereupon he would slip out and try the
+keys. But the night remained clear, and Huck closed his watch and
+retired to bed in an empty sugar hogshead about twelve.
+
+Tuesday the boys had the same ill luck. Also Wednesday. But Thursday
+night promised better. Tom slipped out in good season with his aunt's
+old tin lantern, and a large towel to blindfold it with. He hid the
+lantern in Huck's sugar hogshead and the watch began. An hour before
+midnight the tavern closed up and its lights (the only ones
+thereabouts) were put out. No Spaniard had been seen. Nobody had
+entered or left the alley. Everything was auspicious. The blackness of
+darkness reigned, the perfect stillness was interrupted only by
+occasional mutterings of distant thunder.
+
+Tom got his lantern, lit it in the hogshead, wrapped it closely in the
+towel, and the two adventurers crept in the gloom toward the tavern.
+Huck stood sentry and Tom felt his way into the alley. Then there was a
+season of waiting anxiety that weighed upon Huck's spirits like a
+mountain. He began to wish he could see a flash from the lantern--it
+would frighten him, but it would at least tell him that Tom was alive
+yet. It seemed hours since Tom had disappeared. Surely he must have
+fainted; maybe he was dead; maybe his heart had burst under terror and
+excitement. In his uneasiness Huck found himself drawing closer and
+closer to the alley; fearing all sorts of dreadful things, and
+momentarily expecting some catastrophe to happen that would take away
+his breath. There was not much to take away, for he seemed only able to
+inhale it by thimblefuls, and his heart would soon wear itself out, the
+way it was beating. Suddenly there was a flash of light and Tom came
+tearing by him: "Run!" said he; "run, for your life!"
+
+He needn't have repeated it; once was enough; Huck was making thirty
+or forty miles an hour before the repetition was uttered. The boys
+never stopped till they reached the shed of a deserted slaughter-house
+at the lower end of the village. Just as they got within its shelter
+the storm burst and the rain poured down. As soon as Tom got his breath
+he said:
+
+"Huck, it was awful! I tried two of the keys, just as soft as I could;
+but they seemed to make such a power of racket that I couldn't hardly
+get my breath I was so scared. They wouldn't turn in the lock, either.
+Well, without noticing what I was doing, I took hold of the knob, and
+open comes the door! It warn't locked! I hopped in, and shook off the
+towel, and, GREAT CAESAR'S GHOST!"
+
+"What!--what'd you see, Tom?"
+
+"Huck, I most stepped onto Injun Joe's hand!"
+
+"No!"
+
+"Yes! He was lying there, sound asleep on the floor, with his old
+patch on his eye and his arms spread out."
+
+"Lordy, what did you do? Did he wake up?"
+
+"No, never budged. Drunk, I reckon. I just grabbed that towel and
+started!"
+
+"I'd never 'a' thought of the towel, I bet!"
+
+"Well, I would. My aunt would make me mighty sick if I lost it."
+
+"Say, Tom, did you see that box?"
+
+"Huck, I didn't wait to look around. I didn't see the box, I didn't
+see the cross. I didn't see anything but a bottle and a tin cup on the
+floor by Injun Joe; yes, I saw two barrels and lots more bottles in the
+room. Don't you see, now, what's the matter with that ha'nted room?"
+
+"How?"
+
+"Why, it's ha'nted with whiskey! Maybe ALL the Temperance Taverns have
+got a ha'nted room, hey, Huck?"
+
+"Well, I reckon maybe that's so. Who'd 'a' thought such a thing? But
+say, Tom, now's a mighty good time to get that box, if Injun Joe's
+drunk."
+
+"It is, that! You try it!"
+
+Huck shuddered.
+
+"Well, no--I reckon not."
+
+"And I reckon not, Huck. Only one bottle alongside of Injun Joe ain't
+enough. If there'd been three, he'd be drunk enough and I'd do it."
+
+There was a long pause for reflection, and then Tom said:
+
+"Lookyhere, Huck, less not try that thing any more till we know Injun
+Joe's not in there. It's too scary. Now, if we watch every night, we'll
+be dead sure to see him go out, some time or other, and then we'll
+snatch that box quicker'n lightning."
+
+"Well, I'm agreed. I'll watch the whole night long, and I'll do it
+every night, too, if you'll do the other part of the job."
+
+"All right, I will. All you got to do is to trot up Hooper Street a
+block and maow--and if I'm asleep, you throw some gravel at the window
+and that'll fetch me."
+
+"Agreed, and good as wheat!"
+
+"Now, Huck, the storm's over, and I'll go home. It'll begin to be
+daylight in a couple of hours. You go back and watch that long, will
+you?"
+
+"I said I would, Tom, and I will. I'll ha'nt that tavern every night
+for a year! I'll sleep all day and I'll stand watch all night."
+
+"That's all right. Now, where you going to sleep?"
+
+"In Ben Rogers' hayloft. He lets me, and so does his pap's nigger man,
+Uncle Jake. I tote water for Uncle Jake whenever he wants me to, and
+any time I ask him he gives me a little something to eat if he can
+spare it. That's a mighty good nigger, Tom. He likes me, becuz I don't
+ever act as if I was above him. Sometime I've set right down and eat
+WITH him. But you needn't tell that. A body's got to do things when
+he's awful hungry he wouldn't want to do as a steady thing."
+
+"Well, if I don't want you in the daytime, I'll let you sleep. I won't
+come bothering around. Any time you see something's up, in the night,
+just skip right around and maow."
+
+
+
+CHAPTER XXIX
+
+THE first thing Tom heard on Friday morning was a glad piece of news
+--Judge Thatcher's family had come back to town the night before. Both
+Injun Joe and the treasure sunk into secondary importance for a moment,
+and Becky took the chief place in the boy's interest. He saw her and
+they had an exhausting good time playing "hi-spy" and "gully-keeper"
+with a crowd of their school-mates. The day was completed and crowned
+in a peculiarly satisfactory way: Becky teased her mother to appoint
+the next day for the long-promised and long-delayed picnic, and she
+consented. The child's delight was boundless; and Tom's not more
+moderate. The invitations were sent out before sunset, and straightway
+the young folks of the village were thrown into a fever of preparation
+and pleasurable anticipation. Tom's excitement enabled him to keep
+awake until a pretty late hour, and he had good hopes of hearing Huck's
+"maow," and of having his treasure to astonish Becky and the picnickers
+with, next day; but he was disappointed. No signal came that night.
+
+Morning came, eventually, and by ten or eleven o'clock a giddy and
+rollicking company were gathered at Judge Thatcher's, and everything
+was ready for a start. It was not the custom for elderly people to mar
+the picnics with their presence. The children were considered safe
+enough under the wings of a few young ladies of eighteen and a few
+young gentlemen of twenty-three or thereabouts. The old steam ferryboat
+was chartered for the occasion; presently the gay throng filed up the
+main street laden with provision-baskets. Sid was sick and had to miss
+the fun; Mary remained at home to entertain him. The last thing Mrs.
+Thatcher said to Becky, was:
+
+"You'll not get back till late. Perhaps you'd better stay all night
+with some of the girls that live near the ferry-landing, child."
+
+"Then I'll stay with Susy Harper, mamma."
+
+"Very well. And mind and behave yourself and don't be any trouble."
+
+Presently, as they tripped along, Tom said to Becky:
+
+"Say--I'll tell you what we'll do. 'Stead of going to Joe Harper's
+we'll climb right up the hill and stop at the Widow Douglas'. She'll
+have ice-cream! She has it most every day--dead loads of it. And she'll
+be awful glad to have us."
+
+"Oh, that will be fun!"
+
+Then Becky reflected a moment and said:
+
+"But what will mamma say?"
+
+"How'll she ever know?"
+
+The girl turned the idea over in her mind, and said reluctantly:
+
+"I reckon it's wrong--but--"
+
+"But shucks! Your mother won't know, and so what's the harm? All she
+wants is that you'll be safe; and I bet you she'd 'a' said go there if
+she'd 'a' thought of it. I know she would!"
+
+The Widow Douglas' splendid hospitality was a tempting bait. It and
+Tom's persuasions presently carried the day. So it was decided to say
+nothing anybody about the night's programme. Presently it occurred to
+Tom that maybe Huck might come this very night and give the signal. The
+thought took a deal of the spirit out of his anticipations. Still he
+could not bear to give up the fun at Widow Douglas'. And why should he
+give it up, he reasoned--the signal did not come the night before, so
+why should it be any more likely to come to-night? The sure fun of the
+evening outweighed the uncertain treasure; and, boy-like, he determined
+to yield to the stronger inclination and not allow himself to think of
+the box of money another time that day.
+
+Three miles below town the ferryboat stopped at the mouth of a woody
+hollow and tied up. The crowd swarmed ashore and soon the forest
+distances and craggy heights echoed far and near with shoutings and
+laughter. All the different ways of getting hot and tired were gone
+through with, and by-and-by the rovers straggled back to camp fortified
+with responsible appetites, and then the destruction of the good things
+began. After the feast there was a refreshing season of rest and chat
+in the shade of spreading oaks. By-and-by somebody shouted:
+
+"Who's ready for the cave?"
+
+Everybody was. Bundles of candles were procured, and straightway there
+was a general scamper up the hill. The mouth of the cave was up the
+hillside--an opening shaped like a letter A. Its massive oaken door
+stood unbarred. Within was a small chamber, chilly as an ice-house, and
+walled by Nature with solid limestone that was dewy with a cold sweat.
+It was romantic and mysterious to stand here in the deep gloom and look
+out upon the green valley shining in the sun. But the impressiveness of
+the situation quickly wore off, and the romping began again. The moment
+a candle was lighted there was a general rush upon the owner of it; a
+struggle and a gallant defence followed, but the candle was soon
+knocked down or blown out, and then there was a glad clamor of laughter
+and a new chase. But all things have an end. By-and-by the procession
+went filing down the steep descent of the main avenue, the flickering
+rank of lights dimly revealing the lofty walls of rock almost to their
+point of junction sixty feet overhead. This main avenue was not more
+than eight or ten feet wide. Every few steps other lofty and still
+narrower crevices branched from it on either hand--for McDougal's cave
+was but a vast labyrinth of crooked aisles that ran into each other and
+out again and led nowhere. It was said that one might wander days and
+nights together through its intricate tangle of rifts and chasms, and
+never find the end of the cave; and that he might go down, and down,
+and still down, into the earth, and it was just the same--labyrinth
+under labyrinth, and no end to any of them. No man "knew" the cave.
+That was an impossible thing. Most of the young men knew a portion of
+it, and it was not customary to venture much beyond this known portion.
+Tom Sawyer knew as much of the cave as any one.
+
+The procession moved along the main avenue some three-quarters of a
+mile, and then groups and couples began to slip aside into branch
+avenues, fly along the dismal corridors, and take each other by
+surprise at points where the corridors joined again. Parties were able
+to elude each other for the space of half an hour without going beyond
+the "known" ground.
+
+By-and-by, one group after another came straggling back to the mouth
+of the cave, panting, hilarious, smeared from head to foot with tallow
+drippings, daubed with clay, and entirely delighted with the success of
+the day. Then they were astonished to find that they had been taking no
+note of time and that night was about at hand. The clanging bell had
+been calling for half an hour. However, this sort of close to the day's
+adventures was romantic and therefore satisfactory. When the ferryboat
+with her wild freight pushed into the stream, nobody cared sixpence for
+the wasted time but the captain of the craft.
+
+Huck was already upon his watch when the ferryboat's lights went
+glinting past the wharf. He heard no noise on board, for the young
+people were as subdued and still as people usually are who are nearly
+tired to death. He wondered what boat it was, and why she did not stop
+at the wharf--and then he dropped her out of his mind and put his
+attention upon his business. The night was growing cloudy and dark. Ten
+o'clock came, and the noise of vehicles ceased, scattered lights began
+to wink out, all straggling foot-passengers disappeared, the village
+betook itself to its slumbers and left the small watcher alone with the
+silence and the ghosts. Eleven o'clock came, and the tavern lights were
+put out; darkness everywhere, now. Huck waited what seemed a weary long
+time, but nothing happened. His faith was weakening. Was there any use?
+Was there really any use? Why not give it up and turn in?
+
+A noise fell upon his ear. He was all attention in an instant. The
+alley door closed softly. He sprang to the corner of the brick store.
+The next moment two men brushed by him, and one seemed to have
+something under his arm. It must be that box! So they were going to
+remove the treasure. Why call Tom now? It would be absurd--the men
+would get away with the box and never be found again. No, he would
+stick to their wake and follow them; he would trust to the darkness for
+security from discovery. So communing with himself, Huck stepped out
+and glided along behind the men, cat-like, with bare feet, allowing
+them to keep just far enough ahead not to be invisible.
+
+They moved up the river street three blocks, then turned to the left
+up a cross-street. They went straight ahead, then, until they came to
+the path that led up Cardiff Hill; this they took. They passed by the
+old Welshman's house, half-way up the hill, without hesitating, and
+still climbed upward. Good, thought Huck, they will bury it in the old
+quarry. But they never stopped at the quarry. They passed on, up the
+summit. They plunged into the narrow path between the tall sumach
+bushes, and were at once hidden in the gloom. Huck closed up and
+shortened his distance, now, for they would never be able to see him.
+He trotted along awhile; then slackened his pace, fearing he was
+gaining too fast; moved on a piece, then stopped altogether; listened;
+no sound; none, save that he seemed to hear the beating of his own
+heart. The hooting of an owl came over the hill--ominous sound! But no
+footsteps. Heavens, was everything lost! He was about to spring with
+winged feet, when a man cleared his throat not four feet from him!
+Huck's heart shot into his throat, but he swallowed it again; and then
+he stood there shaking as if a dozen agues had taken charge of him at
+once, and so weak that he thought he must surely fall to the ground. He
+knew where he was. He knew he was within five steps of the stile
+leading into Widow Douglas' grounds. Very well, he thought, let them
+bury it there; it won't be hard to find.
+
+Now there was a voice--a very low voice--Injun Joe's:
+
+"Damn her, maybe she's got company--there's lights, late as it is."
+
+"I can't see any."
+
+This was that stranger's voice--the stranger of the haunted house. A
+deadly chill went to Huck's heart--this, then, was the "revenge" job!
+His thought was, to fly. Then he remembered that the Widow Douglas had
+been kind to him more than once, and maybe these men were going to
+murder her. He wished he dared venture to warn her; but he knew he
+didn't dare--they might come and catch him. He thought all this and
+more in the moment that elapsed between the stranger's remark and Injun
+Joe's next--which was--
+
+"Because the bush is in your way. Now--this way--now you see, don't
+you?"
+
+"Yes. Well, there IS company there, I reckon. Better give it up."
+
+"Give it up, and I just leaving this country forever! Give it up and
+maybe never have another chance. I tell you again, as I've told you
+before, I don't care for her swag--you may have it. But her husband was
+rough on me--many times he was rough on me--and mainly he was the
+justice of the peace that jugged me for a vagrant. And that ain't all.
+It ain't a millionth part of it! He had me HORSEWHIPPED!--horsewhipped
+in front of the jail, like a nigger!--with all the town looking on!
+HORSEWHIPPED!--do you understand? He took advantage of me and died. But
+I'll take it out of HER."
+
+"Oh, don't kill her! Don't do that!"
+
+"Kill? Who said anything about killing? I would kill HIM if he was
+here; but not her. When you want to get revenge on a woman you don't
+kill her--bosh! you go for her looks. You slit her nostrils--you notch
+her ears like a sow!"
+
+"By God, that's--"
+
+"Keep your opinion to yourself! It will be safest for you. I'll tie
+her to the bed. If she bleeds to death, is that my fault? I'll not cry,
+if she does. My friend, you'll help me in this thing--for MY sake
+--that's why you're here--I mightn't be able alone. If you flinch, I'll
+kill you. Do you understand that? And if I have to kill you, I'll kill
+her--and then I reckon nobody'll ever know much about who done this
+business."
+
+"Well, if it's got to be done, let's get at it. The quicker the
+better--I'm all in a shiver."
+
+"Do it NOW? And company there? Look here--I'll get suspicious of you,
+first thing you know. No--we'll wait till the lights are out--there's
+no hurry."
+
+Huck felt that a silence was going to ensue--a thing still more awful
+than any amount of murderous talk; so he held his breath and stepped
+gingerly back; planted his foot carefully and firmly, after balancing,
+one-legged, in a precarious way and almost toppling over, first on one
+side and then on the other. He took another step back, with the same
+elaboration and the same risks; then another and another, and--a twig
+snapped under his foot! His breath stopped and he listened. There was
+no sound--the stillness was perfect. His gratitude was measureless. Now
+he turned in his tracks, between the walls of sumach bushes--turned
+himself as carefully as if he were a ship--and then stepped quickly but
+cautiously along. When he emerged at the quarry he felt secure, and so
+he picked up his nimble heels and flew. Down, down he sped, till he
+reached the Welshman's. He banged at the door, and presently the heads
+of the old man and his two stalwart sons were thrust from windows.
+
+"What's the row there? Who's banging? What do you want?"
+
+"Let me in--quick! I'll tell everything."
+
+"Why, who are you?"
+
+"Huckleberry Finn--quick, let me in!"
+
+"Huckleberry Finn, indeed! It ain't a name to open many doors, I
+judge! But let him in, lads, and let's see what's the trouble."
+
+"Please don't ever tell I told you," were Huck's first words when he
+got in. "Please don't--I'd be killed, sure--but the widow's been good
+friends to me sometimes, and I want to tell--I WILL tell if you'll
+promise you won't ever say it was me."
+
+"By George, he HAS got something to tell, or he wouldn't act so!"
+exclaimed the old man; "out with it and nobody here'll ever tell, lad."
+
+Three minutes later the old man and his sons, well armed, were up the
+hill, and just entering the sumach path on tiptoe, their weapons in
+their hands. Huck accompanied them no further. He hid behind a great
+bowlder and fell to listening. There was a lagging, anxious silence,
+and then all of a sudden there was an explosion of firearms and a cry.
+
+Huck waited for no particulars. He sprang away and sped down the hill
+as fast as his legs could carry him.
+
+
+
+CHAPTER XXX
+
+AS the earliest suspicion of dawn appeared on Sunday morning, Huck
+came groping up the hill and rapped gently at the old Welshman's door.
+The inmates were asleep, but it was a sleep that was set on a
+hair-trigger, on account of the exciting episode of the night. A call
+came from a window:
+
+"Who's there!"
+
+Huck's scared voice answered in a low tone:
+
+"Please let me in! It's only Huck Finn!"
+
+"It's a name that can open this door night or day, lad!--and welcome!"
+
+These were strange words to the vagabond boy's ears, and the
+pleasantest he had ever heard. He could not recollect that the closing
+word had ever been applied in his case before. The door was quickly
+unlocked, and he entered. Huck was given a seat and the old man and his
+brace of tall sons speedily dressed themselves.
+
+"Now, my boy, I hope you're good and hungry, because breakfast will be
+ready as soon as the sun's up, and we'll have a piping hot one, too
+--make yourself easy about that! I and the boys hoped you'd turn up and
+stop here last night."
+
+"I was awful scared," said Huck, "and I run. I took out when the
+pistols went off, and I didn't stop for three mile. I've come now becuz
+I wanted to know about it, you know; and I come before daylight becuz I
+didn't want to run across them devils, even if they was dead."
+
+"Well, poor chap, you do look as if you'd had a hard night of it--but
+there's a bed here for you when you've had your breakfast. No, they
+ain't dead, lad--we are sorry enough for that. You see we knew right
+where to put our hands on them, by your description; so we crept along
+on tiptoe till we got within fifteen feet of them--dark as a cellar
+that sumach path was--and just then I found I was going to sneeze. It
+was the meanest kind of luck! I tried to keep it back, but no use
+--'twas bound to come, and it did come! I was in the lead with my pistol
+raised, and when the sneeze started those scoundrels a-rustling to get
+out of the path, I sung out, 'Fire boys!' and blazed away at the place
+where the rustling was. So did the boys. But they were off in a jiffy,
+those villains, and we after them, down through the woods. I judge we
+never touched them. They fired a shot apiece as they started, but their
+bullets whizzed by and didn't do us any harm. As soon as we lost the
+sound of their feet we quit chasing, and went down and stirred up the
+constables. They got a posse together, and went off to guard the river
+bank, and as soon as it is light the sheriff and a gang are going to
+beat up the woods. My boys will be with them presently. I wish we had
+some sort of description of those rascals--'twould help a good deal.
+But you couldn't see what they were like, in the dark, lad, I suppose?"
+
+"Oh yes; I saw them down-town and follered them."
+
+"Splendid! Describe them--describe them, my boy!"
+
+"One's the old deaf and dumb Spaniard that's ben around here once or
+twice, and t'other's a mean-looking, ragged--"
+
+"That's enough, lad, we know the men! Happened on them in the woods
+back of the widow's one day, and they slunk away. Off with you, boys,
+and tell the sheriff--get your breakfast to-morrow morning!"
+
+The Welshman's sons departed at once. As they were leaving the room
+Huck sprang up and exclaimed:
+
+"Oh, please don't tell ANYbody it was me that blowed on them! Oh,
+please!"
+
+"All right if you say it, Huck, but you ought to have the credit of
+what you did."
+
+"Oh no, no! Please don't tell!"
+
+When the young men were gone, the old Welshman said:
+
+"They won't tell--and I won't. But why don't you want it known?"
+
+Huck would not explain, further than to say that he already knew too
+much about one of those men and would not have the man know that he
+knew anything against him for the whole world--he would be killed for
+knowing it, sure.
+
+The old man promised secrecy once more, and said:
+
+"How did you come to follow these fellows, lad? Were they looking
+suspicious?"
+
+Huck was silent while he framed a duly cautious reply. Then he said:
+
+"Well, you see, I'm a kind of a hard lot,--least everybody says so,
+and I don't see nothing agin it--and sometimes I can't sleep much, on
+account of thinking about it and sort of trying to strike out a new way
+of doing. That was the way of it last night. I couldn't sleep, and so I
+come along up-street 'bout midnight, a-turning it all over, and when I
+got to that old shackly brick store by the Temperance Tavern, I backed
+up agin the wall to have another think. Well, just then along comes
+these two chaps slipping along close by me, with something under their
+arm, and I reckoned they'd stole it. One was a-smoking, and t'other one
+wanted a light; so they stopped right before me and the cigars lit up
+their faces and I see that the big one was the deaf and dumb Spaniard,
+by his white whiskers and the patch on his eye, and t'other one was a
+rusty, ragged-looking devil."
+
+"Could you see the rags by the light of the cigars?"
+
+This staggered Huck for a moment. Then he said:
+
+"Well, I don't know--but somehow it seems as if I did."
+
+"Then they went on, and you--"
+
+"Follered 'em--yes. That was it. I wanted to see what was up--they
+sneaked along so. I dogged 'em to the widder's stile, and stood in the
+dark and heard the ragged one beg for the widder, and the Spaniard
+swear he'd spile her looks just as I told you and your two--"
+
+"What! The DEAF AND DUMB man said all that!"
+
+Huck had made another terrible mistake! He was trying his best to keep
+the old man from getting the faintest hint of who the Spaniard might
+be, and yet his tongue seemed determined to get him into trouble in
+spite of all he could do. He made several efforts to creep out of his
+scrape, but the old man's eye was upon him and he made blunder after
+blunder. Presently the Welshman said:
+
+"My boy, don't be afraid of me. I wouldn't hurt a hair of your head
+for all the world. No--I'd protect you--I'd protect you. This Spaniard
+is not deaf and dumb; you've let that slip without intending it; you
+can't cover that up now. You know something about that Spaniard that
+you want to keep dark. Now trust me--tell me what it is, and trust me
+--I won't betray you."
+
+Huck looked into the old man's honest eyes a moment, then bent over
+and whispered in his ear:
+
+"'Tain't a Spaniard--it's Injun Joe!"
+
+The Welshman almost jumped out of his chair. In a moment he said:
+
+"It's all plain enough, now. When you talked about notching ears and
+slitting noses I judged that that was your own embellishment, because
+white men don't take that sort of revenge. But an Injun! That's a
+different matter altogether."
+
+During breakfast the talk went on, and in the course of it the old man
+said that the last thing which he and his sons had done, before going
+to bed, was to get a lantern and examine the stile and its vicinity for
+marks of blood. They found none, but captured a bulky bundle of--
+
+"Of WHAT?"
+
+If the words had been lightning they could not have leaped with a more
+stunning suddenness from Huck's blanched lips. His eyes were staring
+wide, now, and his breath suspended--waiting for the answer. The
+Welshman started--stared in return--three seconds--five seconds--ten
+--then replied:
+
+"Of burglar's tools. Why, what's the MATTER with you?"
+
+Huck sank back, panting gently, but deeply, unutterably grateful. The
+Welshman eyed him gravely, curiously--and presently said:
+
+"Yes, burglar's tools. That appears to relieve you a good deal. But
+what did give you that turn? What were YOU expecting we'd found?"
+
+Huck was in a close place--the inquiring eye was upon him--he would
+have given anything for material for a plausible answer--nothing
+suggested itself--the inquiring eye was boring deeper and deeper--a
+senseless reply offered--there was no time to weigh it, so at a venture
+he uttered it--feebly:
+
+"Sunday-school books, maybe."
+
+Poor Huck was too distressed to smile, but the old man laughed loud
+and joyously, shook up the details of his anatomy from head to foot,
+and ended by saying that such a laugh was money in a-man's pocket,
+because it cut down the doctor's bill like everything. Then he added:
+
+"Poor old chap, you're white and jaded--you ain't well a bit--no
+wonder you're a little flighty and off your balance. But you'll come
+out of it. Rest and sleep will fetch you out all right, I hope."
+
+Huck was irritated to think he had been such a goose and betrayed such
+a suspicious excitement, for he had dropped the idea that the parcel
+brought from the tavern was the treasure, as soon as he had heard the
+talk at the widow's stile. He had only thought it was not the treasure,
+however--he had not known that it wasn't--and so the suggestion of a
+captured bundle was too much for his self-possession. But on the whole
+he felt glad the little episode had happened, for now he knew beyond
+all question that that bundle was not THE bundle, and so his mind was
+at rest and exceedingly comfortable. In fact, everything seemed to be
+drifting just in the right direction, now; the treasure must be still
+in No. 2, the men would be captured and jailed that day, and he and Tom
+could seize the gold that night without any trouble or any fear of
+interruption.
+
+Just as breakfast was completed there was a knock at the door. Huck
+jumped for a hiding-place, for he had no mind to be connected even
+remotely with the late event. The Welshman admitted several ladies and
+gentlemen, among them the Widow Douglas, and noticed that groups of
+citizens were climbing up the hill--to stare at the stile. So the news
+had spread. The Welshman had to tell the story of the night to the
+visitors. The widow's gratitude for her preservation was outspoken.
+
+"Don't say a word about it, madam. There's another that you're more
+beholden to than you are to me and my boys, maybe, but he don't allow
+me to tell his name. We wouldn't have been there but for him."
+
+Of course this excited a curiosity so vast that it almost belittled
+the main matter--but the Welshman allowed it to eat into the vitals of
+his visitors, and through them be transmitted to the whole town, for he
+refused to part with his secret. When all else had been learned, the
+widow said:
+
+"I went to sleep reading in bed and slept straight through all that
+noise. Why didn't you come and wake me?"
+
+"We judged it warn't worth while. Those fellows warn't likely to come
+again--they hadn't any tools left to work with, and what was the use of
+waking you up and scaring you to death? My three negro men stood guard
+at your house all the rest of the night. They've just come back."
+
+More visitors came, and the story had to be told and retold for a
+couple of hours more.
+
+There was no Sabbath-school during day-school vacation, but everybody
+was early at church. The stirring event was well canvassed. News came
+that not a sign of the two villains had been yet discovered. When the
+sermon was finished, Judge Thatcher's wife dropped alongside of Mrs.
+Harper as she moved down the aisle with the crowd and said:
+
+"Is my Becky going to sleep all day? I just expected she would be
+tired to death."
+
+"Your Becky?"
+
+"Yes," with a startled look--"didn't she stay with you last night?"
+
+"Why, no."
+
+Mrs. Thatcher turned pale, and sank into a pew, just as Aunt Polly,
+talking briskly with a friend, passed by. Aunt Polly said:
+
+"Good-morning, Mrs. Thatcher. Good-morning, Mrs. Harper. I've got a
+boy that's turned up missing. I reckon my Tom stayed at your house last
+night--one of you. And now he's afraid to come to church. I've got to
+settle with him."
+
+Mrs. Thatcher shook her head feebly and turned paler than ever.
+
+"He didn't stay with us," said Mrs. Harper, beginning to look uneasy.
+A marked anxiety came into Aunt Polly's face.
+
+"Joe Harper, have you seen my Tom this morning?"
+
+"No'm."
+
+"When did you see him last?"
+
+Joe tried to remember, but was not sure he could say. The people had
+stopped moving out of church. Whispers passed along, and a boding
+uneasiness took possession of every countenance. Children were
+anxiously questioned, and young teachers. They all said they had not
+noticed whether Tom and Becky were on board the ferryboat on the
+homeward trip; it was dark; no one thought of inquiring if any one was
+missing. One young man finally blurted out his fear that they were
+still in the cave! Mrs. Thatcher swooned away. Aunt Polly fell to
+crying and wringing her hands.
+
+The alarm swept from lip to lip, from group to group, from street to
+street, and within five minutes the bells were wildly clanging and the
+whole town was up! The Cardiff Hill episode sank into instant
+insignificance, the burglars were forgotten, horses were saddled,
+skiffs were manned, the ferryboat ordered out, and before the horror
+was half an hour old, two hundred men were pouring down highroad and
+river toward the cave.
+
+All the long afternoon the village seemed empty and dead. Many women
+visited Aunt Polly and Mrs. Thatcher and tried to comfort them. They
+cried with them, too, and that was still better than words. All the
+tedious night the town waited for news; but when the morning dawned at
+last, all the word that came was, "Send more candles--and send food."
+Mrs. Thatcher was almost crazed; and Aunt Polly, also. Judge Thatcher
+sent messages of hope and encouragement from the cave, but they
+conveyed no real cheer.
+
+The old Welshman came home toward daylight, spattered with
+candle-grease, smeared with clay, and almost worn out. He found Huck
+still in the bed that had been provided for him, and delirious with
+fever. The physicians were all at the cave, so the Widow Douglas came
+and took charge of the patient. She said she would do her best by him,
+because, whether he was good, bad, or indifferent, he was the Lord's,
+and nothing that was the Lord's was a thing to be neglected. The
+Welshman said Huck had good spots in him, and the widow said:
+
+"You can depend on it. That's the Lord's mark. He don't leave it off.
+He never does. Puts it somewhere on every creature that comes from his
+hands."
+
+Early in the forenoon parties of jaded men began to straggle into the
+village, but the strongest of the citizens continued searching. All the
+news that could be gained was that remotenesses of the cavern were
+being ransacked that had never been visited before; that every corner
+and crevice was going to be thoroughly searched; that wherever one
+wandered through the maze of passages, lights were to be seen flitting
+hither and thither in the distance, and shoutings and pistol-shots sent
+their hollow reverberations to the ear down the sombre aisles. In one
+place, far from the section usually traversed by tourists, the names
+"BECKY & TOM" had been found traced upon the rocky wall with
+candle-smoke, and near at hand a grease-soiled bit of ribbon. Mrs.
+Thatcher recognized the ribbon and cried over it. She said it was the
+last relic she should ever have of her child; and that no other memorial
+of her could ever be so precious, because this one parted latest from
+the living body before the awful death came. Some said that now and
+then, in the cave, a far-away speck of light would glimmer, and then a
+glorious shout would burst forth and a score of men go trooping down the
+echoing aisle--and then a sickening disappointment always followed; the
+children were not there; it was only a searcher's light.
+
+Three dreadful days and nights dragged their tedious hours along, and
+the village sank into a hopeless stupor. No one had heart for anything.
+The accidental discovery, just made, that the proprietor of the
+Temperance Tavern kept liquor on his premises, scarcely fluttered the
+public pulse, tremendous as the fact was. In a lucid interval, Huck
+feebly led up to the subject of taverns, and finally asked--dimly
+dreading the worst--if anything had been discovered at the Temperance
+Tavern since he had been ill.
+
+"Yes," said the widow.
+
+Huck started up in bed, wild-eyed:
+
+"What? What was it?"
+
+"Liquor!--and the place has been shut up. Lie down, child--what a turn
+you did give me!"
+
+"Only tell me just one thing--only just one--please! Was it Tom Sawyer
+that found it?"
+
+The widow burst into tears. "Hush, hush, child, hush! I've told you
+before, you must NOT talk. You are very, very sick!"
+
+Then nothing but liquor had been found; there would have been a great
+powwow if it had been the gold. So the treasure was gone forever--gone
+forever! But what could she be crying about? Curious that she should
+cry.
+
+These thoughts worked their dim way through Huck's mind, and under the
+weariness they gave him he fell asleep. The widow said to herself:
+
+"There--he's asleep, poor wreck. Tom Sawyer find it! Pity but somebody
+could find Tom Sawyer! Ah, there ain't many left, now, that's got hope
+enough, or strength enough, either, to go on searching."
+
+
+
+CHAPTER XXXI
+
+NOW to return to Tom and Becky's share in the picnic. They tripped
+along the murky aisles with the rest of the company, visiting the
+familiar wonders of the cave--wonders dubbed with rather
+over-descriptive names, such as "The Drawing-Room," "The Cathedral,"
+"Aladdin's Palace," and so on. Presently the hide-and-seek frolicking
+began, and Tom and Becky engaged in it with zeal until the exertion
+began to grow a trifle wearisome; then they wandered down a sinuous
+avenue holding their candles aloft and reading the tangled web-work of
+names, dates, post-office addresses, and mottoes with which the rocky
+walls had been frescoed (in candle-smoke). Still drifting along and
+talking, they scarcely noticed that they were now in a part of the cave
+whose walls were not frescoed. They smoked their own names under an
+overhanging shelf and moved on. Presently they came to a place where a
+little stream of water, trickling over a ledge and carrying a limestone
+sediment with it, had, in the slow-dragging ages, formed a laced and
+ruffled Niagara in gleaming and imperishable stone. Tom squeezed his
+small body behind it in order to illuminate it for Becky's
+gratification. He found that it curtained a sort of steep natural
+stairway which was enclosed between narrow walls, and at once the
+ambition to be a discoverer seized him. Becky responded to his call,
+and they made a smoke-mark for future guidance, and started upon their
+quest. They wound this way and that, far down into the secret depths of
+the cave, made another mark, and branched off in search of novelties to
+tell the upper world about. In one place they found a spacious cavern,
+from whose ceiling depended a multitude of shining stalactites of the
+length and circumference of a man's leg; they walked all about it,
+wondering and admiring, and presently left it by one of the numerous
+passages that opened into it. This shortly brought them to a bewitching
+spring, whose basin was incrusted with a frostwork of glittering
+crystals; it was in the midst of a cavern whose walls were supported by
+many fantastic pillars which had been formed by the joining of great
+stalactites and stalagmites together, the result of the ceaseless
+water-drip of centuries. Under the roof vast knots of bats had packed
+themselves together, thousands in a bunch; the lights disturbed the
+creatures and they came flocking down by hundreds, squeaking and
+darting furiously at the candles. Tom knew their ways and the danger of
+this sort of conduct. He seized Becky's hand and hurried her into the
+first corridor that offered; and none too soon, for a bat struck
+Becky's light out with its wing while she was passing out of the
+cavern. The bats chased the children a good distance; but the fugitives
+plunged into every new passage that offered, and at last got rid of the
+perilous things. Tom found a subterranean lake, shortly, which
+stretched its dim length away until its shape was lost in the shadows.
+He wanted to explore its borders, but concluded that it would be best
+to sit down and rest awhile, first. Now, for the first time, the deep
+stillness of the place laid a clammy hand upon the spirits of the
+children. Becky said:
+
+"Why, I didn't notice, but it seems ever so long since I heard any of
+the others."
+
+"Come to think, Becky, we are away down below them--and I don't know
+how far away north, or south, or east, or whichever it is. We couldn't
+hear them here."
+
+Becky grew apprehensive.
+
+"I wonder how long we've been down here, Tom? We better start back."
+
+"Yes, I reckon we better. P'raps we better."
+
+"Can you find the way, Tom? It's all a mixed-up crookedness to me."
+
+"I reckon I could find it--but then the bats. If they put our candles
+out it will be an awful fix. Let's try some other way, so as not to go
+through there."
+
+"Well. But I hope we won't get lost. It would be so awful!" and the
+girl shuddered at the thought of the dreadful possibilities.
+
+They started through a corridor, and traversed it in silence a long
+way, glancing at each new opening, to see if there was anything
+familiar about the look of it; but they were all strange. Every time
+Tom made an examination, Becky would watch his face for an encouraging
+sign, and he would say cheerily:
+
+"Oh, it's all right. This ain't the one, but we'll come to it right
+away!"
+
+But he felt less and less hopeful with each failure, and presently
+began to turn off into diverging avenues at sheer random, in desperate
+hope of finding the one that was wanted. He still said it was "all
+right," but there was such a leaden dread at his heart that the words
+had lost their ring and sounded just as if he had said, "All is lost!"
+Becky clung to his side in an anguish of fear, and tried hard to keep
+back the tears, but they would come. At last she said:
+
+"Oh, Tom, never mind the bats, let's go back that way! We seem to get
+worse and worse off all the time."
+
+"Listen!" said he.
+
+Profound silence; silence so deep that even their breathings were
+conspicuous in the hush. Tom shouted. The call went echoing down the
+empty aisles and died out in the distance in a faint sound that
+resembled a ripple of mocking laughter.
+
+"Oh, don't do it again, Tom, it is too horrid," said Becky.
+
+"It is horrid, but I better, Becky; they might hear us, you know," and
+he shouted again.
+
+The "might" was even a chillier horror than the ghostly laughter, it
+so confessed a perishing hope. The children stood still and listened;
+but there was no result. Tom turned upon the back track at once, and
+hurried his steps. It was but a little while before a certain
+indecision in his manner revealed another fearful fact to Becky--he
+could not find his way back!
+
+"Oh, Tom, you didn't make any marks!"
+
+"Becky, I was such a fool! Such a fool! I never thought we might want
+to come back! No--I can't find the way. It's all mixed up."
+
+"Tom, Tom, we're lost! we're lost! We never can get out of this awful
+place! Oh, why DID we ever leave the others!"
+
+She sank to the ground and burst into such a frenzy of crying that Tom
+was appalled with the idea that she might die, or lose her reason. He
+sat down by her and put his arms around her; she buried her face in his
+bosom, she clung to him, she poured out her terrors, her unavailing
+regrets, and the far echoes turned them all to jeering laughter. Tom
+begged her to pluck up hope again, and she said she could not. He fell
+to blaming and abusing himself for getting her into this miserable
+situation; this had a better effect. She said she would try to hope
+again, she would get up and follow wherever he might lead if only he
+would not talk like that any more. For he was no more to blame than
+she, she said.
+
+So they moved on again--aimlessly--simply at random--all they could do
+was to move, keep moving. For a little while, hope made a show of
+reviving--not with any reason to back it, but only because it is its
+nature to revive when the spring has not been taken out of it by age
+and familiarity with failure.
+
+By-and-by Tom took Becky's candle and blew it out. This economy meant
+so much! Words were not needed. Becky understood, and her hope died
+again. She knew that Tom had a whole candle and three or four pieces in
+his pockets--yet he must economize.
+
+By-and-by, fatigue began to assert its claims; the children tried to
+pay attention, for it was dreadful to think of sitting down when time
+was grown to be so precious, moving, in some direction, in any
+direction, was at least progress and might bear fruit; but to sit down
+was to invite death and shorten its pursuit.
+
+At last Becky's frail limbs refused to carry her farther. She sat
+down. Tom rested with her, and they talked of home, and the friends
+there, and the comfortable beds and, above all, the light! Becky cried,
+and Tom tried to think of some way of comforting her, but all his
+encouragements were grown threadbare with use, and sounded like
+sarcasms. Fatigue bore so heavily upon Becky that she drowsed off to
+sleep. Tom was grateful. He sat looking into her drawn face and saw it
+grow smooth and natural under the influence of pleasant dreams; and
+by-and-by a smile dawned and rested there. The peaceful face reflected
+somewhat of peace and healing into his own spirit, and his thoughts
+wandered away to bygone times and dreamy memories. While he was deep in
+his musings, Becky woke up with a breezy little laugh--but it was
+stricken dead upon her lips, and a groan followed it.
+
+"Oh, how COULD I sleep! I wish I never, never had waked! No! No, I
+don't, Tom! Don't look so! I won't say it again."
+
+"I'm glad you've slept, Becky; you'll feel rested, now, and we'll find
+the way out."
+
+"We can try, Tom; but I've seen such a beautiful country in my dream.
+I reckon we are going there."
+
+"Maybe not, maybe not. Cheer up, Becky, and let's go on trying."
+
+They rose up and wandered along, hand in hand and hopeless. They tried
+to estimate how long they had been in the cave, but all they knew was
+that it seemed days and weeks, and yet it was plain that this could not
+be, for their candles were not gone yet. A long time after this--they
+could not tell how long--Tom said they must go softly and listen for
+dripping water--they must find a spring. They found one presently, and
+Tom said it was time to rest again. Both were cruelly tired, yet Becky
+said she thought she could go a little farther. She was surprised to
+hear Tom dissent. She could not understand it. They sat down, and Tom
+fastened his candle to the wall in front of them with some clay.
+Thought was soon busy; nothing was said for some time. Then Becky broke
+the silence:
+
+"Tom, I am so hungry!"
+
+Tom took something out of his pocket.
+
+"Do you remember this?" said he.
+
+Becky almost smiled.
+
+"It's our wedding-cake, Tom."
+
+"Yes--I wish it was as big as a barrel, for it's all we've got."
+
+"I saved it from the picnic for us to dream on, Tom, the way grown-up
+people do with wedding-cake--but it'll be our--"
+
+She dropped the sentence where it was. Tom divided the cake and Becky
+ate with good appetite, while Tom nibbled at his moiety. There was
+abundance of cold water to finish the feast with. By-and-by Becky
+suggested that they move on again. Tom was silent a moment. Then he
+said:
+
+"Becky, can you bear it if I tell you something?"
+
+Becky's face paled, but she thought she could.
+
+"Well, then, Becky, we must stay here, where there's water to drink.
+That little piece is our last candle!"
+
+Becky gave loose to tears and wailings. Tom did what he could to
+comfort her, but with little effect. At length Becky said:
+
+"Tom!"
+
+"Well, Becky?"
+
+"They'll miss us and hunt for us!"
+
+"Yes, they will! Certainly they will!"
+
+"Maybe they're hunting for us now, Tom."
+
+"Why, I reckon maybe they are. I hope they are."
+
+"When would they miss us, Tom?"
+
+"When they get back to the boat, I reckon."
+
+"Tom, it might be dark then--would they notice we hadn't come?"
+
+"I don't know. But anyway, your mother would miss you as soon as they
+got home."
+
+A frightened look in Becky's face brought Tom to his senses and he saw
+that he had made a blunder. Becky was not to have gone home that night!
+The children became silent and thoughtful. In a moment a new burst of
+grief from Becky showed Tom that the thing in his mind had struck hers
+also--that the Sabbath morning might be half spent before Mrs. Thatcher
+discovered that Becky was not at Mrs. Harper's.
+
+The children fastened their eyes upon their bit of candle and watched
+it melt slowly and pitilessly away; saw the half inch of wick stand
+alone at last; saw the feeble flame rise and fall, climb the thin
+column of smoke, linger at its top a moment, and then--the horror of
+utter darkness reigned!
+
+How long afterward it was that Becky came to a slow consciousness that
+she was crying in Tom's arms, neither could tell. All that they knew
+was, that after what seemed a mighty stretch of time, both awoke out of
+a dead stupor of sleep and resumed their miseries once more. Tom said
+it might be Sunday, now--maybe Monday. He tried to get Becky to talk,
+but her sorrows were too oppressive, all her hopes were gone. Tom said
+that they must have been missed long ago, and no doubt the search was
+going on. He would shout and maybe some one would come. He tried it;
+but in the darkness the distant echoes sounded so hideously that he
+tried it no more.
+
+The hours wasted away, and hunger came to torment the captives again.
+A portion of Tom's half of the cake was left; they divided and ate it.
+But they seemed hungrier than before. The poor morsel of food only
+whetted desire.
+
+By-and-by Tom said:
+
+"SH! Did you hear that?"
+
+Both held their breath and listened. There was a sound like the
+faintest, far-off shout. Instantly Tom answered it, and leading Becky
+by the hand, started groping down the corridor in its direction.
+Presently he listened again; again the sound was heard, and apparently
+a little nearer.
+
+"It's them!" said Tom; "they're coming! Come along, Becky--we're all
+right now!"
+
+The joy of the prisoners was almost overwhelming. Their speed was
+slow, however, because pitfalls were somewhat common, and had to be
+guarded against. They shortly came to one and had to stop. It might be
+three feet deep, it might be a hundred--there was no passing it at any
+rate. Tom got down on his breast and reached as far down as he could.
+No bottom. They must stay there and wait until the searchers came. They
+listened; evidently the distant shoutings were growing more distant! a
+moment or two more and they had gone altogether. The heart-sinking
+misery of it! Tom whooped until he was hoarse, but it was of no use. He
+talked hopefully to Becky; but an age of anxious waiting passed and no
+sounds came again.
+
+The children groped their way back to the spring. The weary time
+dragged on; they slept again, and awoke famished and woe-stricken. Tom
+believed it must be Tuesday by this time.
+
+Now an idea struck him. There were some side passages near at hand. It
+would be better to explore some of these than bear the weight of the
+heavy time in idleness. He took a kite-line from his pocket, tied it to
+a projection, and he and Becky started, Tom in the lead, unwinding the
+line as he groped along. At the end of twenty steps the corridor ended
+in a "jumping-off place." Tom got down on his knees and felt below, and
+then as far around the corner as he could reach with his hands
+conveniently; he made an effort to stretch yet a little farther to the
+right, and at that moment, not twenty yards away, a human hand, holding
+a candle, appeared from behind a rock! Tom lifted up a glorious shout,
+and instantly that hand was followed by the body it belonged to--Injun
+Joe's! Tom was paralyzed; he could not move. He was vastly gratified
+the next moment, to see the "Spaniard" take to his heels and get
+himself out of sight. Tom wondered that Joe had not recognized his
+voice and come over and killed him for testifying in court. But the
+echoes must have disguised the voice. Without doubt, that was it, he
+reasoned. Tom's fright weakened every muscle in his body. He said to
+himself that if he had strength enough to get back to the spring he
+would stay there, and nothing should tempt him to run the risk of
+meeting Injun Joe again. He was careful to keep from Becky what it was
+he had seen. He told her he had only shouted "for luck."
+
+But hunger and wretchedness rise superior to fears in the long run.
+Another tedious wait at the spring and another long sleep brought
+changes. The children awoke tortured with a raging hunger. Tom believed
+that it must be Wednesday or Thursday or even Friday or Saturday, now,
+and that the search had been given over. He proposed to explore another
+passage. He felt willing to risk Injun Joe and all other terrors. But
+Becky was very weak. She had sunk into a dreary apathy and would not be
+roused. She said she would wait, now, where she was, and die--it would
+not be long. She told Tom to go with the kite-line and explore if he
+chose; but she implored him to come back every little while and speak
+to her; and she made him promise that when the awful time came, he
+would stay by her and hold her hand until all was over.
+
+Tom kissed her, with a choking sensation in his throat, and made a
+show of being confident of finding the searchers or an escape from the
+cave; then he took the kite-line in his hand and went groping down one
+of the passages on his hands and knees, distressed with hunger and sick
+with bodings of coming doom.
+
+
+
+CHAPTER XXXII
+
+TUESDAY afternoon came, and waned to the twilight. The village of St.
+Petersburg still mourned. The lost children had not been found. Public
+prayers had been offered up for them, and many and many a private
+prayer that had the petitioner's whole heart in it; but still no good
+news came from the cave. The majority of the searchers had given up the
+quest and gone back to their daily avocations, saying that it was plain
+the children could never be found. Mrs. Thatcher was very ill, and a
+great part of the time delirious. People said it was heartbreaking to
+hear her call her child, and raise her head and listen a whole minute
+at a time, then lay it wearily down again with a moan. Aunt Polly had
+drooped into a settled melancholy, and her gray hair had grown almost
+white. The village went to its rest on Tuesday night, sad and forlorn.
+
+Away in the middle of the night a wild peal burst from the village
+bells, and in a moment the streets were swarming with frantic half-clad
+people, who shouted, "Turn out! turn out! they're found! they're
+found!" Tin pans and horns were added to the din, the population massed
+itself and moved toward the river, met the children coming in an open
+carriage drawn by shouting citizens, thronged around it, joined its
+homeward march, and swept magnificently up the main street roaring
+huzzah after huzzah!
+
+The village was illuminated; nobody went to bed again; it was the
+greatest night the little town had ever seen. During the first half-hour
+a procession of villagers filed through Judge Thatcher's house, seized
+the saved ones and kissed them, squeezed Mrs. Thatcher's hand, tried to
+speak but couldn't--and drifted out raining tears all over the place.
+
+Aunt Polly's happiness was complete, and Mrs. Thatcher's nearly so. It
+would be complete, however, as soon as the messenger dispatched with
+the great news to the cave should get the word to her husband. Tom lay
+upon a sofa with an eager auditory about him and told the history of
+the wonderful adventure, putting in many striking additions to adorn it
+withal; and closed with a description of how he left Becky and went on
+an exploring expedition; how he followed two avenues as far as his
+kite-line would reach; how he followed a third to the fullest stretch of
+the kite-line, and was about to turn back when he glimpsed a far-off
+speck that looked like daylight; dropped the line and groped toward it,
+pushed his head and shoulders through a small hole, and saw the broad
+Mississippi rolling by! And if it had only happened to be night he would
+not have seen that speck of daylight and would not have explored that
+passage any more! He told how he went back for Becky and broke the good
+news and she told him not to fret her with such stuff, for she was
+tired, and knew she was going to die, and wanted to. He described how he
+labored with her and convinced her; and how she almost died for joy when
+she had groped to where she actually saw the blue speck of daylight; how
+he pushed his way out at the hole and then helped her out; how they sat
+there and cried for gladness; how some men came along in a skiff and Tom
+hailed them and told them their situation and their famished condition;
+how the men didn't believe the wild tale at first, "because," said they,
+"you are five miles down the river below the valley the cave is in"
+--then took them aboard, rowed to a house, gave them supper, made them
+rest till two or three hours after dark and then brought them home.
+
+Before day-dawn, Judge Thatcher and the handful of searchers with him
+were tracked out, in the cave, by the twine clews they had strung
+behind them, and informed of the great news.
+
+Three days and nights of toil and hunger in the cave were not to be
+shaken off at once, as Tom and Becky soon discovered. They were
+bedridden all of Wednesday and Thursday, and seemed to grow more and
+more tired and worn, all the time. Tom got about, a little, on
+Thursday, was down-town Friday, and nearly as whole as ever Saturday;
+but Becky did not leave her room until Sunday, and then she looked as
+if she had passed through a wasting illness.
+
+Tom learned of Huck's sickness and went to see him on Friday, but
+could not be admitted to the bedroom; neither could he on Saturday or
+Sunday. He was admitted daily after that, but was warned to keep still
+about his adventure and introduce no exciting topic. The Widow Douglas
+stayed by to see that he obeyed. At home Tom learned of the Cardiff
+Hill event; also that the "ragged man's" body had eventually been found
+in the river near the ferry-landing; he had been drowned while trying
+to escape, perhaps.
+
+About a fortnight after Tom's rescue from the cave, he started off to
+visit Huck, who had grown plenty strong enough, now, to hear exciting
+talk, and Tom had some that would interest him, he thought. Judge
+Thatcher's house was on Tom's way, and he stopped to see Becky. The
+Judge and some friends set Tom to talking, and some one asked him
+ironically if he wouldn't like to go to the cave again. Tom said he
+thought he wouldn't mind it. The Judge said:
+
+"Well, there are others just like you, Tom, I've not the least doubt.
+But we have taken care of that. Nobody will get lost in that cave any
+more."
+
+"Why?"
+
+"Because I had its big door sheathed with boiler iron two weeks ago,
+and triple-locked--and I've got the keys."
+
+Tom turned as white as a sheet.
+
+"What's the matter, boy! Here, run, somebody! Fetch a glass of water!"
+
+The water was brought and thrown into Tom's face.
+
+"Ah, now you're all right. What was the matter with you, Tom?"
+
+"Oh, Judge, Injun Joe's in the cave!"
+
+
+
+CHAPTER XXXIII
+
+WITHIN a few minutes the news had spread, and a dozen skiff-loads of
+men were on their way to McDougal's cave, and the ferryboat, well
+filled with passengers, soon followed. Tom Sawyer was in the skiff that
+bore Judge Thatcher.
+
+When the cave door was unlocked, a sorrowful sight presented itself in
+the dim twilight of the place. Injun Joe lay stretched upon the ground,
+dead, with his face close to the crack of the door, as if his longing
+eyes had been fixed, to the latest moment, upon the light and the cheer
+of the free world outside. Tom was touched, for he knew by his own
+experience how this wretch had suffered. His pity was moved, but
+nevertheless he felt an abounding sense of relief and security, now,
+which revealed to him in a degree which he had not fully appreciated
+before how vast a weight of dread had been lying upon him since the day
+he lifted his voice against this bloody-minded outcast.
+
+Injun Joe's bowie-knife lay close by, its blade broken in two. The
+great foundation-beam of the door had been chipped and hacked through,
+with tedious labor; useless labor, too, it was, for the native rock
+formed a sill outside it, and upon that stubborn material the knife had
+wrought no effect; the only damage done was to the knife itself. But if
+there had been no stony obstruction there the labor would have been
+useless still, for if the beam had been wholly cut away Injun Joe could
+not have squeezed his body under the door, and he knew it. So he had
+only hacked that place in order to be doing something--in order to pass
+the weary time--in order to employ his tortured faculties. Ordinarily
+one could find half a dozen bits of candle stuck around in the crevices
+of this vestibule, left there by tourists; but there were none now. The
+prisoner had searched them out and eaten them. He had also contrived to
+catch a few bats, and these, also, he had eaten, leaving only their
+claws. The poor unfortunate had starved to death. In one place, near at
+hand, a stalagmite had been slowly growing up from the ground for ages,
+builded by the water-drip from a stalactite overhead. The captive had
+broken off the stalagmite, and upon the stump had placed a stone,
+wherein he had scooped a shallow hollow to catch the precious drop
+that fell once in every three minutes with the dreary regularity of a
+clock-tick--a dessertspoonful once in four and twenty hours. That drop
+was falling when the Pyramids were new; when Troy fell; when the
+foundations of Rome were laid; when Christ was crucified; when the
+Conqueror created the British empire; when Columbus sailed; when the
+massacre at Lexington was "news." It is falling now; it will still be
+falling when all these things shall have sunk down the afternoon of
+history, and the twilight of tradition, and been swallowed up in the
+thick night of oblivion. Has everything a purpose and a mission? Did
+this drop fall patiently during five thousand years to be ready for
+this flitting human insect's need? and has it another important object
+to accomplish ten thousand years to come? No matter. It is many and
+many a year since the hapless half-breed scooped out the stone to catch
+the priceless drops, but to this day the tourist stares longest at that
+pathetic stone and that slow-dropping water when he comes to see the
+wonders of McDougal's cave. Injun Joe's cup stands first in the list of
+the cavern's marvels; even "Aladdin's Palace" cannot rival it.
+
+Injun Joe was buried near the mouth of the cave; and people flocked
+there in boats and wagons from the towns and from all the farms and
+hamlets for seven miles around; they brought their children, and all
+sorts of provisions, and confessed that they had had almost as
+satisfactory a time at the funeral as they could have had at the
+hanging.
+
+This funeral stopped the further growth of one thing--the petition to
+the governor for Injun Joe's pardon. The petition had been largely
+signed; many tearful and eloquent meetings had been held, and a
+committee of sappy women been appointed to go in deep mourning and wail
+around the governor, and implore him to be a merciful ass and trample
+his duty under foot. Injun Joe was believed to have killed five
+citizens of the village, but what of that? If he had been Satan himself
+there would have been plenty of weaklings ready to scribble their names
+to a pardon-petition, and drip a tear on it from their permanently
+impaired and leaky water-works.
+
+The morning after the funeral Tom took Huck to a private place to have
+an important talk. Huck had learned all about Tom's adventure from the
+Welshman and the Widow Douglas, by this time, but Tom said he reckoned
+there was one thing they had not told him; that thing was what he
+wanted to talk about now. Huck's face saddened. He said:
+
+"I know what it is. You got into No. 2 and never found anything but
+whiskey. Nobody told me it was you; but I just knowed it must 'a' ben
+you, soon as I heard 'bout that whiskey business; and I knowed you
+hadn't got the money becuz you'd 'a' got at me some way or other and
+told me even if you was mum to everybody else. Tom, something's always
+told me we'd never get holt of that swag."
+
+"Why, Huck, I never told on that tavern-keeper. YOU know his tavern
+was all right the Saturday I went to the picnic. Don't you remember you
+was to watch there that night?"
+
+"Oh yes! Why, it seems 'bout a year ago. It was that very night that I
+follered Injun Joe to the widder's."
+
+"YOU followed him?"
+
+"Yes--but you keep mum. I reckon Injun Joe's left friends behind him,
+and I don't want 'em souring on me and doing me mean tricks. If it
+hadn't ben for me he'd be down in Texas now, all right."
+
+Then Huck told his entire adventure in confidence to Tom, who had only
+heard of the Welshman's part of it before.
+
+"Well," said Huck, presently, coming back to the main question,
+"whoever nipped the whiskey in No. 2, nipped the money, too, I reckon
+--anyways it's a goner for us, Tom."
+
+"Huck, that money wasn't ever in No. 2!"
+
+"What!" Huck searched his comrade's face keenly. "Tom, have you got on
+the track of that money again?"
+
+"Huck, it's in the cave!"
+
+Huck's eyes blazed.
+
+"Say it again, Tom."
+
+"The money's in the cave!"
+
+"Tom--honest injun, now--is it fun, or earnest?"
+
+"Earnest, Huck--just as earnest as ever I was in my life. Will you go
+in there with me and help get it out?"
+
+"I bet I will! I will if it's where we can blaze our way to it and not
+get lost."
+
+"Huck, we can do that without the least little bit of trouble in the
+world."
+
+"Good as wheat! What makes you think the money's--"
+
+"Huck, you just wait till we get in there. If we don't find it I'll
+agree to give you my drum and every thing I've got in the world. I
+will, by jings."
+
+"All right--it's a whiz. When do you say?"
+
+"Right now, if you say it. Are you strong enough?"
+
+"Is it far in the cave? I ben on my pins a little, three or four days,
+now, but I can't walk more'n a mile, Tom--least I don't think I could."
+
+"It's about five mile into there the way anybody but me would go,
+Huck, but there's a mighty short cut that they don't anybody but me
+know about. Huck, I'll take you right to it in a skiff. I'll float the
+skiff down there, and I'll pull it back again all by myself. You
+needn't ever turn your hand over."
+
+"Less start right off, Tom."
+
+"All right. We want some bread and meat, and our pipes, and a little
+bag or two, and two or three kite-strings, and some of these
+new-fangled things they call lucifer matches. I tell you, many's
+the time I wished I had some when I was in there before."
+
+A trifle after noon the boys borrowed a small skiff from a citizen who
+was absent, and got under way at once. When they were several miles
+below "Cave Hollow," Tom said:
+
+"Now you see this bluff here looks all alike all the way down from the
+cave hollow--no houses, no wood-yards, bushes all alike. But do you see
+that white place up yonder where there's been a landslide? Well, that's
+one of my marks. We'll get ashore, now."
+
+They landed.
+
+"Now, Huck, where we're a-standing you could touch that hole I got out
+of with a fishing-pole. See if you can find it."
+
+Huck searched all the place about, and found nothing. Tom proudly
+marched into a thick clump of sumach bushes and said:
+
+"Here you are! Look at it, Huck; it's the snuggest hole in this
+country. You just keep mum about it. All along I've been wanting to be
+a robber, but I knew I'd got to have a thing like this, and where to
+run across it was the bother. We've got it now, and we'll keep it
+quiet, only we'll let Joe Harper and Ben Rogers in--because of course
+there's got to be a Gang, or else there wouldn't be any style about it.
+Tom Sawyer's Gang--it sounds splendid, don't it, Huck?"
+
+"Well, it just does, Tom. And who'll we rob?"
+
+"Oh, most anybody. Waylay people--that's mostly the way."
+
+"And kill them?"
+
+"No, not always. Hive them in the cave till they raise a ransom."
+
+"What's a ransom?"
+
+"Money. You make them raise all they can, off'n their friends; and
+after you've kept them a year, if it ain't raised then you kill them.
+That's the general way. Only you don't kill the women. You shut up the
+women, but you don't kill them. They're always beautiful and rich, and
+awfully scared. You take their watches and things, but you always take
+your hat off and talk polite. They ain't anybody as polite as robbers
+--you'll see that in any book. Well, the women get to loving you, and
+after they've been in the cave a week or two weeks they stop crying and
+after that you couldn't get them to leave. If you drove them out they'd
+turn right around and come back. It's so in all the books."
+
+"Why, it's real bully, Tom. I believe it's better'n to be a pirate."
+
+"Yes, it's better in some ways, because it's close to home and
+circuses and all that."
+
+By this time everything was ready and the boys entered the hole, Tom
+in the lead. They toiled their way to the farther end of the tunnel,
+then made their spliced kite-strings fast and moved on. A few steps
+brought them to the spring, and Tom felt a shudder quiver all through
+him. He showed Huck the fragment of candle-wick perched on a lump of
+clay against the wall, and described how he and Becky had watched the
+flame struggle and expire.
+
+The boys began to quiet down to whispers, now, for the stillness and
+gloom of the place oppressed their spirits. They went on, and presently
+entered and followed Tom's other corridor until they reached the
+"jumping-off place." The candles revealed the fact that it was not
+really a precipice, but only a steep clay hill twenty or thirty feet
+high. Tom whispered:
+
+"Now I'll show you something, Huck."
+
+He held his candle aloft and said:
+
+"Look as far around the corner as you can. Do you see that? There--on
+the big rock over yonder--done with candle-smoke."
+
+"Tom, it's a CROSS!"
+
+"NOW where's your Number Two? 'UNDER THE CROSS,' hey? Right yonder's
+where I saw Injun Joe poke up his candle, Huck!"
+
+Huck stared at the mystic sign awhile, and then said with a shaky voice:
+
+"Tom, less git out of here!"
+
+"What! and leave the treasure?"
+
+"Yes--leave it. Injun Joe's ghost is round about there, certain."
+
+"No it ain't, Huck, no it ain't. It would ha'nt the place where he
+died--away out at the mouth of the cave--five mile from here."
+
+"No, Tom, it wouldn't. It would hang round the money. I know the ways
+of ghosts, and so do you."
+
+Tom began to fear that Huck was right. Misgivings gathered in his
+mind. But presently an idea occurred to him--
+
+"Lookyhere, Huck, what fools we're making of ourselves! Injun Joe's
+ghost ain't a going to come around where there's a cross!"
+
+The point was well taken. It had its effect.
+
+"Tom, I didn't think of that. But that's so. It's luck for us, that
+cross is. I reckon we'll climb down there and have a hunt for that box."
+
+Tom went first, cutting rude steps in the clay hill as he descended.
+Huck followed. Four avenues opened out of the small cavern which the
+great rock stood in. The boys examined three of them with no result.
+They found a small recess in the one nearest the base of the rock, with
+a pallet of blankets spread down in it; also an old suspender, some
+bacon rind, and the well-gnawed bones of two or three fowls. But there
+was no money-box. The lads searched and researched this place, but in
+vain. Tom said:
+
+"He said UNDER the cross. Well, this comes nearest to being under the
+cross. It can't be under the rock itself, because that sets solid on
+the ground."
+
+They searched everywhere once more, and then sat down discouraged.
+Huck could suggest nothing. By-and-by Tom said:
+
+"Lookyhere, Huck, there's footprints and some candle-grease on the
+clay about one side of this rock, but not on the other sides. Now,
+what's that for? I bet you the money IS under the rock. I'm going to
+dig in the clay."
+
+"That ain't no bad notion, Tom!" said Huck with animation.
+
+Tom's "real Barlow" was out at once, and he had not dug four inches
+before he struck wood.
+
+"Hey, Huck!--you hear that?"
+
+Huck began to dig and scratch now. Some boards were soon uncovered and
+removed. They had concealed a natural chasm which led under the rock.
+Tom got into this and held his candle as far under the rock as he
+could, but said he could not see to the end of the rift. He proposed to
+explore. He stooped and passed under; the narrow way descended
+gradually. He followed its winding course, first to the right, then to
+the left, Huck at his heels. Tom turned a short curve, by-and-by, and
+exclaimed:
+
+"My goodness, Huck, lookyhere!"
+
+It was the treasure-box, sure enough, occupying a snug little cavern,
+along with an empty powder-keg, a couple of guns in leather cases, two
+or three pairs of old moccasins, a leather belt, and some other rubbish
+well soaked with the water-drip.
+
+"Got it at last!" said Huck, ploughing among the tarnished coins with
+his hand. "My, but we're rich, Tom!"
+
+"Huck, I always reckoned we'd get it. It's just too good to believe,
+but we HAVE got it, sure! Say--let's not fool around here. Let's snake
+it out. Lemme see if I can lift the box."
+
+It weighed about fifty pounds. Tom could lift it, after an awkward
+fashion, but could not carry it conveniently.
+
+"I thought so," he said; "THEY carried it like it was heavy, that day
+at the ha'nted house. I noticed that. I reckon I was right to think of
+fetching the little bags along."
+
+The money was soon in the bags and the boys took it up to the cross
+rock.
+
+"Now less fetch the guns and things," said Huck.
+
+"No, Huck--leave them there. They're just the tricks to have when we
+go to robbing. We'll keep them there all the time, and we'll hold our
+orgies there, too. It's an awful snug place for orgies."
+
+"What orgies?"
+
+"I dono. But robbers always have orgies, and of course we've got to
+have them, too. Come along, Huck, we've been in here a long time. It's
+getting late, I reckon. I'm hungry, too. We'll eat and smoke when we
+get to the skiff."
+
+They presently emerged into the clump of sumach bushes, looked warily
+out, found the coast clear, and were soon lunching and smoking in the
+skiff. As the sun dipped toward the horizon they pushed out and got
+under way. Tom skimmed up the shore through the long twilight, chatting
+cheerily with Huck, and landed shortly after dark.
+
+"Now, Huck," said Tom, "we'll hide the money in the loft of the
+widow's woodshed, and I'll come up in the morning and we'll count it
+and divide, and then we'll hunt up a place out in the woods for it
+where it will be safe. Just you lay quiet here and watch the stuff till
+I run and hook Benny Taylor's little wagon; I won't be gone a minute."
+
+He disappeared, and presently returned with the wagon, put the two
+small sacks into it, threw some old rags on top of them, and started
+off, dragging his cargo behind him. When the boys reached the
+Welshman's house, they stopped to rest. Just as they were about to move
+on, the Welshman stepped out and said:
+
+"Hallo, who's that?"
+
+"Huck and Tom Sawyer."
+
+"Good! Come along with me, boys, you are keeping everybody waiting.
+Here--hurry up, trot ahead--I'll haul the wagon for you. Why, it's not
+as light as it might be. Got bricks in it?--or old metal?"
+
+"Old metal," said Tom.
+
+"I judged so; the boys in this town will take more trouble and fool
+away more time hunting up six bits' worth of old iron to sell to the
+foundry than they would to make twice the money at regular work. But
+that's human nature--hurry along, hurry along!"
+
+The boys wanted to know what the hurry was about.
+
+"Never mind; you'll see, when we get to the Widow Douglas'."
+
+Huck said with some apprehension--for he was long used to being
+falsely accused:
+
+"Mr. Jones, we haven't been doing nothing."
+
+The Welshman laughed.
+
+"Well, I don't know, Huck, my boy. I don't know about that. Ain't you
+and the widow good friends?"
+
+"Yes. Well, she's ben good friends to me, anyway."
+
+"All right, then. What do you want to be afraid for?"
+
+This question was not entirely answered in Huck's slow mind before he
+found himself pushed, along with Tom, into Mrs. Douglas' drawing-room.
+Mr. Jones left the wagon near the door and followed.
+
+The place was grandly lighted, and everybody that was of any
+consequence in the village was there. The Thatchers were there, the
+Harpers, the Rogerses, Aunt Polly, Sid, Mary, the minister, the editor,
+and a great many more, and all dressed in their best. The widow
+received the boys as heartily as any one could well receive two such
+looking beings. They were covered with clay and candle-grease. Aunt
+Polly blushed crimson with humiliation, and frowned and shook her head
+at Tom. Nobody suffered half as much as the two boys did, however. Mr.
+Jones said:
+
+"Tom wasn't at home, yet, so I gave him up; but I stumbled on him and
+Huck right at my door, and so I just brought them along in a hurry."
+
+"And you did just right," said the widow. "Come with me, boys."
+
+She took them to a bedchamber and said:
+
+"Now wash and dress yourselves. Here are two new suits of clothes
+--shirts, socks, everything complete. They're Huck's--no, no thanks,
+Huck--Mr. Jones bought one and I the other. But they'll fit both of you.
+Get into them. We'll wait--come down when you are slicked up enough."
+
+Then she left.
+
+
+
+CHAPTER XXXIV
+
+HUCK said: "Tom, we can slope, if we can find a rope. The window ain't
+high from the ground."
+
+"Shucks! what do you want to slope for?"
+
+"Well, I ain't used to that kind of a crowd. I can't stand it. I ain't
+going down there, Tom."
+
+"Oh, bother! It ain't anything. I don't mind it a bit. I'll take care
+of you."
+
+Sid appeared.
+
+"Tom," said he, "auntie has been waiting for you all the afternoon.
+Mary got your Sunday clothes ready, and everybody's been fretting about
+you. Say--ain't this grease and clay, on your clothes?"
+
+"Now, Mr. Siddy, you jist 'tend to your own business. What's all this
+blow-out about, anyway?"
+
+"It's one of the widow's parties that she's always having. This time
+it's for the Welshman and his sons, on account of that scrape they
+helped her out of the other night. And say--I can tell you something,
+if you want to know."
+
+"Well, what?"
+
+"Why, old Mr. Jones is going to try to spring something on the people
+here to-night, but I overheard him tell auntie to-day about it, as a
+secret, but I reckon it's not much of a secret now. Everybody knows
+--the widow, too, for all she tries to let on she don't. Mr. Jones was
+bound Huck should be here--couldn't get along with his grand secret
+without Huck, you know!"
+
+"Secret about what, Sid?"
+
+"About Huck tracking the robbers to the widow's. I reckon Mr. Jones
+was going to make a grand time over his surprise, but I bet you it will
+drop pretty flat."
+
+Sid chuckled in a very contented and satisfied way.
+
+"Sid, was it you that told?"
+
+"Oh, never mind who it was. SOMEBODY told--that's enough."
+
+"Sid, there's only one person in this town mean enough to do that, and
+that's you. If you had been in Huck's place you'd 'a' sneaked down the
+hill and never told anybody on the robbers. You can't do any but mean
+things, and you can't bear to see anybody praised for doing good ones.
+There--no thanks, as the widow says"--and Tom cuffed Sid's ears and
+helped him to the door with several kicks. "Now go and tell auntie if
+you dare--and to-morrow you'll catch it!"
+
+Some minutes later the widow's guests were at the supper-table, and a
+dozen children were propped up at little side-tables in the same room,
+after the fashion of that country and that day. At the proper time Mr.
+Jones made his little speech, in which he thanked the widow for the
+honor she was doing himself and his sons, but said that there was
+another person whose modesty--
+
+And so forth and so on. He sprung his secret about Huck's share in the
+adventure in the finest dramatic manner he was master of, but the
+surprise it occasioned was largely counterfeit and not as clamorous and
+effusive as it might have been under happier circumstances. However,
+the widow made a pretty fair show of astonishment, and heaped so many
+compliments and so much gratitude upon Huck that he almost forgot the
+nearly intolerable discomfort of his new clothes in the entirely
+intolerable discomfort of being set up as a target for everybody's gaze
+and everybody's laudations.
+
+The widow said she meant to give Huck a home under her roof and have
+him educated; and that when she could spare the money she would start
+him in business in a modest way. Tom's chance was come. He said:
+
+"Huck don't need it. Huck's rich."
+
+Nothing but a heavy strain upon the good manners of the company kept
+back the due and proper complimentary laugh at this pleasant joke. But
+the silence was a little awkward. Tom broke it:
+
+"Huck's got money. Maybe you don't believe it, but he's got lots of
+it. Oh, you needn't smile--I reckon I can show you. You just wait a
+minute."
+
+Tom ran out of doors. The company looked at each other with a
+perplexed interest--and inquiringly at Huck, who was tongue-tied.
+
+"Sid, what ails Tom?" said Aunt Polly. "He--well, there ain't ever any
+making of that boy out. I never--"
+
+Tom entered, struggling with the weight of his sacks, and Aunt Polly
+did not finish her sentence. Tom poured the mass of yellow coin upon
+the table and said:
+
+"There--what did I tell you? Half of it's Huck's and half of it's mine!"
+
+The spectacle took the general breath away. All gazed, nobody spoke
+for a moment. Then there was a unanimous call for an explanation. Tom
+said he could furnish it, and he did. The tale was long, but brimful of
+interest. There was scarcely an interruption from any one to break the
+charm of its flow. When he had finished, Mr. Jones said:
+
+"I thought I had fixed up a little surprise for this occasion, but it
+don't amount to anything now. This one makes it sing mighty small, I'm
+willing to allow."
+
+The money was counted. The sum amounted to a little over twelve
+thousand dollars. It was more than any one present had ever seen at one
+time before, though several persons were there who were worth
+considerably more than that in property.
+
+
+
+CHAPTER XXXV
+
+THE reader may rest satisfied that Tom's and Huck's windfall made a
+mighty stir in the poor little village of St. Petersburg. So vast a
+sum, all in actual cash, seemed next to incredible. It was talked
+about, gloated over, glorified, until the reason of many of the
+citizens tottered under the strain of the unhealthy excitement. Every
+"haunted" house in St. Petersburg and the neighboring villages was
+dissected, plank by plank, and its foundations dug up and ransacked for
+hidden treasure--and not by boys, but men--pretty grave, unromantic
+men, too, some of them. Wherever Tom and Huck appeared they were
+courted, admired, stared at. The boys were not able to remember that
+their remarks had possessed weight before; but now their sayings were
+treasured and repeated; everything they did seemed somehow to be
+regarded as remarkable; they had evidently lost the power of doing and
+saying commonplace things; moreover, their past history was raked up
+and discovered to bear marks of conspicuous originality. The village
+paper published biographical sketches of the boys.
+
+The Widow Douglas put Huck's money out at six per cent., and Judge
+Thatcher did the same with Tom's at Aunt Polly's request. Each lad had
+an income, now, that was simply prodigious--a dollar for every week-day
+in the year and half of the Sundays. It was just what the minister got
+--no, it was what he was promised--he generally couldn't collect it. A
+dollar and a quarter a week would board, lodge, and school a boy in
+those old simple days--and clothe him and wash him, too, for that
+matter.
+
+Judge Thatcher had conceived a great opinion of Tom. He said that no
+commonplace boy would ever have got his daughter out of the cave. When
+Becky told her father, in strict confidence, how Tom had taken her
+whipping at school, the Judge was visibly moved; and when she pleaded
+grace for the mighty lie which Tom had told in order to shift that
+whipping from her shoulders to his own, the Judge said with a fine
+outburst that it was a noble, a generous, a magnanimous lie--a lie that
+was worthy to hold up its head and march down through history breast to
+breast with George Washington's lauded Truth about the hatchet! Becky
+thought her father had never looked so tall and so superb as when he
+walked the floor and stamped his foot and said that. She went straight
+off and told Tom about it.
+
+Judge Thatcher hoped to see Tom a great lawyer or a great soldier some
+day. He said he meant to look to it that Tom should be admitted to the
+National Military Academy and afterward trained in the best law school
+in the country, in order that he might be ready for either career or
+both.
+
+Huck Finn's wealth and the fact that he was now under the Widow
+Douglas' protection introduced him into society--no, dragged him into
+it, hurled him into it--and his sufferings were almost more than he
+could bear. The widow's servants kept him clean and neat, combed and
+brushed, and they bedded him nightly in unsympathetic sheets that had
+not one little spot or stain which he could press to his heart and know
+for a friend. He had to eat with a knife and fork; he had to use
+napkin, cup, and plate; he had to learn his book, he had to go to
+church; he had to talk so properly that speech was become insipid in
+his mouth; whithersoever he turned, the bars and shackles of
+civilization shut him in and bound him hand and foot.
+
+He bravely bore his miseries three weeks, and then one day turned up
+missing. For forty-eight hours the widow hunted for him everywhere in
+great distress. The public were profoundly concerned; they searched
+high and low, they dragged the river for his body. Early the third
+morning Tom Sawyer wisely went poking among some old empty hogsheads
+down behind the abandoned slaughter-house, and in one of them he found
+the refugee. Huck had slept there; he had just breakfasted upon some
+stolen odds and ends of food, and was lying off, now, in comfort, with
+his pipe. He was unkempt, uncombed, and clad in the same old ruin of
+rags that had made him picturesque in the days when he was free and
+happy. Tom routed him out, told him the trouble he had been causing,
+and urged him to go home. Huck's face lost its tranquil content, and
+took a melancholy cast. He said:
+
+"Don't talk about it, Tom. I've tried it, and it don't work; it don't
+work, Tom. It ain't for me; I ain't used to it. The widder's good to
+me, and friendly; but I can't stand them ways. She makes me get up just
+at the same time every morning; she makes me wash, they comb me all to
+thunder; she won't let me sleep in the woodshed; I got to wear them
+blamed clothes that just smothers me, Tom; they don't seem to any air
+git through 'em, somehow; and they're so rotten nice that I can't set
+down, nor lay down, nor roll around anywher's; I hain't slid on a
+cellar-door for--well, it 'pears to be years; I got to go to church and
+sweat and sweat--I hate them ornery sermons! I can't ketch a fly in
+there, I can't chaw. I got to wear shoes all Sunday. The widder eats by
+a bell; she goes to bed by a bell; she gits up by a bell--everything's
+so awful reg'lar a body can't stand it."
+
+"Well, everybody does that way, Huck."
+
+"Tom, it don't make no difference. I ain't everybody, and I can't
+STAND it. It's awful to be tied up so. And grub comes too easy--I don't
+take no interest in vittles, that way. I got to ask to go a-fishing; I
+got to ask to go in a-swimming--dern'd if I hain't got to ask to do
+everything. Well, I'd got to talk so nice it wasn't no comfort--I'd got
+to go up in the attic and rip out awhile, every day, to git a taste in
+my mouth, or I'd a died, Tom. The widder wouldn't let me smoke; she
+wouldn't let me yell, she wouldn't let me gape, nor stretch, nor
+scratch, before folks--" [Then with a spasm of special irritation and
+injury]--"And dad fetch it, she prayed all the time! I never see such a
+woman! I HAD to shove, Tom--I just had to. And besides, that school's
+going to open, and I'd a had to go to it--well, I wouldn't stand THAT,
+Tom. Looky here, Tom, being rich ain't what it's cracked up to be. It's
+just worry and worry, and sweat and sweat, and a-wishing you was dead
+all the time. Now these clothes suits me, and this bar'l suits me, and
+I ain't ever going to shake 'em any more. Tom, I wouldn't ever got into
+all this trouble if it hadn't 'a' ben for that money; now you just take
+my sheer of it along with your'n, and gimme a ten-center sometimes--not
+many times, becuz I don't give a dern for a thing 'thout it's tollable
+hard to git--and you go and beg off for me with the widder."
+
+"Oh, Huck, you know I can't do that. 'Tain't fair; and besides if
+you'll try this thing just a while longer you'll come to like it."
+
+"Like it! Yes--the way I'd like a hot stove if I was to set on it long
+enough. No, Tom, I won't be rich, and I won't live in them cussed
+smothery houses. I like the woods, and the river, and hogsheads, and
+I'll stick to 'em, too. Blame it all! just as we'd got guns, and a
+cave, and all just fixed to rob, here this dern foolishness has got to
+come up and spile it all!"
+
+Tom saw his opportunity--
+
+"Lookyhere, Huck, being rich ain't going to keep me back from turning
+robber."
+
+"No! Oh, good-licks; are you in real dead-wood earnest, Tom?"
+
+"Just as dead earnest as I'm sitting here. But Huck, we can't let you
+into the gang if you ain't respectable, you know."
+
+Huck's joy was quenched.
+
+"Can't let me in, Tom? Didn't you let me go for a pirate?"
+
+"Yes, but that's different. A robber is more high-toned than what a
+pirate is--as a general thing. In most countries they're awful high up
+in the nobility--dukes and such."
+
+"Now, Tom, hain't you always ben friendly to me? You wouldn't shet me
+out, would you, Tom? You wouldn't do that, now, WOULD you, Tom?"
+
+"Huck, I wouldn't want to, and I DON'T want to--but what would people
+say? Why, they'd say, 'Mph! Tom Sawyer's Gang! pretty low characters in
+it!' They'd mean you, Huck. You wouldn't like that, and I wouldn't."
+
+Huck was silent for some time, engaged in a mental struggle. Finally
+he said:
+
+"Well, I'll go back to the widder for a month and tackle it and see if
+I can come to stand it, if you'll let me b'long to the gang, Tom."
+
+"All right, Huck, it's a whiz! Come along, old chap, and I'll ask the
+widow to let up on you a little, Huck."
+
+"Will you, Tom--now will you? That's good. If she'll let up on some of
+the roughest things, I'll smoke private and cuss private, and crowd
+through or bust. When you going to start the gang and turn robbers?"
+
+"Oh, right off. We'll get the boys together and have the initiation
+to-night, maybe."
+
+"Have the which?"
+
+"Have the initiation."
+
+"What's that?"
+
+"It's to swear to stand by one another, and never tell the gang's
+secrets, even if you're chopped all to flinders, and kill anybody and
+all his family that hurts one of the gang."
+
+"That's gay--that's mighty gay, Tom, I tell you."
+
+"Well, I bet it is. And all that swearing's got to be done at
+midnight, in the lonesomest, awfulest place you can find--a ha'nted
+house is the best, but they're all ripped up now."
+
+"Well, midnight's good, anyway, Tom."
+
+"Yes, so it is. And you've got to swear on a coffin, and sign it with
+blood."
+
+"Now, that's something LIKE! Why, it's a million times bullier than
+pirating. I'll stick to the widder till I rot, Tom; and if I git to be
+a reg'lar ripper of a robber, and everybody talking 'bout it, I reckon
+she'll be proud she snaked me in out of the wet."
+
+
+
+CONCLUSION
+
+SO endeth this chronicle. It being strictly a history of a BOY, it
+must stop here; the story could not go much further without becoming
+the history of a MAN. When one writes a novel about grown people, he
+knows exactly where to stop--that is, with a marriage; but when he
+writes of juveniles, he must stop where he best can.
+
+Most of the characters that perform in this book still live, and are
+prosperous and happy. Some day it may seem worth while to take up the
+story of the younger ones again and see what sort of men and women they
+turned out to be; therefore it will be wisest not to reveal any of that
+part of their lives at present.
diff --git a/src/net/timeout_test.go b/src/net/timeout_test.go
index d80e478c77..86010927b3 100644
--- a/src/net/timeout_test.go
+++ b/src/net/timeout_test.go
@@ -6,6 +6,7 @@ package net
import (
"fmt"
+ "internal/testenv"
"io"
"io/ioutil"
"net/internal/socktest"
@@ -111,7 +112,9 @@ var dialTimeoutMaxDurationTests = []struct {
}
func TestDialTimeoutMaxDuration(t *testing.T) {
- t.Parallel()
+ if runtime.GOOS == "openbsd" {
+ testenv.SkipFlaky(t, 15157)
+ }
ln, err := newLocalListener("tcp")
if err != nil {
@@ -311,8 +314,6 @@ var readTimeoutTests = []struct {
}
func TestReadTimeout(t *testing.T) {
- t.Parallel()
-
switch runtime.GOOS {
case "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
diff --git a/src/net/udpsock.go b/src/net/udpsock.go
index e7e9796668..980f67c81f 100644
--- a/src/net/udpsock.go
+++ b/src/net/udpsock.go
@@ -4,7 +4,10 @@
package net
-import "syscall"
+import (
+ "context"
+ "syscall"
+)
// UDPAddr represents the address of a UDP end point.
type UDPAddr struct {
@@ -55,7 +58,7 @@ func ResolveUDPAddr(net, addr string) (*UDPAddr, error) {
default:
return nil, UnknownNetworkError(net)
}
- addrs, err := internetAddrList(net, addr, noDeadline)
+ addrs, err := internetAddrList(context.Background(), net, addr)
if err != nil {
return nil, err
}
@@ -181,7 +184,7 @@ func DialUDP(net string, laddr, raddr *UDPAddr) (*UDPConn, error) {
if raddr == nil {
return nil, &OpError{Op: "dial", Net: net, Source: laddr.opAddr(), Addr: nil, Err: errMissingAddress}
}
- c, err := dialUDP(net, laddr, raddr, noDeadline)
+ c, err := dialUDP(context.Background(), net, laddr, raddr)
if err != nil {
return nil, &OpError{Op: "dial", Net: net, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: err}
}
@@ -204,7 +207,7 @@ func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) {
if laddr == nil {
laddr = &UDPAddr{}
}
- c, err := listenUDP(net, laddr)
+ c, err := listenUDP(context.Background(), net, laddr)
if err != nil {
return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: laddr.opAddr(), Err: err}
}
@@ -231,7 +234,7 @@ func ListenMulticastUDP(network string, ifi *Interface, gaddr *UDPAddr) (*UDPCon
if gaddr == nil || gaddr.IP == nil {
return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: gaddr.opAddr(), Err: errMissingAddress}
}
- c, err := listenMulticastUDP(network, ifi, gaddr)
+ c, err := listenMulticastUDP(context.Background(), network, ifi, gaddr)
if err != nil {
return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: gaddr.opAddr(), Err: err}
}
diff --git a/src/net/udpsock_plan9.go b/src/net/udpsock_plan9.go
index 5f15427064..666f20622f 100644
--- a/src/net/udpsock_plan9.go
+++ b/src/net/udpsock_plan9.go
@@ -5,10 +5,10 @@
package net
import (
+ "context"
"errors"
"os"
"syscall"
- "time"
)
func (c *UDPConn) readFrom(b []byte) (n int, addr *UDPAddr, err error) {
@@ -55,11 +55,8 @@ func (c *UDPConn) writeMsg(b, oob []byte, addr *UDPAddr) (n, oobn int, err error
return 0, 0, syscall.EPLAN9
}
-func dialUDP(net string, laddr, raddr *UDPAddr, deadline time.Time) (*UDPConn, error) {
- if !deadline.IsZero() {
- panic("net.dialUDP: deadline not implemented on Plan 9")
- }
- fd, err := dialPlan9(net, laddr, raddr)
+func dialUDP(ctx context.Context, net string, laddr, raddr *UDPAddr) (*UDPConn, error) {
+ fd, err := dialPlan9(ctx, net, laddr, raddr)
if err != nil {
return nil, err
}
@@ -94,8 +91,8 @@ func unmarshalUDPHeader(b []byte) (*udpHeader, []byte) {
return h, b
}
-func listenUDP(network string, laddr *UDPAddr) (*UDPConn, error) {
- l, err := listenPlan9(network, laddr)
+func listenUDP(ctx context.Context, network string, laddr *UDPAddr) (*UDPConn, error) {
+ l, err := listenPlan9(ctx, network, laddr)
if err != nil {
return nil, err
}
@@ -111,6 +108,6 @@ func listenUDP(network string, laddr *UDPAddr) (*UDPConn, error) {
return newUDPConn(fd), err
}
-func listenMulticastUDP(network string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) {
+func listenMulticastUDP(ctx context.Context, network string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) {
return nil, syscall.EPLAN9
}
diff --git a/src/net/udpsock_posix.go b/src/net/udpsock_posix.go
index 4d3255c996..4924801ebb 100644
--- a/src/net/udpsock_posix.go
+++ b/src/net/udpsock_posix.go
@@ -7,8 +7,8 @@
package net
import (
+ "context"
"syscall"
- "time"
)
func sockaddrToUDP(sa syscall.Sockaddr) Addr {
@@ -90,24 +90,24 @@ func (c *UDPConn) writeMsg(b, oob []byte, addr *UDPAddr) (n, oobn int, err error
return c.fd.writeMsg(b, oob, sa)
}
-func dialUDP(net string, laddr, raddr *UDPAddr, deadline time.Time) (*UDPConn, error) {
- fd, err := internetSocket(net, laddr, raddr, deadline, syscall.SOCK_DGRAM, 0, "dial", noCancel)
+func dialUDP(ctx context.Context, net string, laddr, raddr *UDPAddr) (*UDPConn, error) {
+ fd, err := internetSocket(ctx, net, laddr, raddr, syscall.SOCK_DGRAM, 0, "dial")
if err != nil {
return nil, err
}
return newUDPConn(fd), nil
}
-func listenUDP(network string, laddr *UDPAddr) (*UDPConn, error) {
- fd, err := internetSocket(network, laddr, nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", noCancel)
+func listenUDP(ctx context.Context, network string, laddr *UDPAddr) (*UDPConn, error) {
+ fd, err := internetSocket(ctx, network, laddr, nil, syscall.SOCK_DGRAM, 0, "listen")
if err != nil {
return nil, err
}
return newUDPConn(fd), nil
}
-func listenMulticastUDP(network string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) {
- fd, err := internetSocket(network, gaddr, nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", noCancel)
+func listenMulticastUDP(ctx context.Context, network string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) {
+ fd, err := internetSocket(ctx, network, gaddr, nil, syscall.SOCK_DGRAM, 0, "listen")
if err != nil {
return nil, err
}
diff --git a/src/net/udpsock_test.go b/src/net/udpsock_test.go
index 1404b7ce80..29d769c5a5 100644
--- a/src/net/udpsock_test.go
+++ b/src/net/udpsock_test.go
@@ -5,12 +5,50 @@
package net
import (
+ "internal/testenv"
"reflect"
"runtime"
"testing"
"time"
)
+func BenchmarkUDP6LinkLocalUnicast(b *testing.B) {
+ testHookUninstaller.Do(uninstallTestHooks)
+
+ if !supportsIPv6 {
+ b.Skip("IPv6 is not supported")
+ }
+ ifi := loopbackInterface()
+ if ifi == nil {
+ b.Skip("loopback interface not found")
+ }
+ lla := ipv6LinkLocalUnicastAddr(ifi)
+ if lla == "" {
+ b.Skip("IPv6 link-local unicast address not found")
+ }
+
+ c1, err := ListenPacket("udp6", JoinHostPort(lla+"%"+ifi.Name, "0"))
+ if err != nil {
+ b.Fatal(err)
+ }
+ defer c1.Close()
+ c2, err := ListenPacket("udp6", JoinHostPort(lla+"%"+ifi.Name, "0"))
+ if err != nil {
+ b.Fatal(err)
+ }
+ defer c2.Close()
+
+ var buf [1]byte
+ for i := 0; i < b.N; i++ {
+ if _, err := c1.WriteTo(buf[:], c2.LocalAddr()); err != nil {
+ b.Fatal(err)
+ }
+ if _, _, err := c2.ReadFrom(buf[:]); err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
type resolveUDPAddrTest struct {
network string
litAddrOrName string
@@ -178,9 +216,7 @@ var udpConnLocalNameTests = []struct {
}
func TestUDPConnLocalName(t *testing.T) {
- if testing.Short() || !*testExternal {
- t.Skip("avoid external network")
- }
+ testenv.MustHaveExternalNetwork(t)
for _, tt := range udpConnLocalNameTests {
c, err := ListenUDP(tt.net, tt.laddr)
@@ -234,9 +270,8 @@ func TestUDPConnLocalAndRemoteNames(t *testing.T) {
}
func TestIPv6LinkLocalUnicastUDP(t *testing.T) {
- if testing.Short() || !*testExternal {
- t.Skip("avoid external network")
- }
+ testenv.MustHaveExternalNetwork(t)
+
if !supportsIPv6 {
t.Skip("IPv6 is not supported")
}
diff --git a/src/net/unixsock.go b/src/net/unixsock.go
index d1eb0b62ee..bacdaa41d9 100644
--- a/src/net/unixsock.go
+++ b/src/net/unixsock.go
@@ -5,6 +5,7 @@
package net
import (
+ "context"
"os"
"syscall"
"time"
@@ -188,7 +189,7 @@ func DialUnix(net string, laddr, raddr *UnixAddr) (*UnixConn, error) {
default:
return nil, &OpError{Op: "dial", Net: net, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: UnknownNetworkError(net)}
}
- c, err := dialUnix(net, laddr, raddr, noDeadline)
+ c, err := dialUnix(context.Background(), net, laddr, raddr)
if err != nil {
return nil, &OpError{Op: "dial", Net: net, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: err}
}
@@ -290,7 +291,7 @@ func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) {
if laddr == nil {
return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: laddr.opAddr(), Err: errMissingAddress}
}
- ln, err := listenUnix(net, laddr)
+ ln, err := listenUnix(context.Background(), net, laddr)
if err != nil {
return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: laddr.opAddr(), Err: err}
}
@@ -310,7 +311,7 @@ func ListenUnixgram(net string, laddr *UnixAddr) (*UnixConn, error) {
if laddr == nil {
return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: nil, Err: errMissingAddress}
}
- c, err := listenUnixgram(net, laddr)
+ c, err := listenUnixgram(context.Background(), net, laddr)
if err != nil {
return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: laddr.opAddr(), Err: err}
}
diff --git a/src/net/unixsock_plan9.go b/src/net/unixsock_plan9.go
index 5d5b18f467..e70eb211bb 100644
--- a/src/net/unixsock_plan9.go
+++ b/src/net/unixsock_plan9.go
@@ -5,9 +5,9 @@
package net
import (
+ "context"
"os"
"syscall"
- "time"
)
func (c *UnixConn) readFrom(b []byte) (int, *UnixAddr, error) {
@@ -26,7 +26,7 @@ func (c *UnixConn) writeMsg(b, oob []byte, addr *UnixAddr) (n, oobn int, err err
return 0, 0, syscall.EPLAN9
}
-func dialUnix(network string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn, error) {
+func dialUnix(ctx context.Context, network string, laddr, raddr *UnixAddr) (*UnixConn, error) {
return nil, syscall.EPLAN9
}
@@ -42,10 +42,10 @@ func (ln *UnixListener) file() (*os.File, error) {
return nil, syscall.EPLAN9
}
-func listenUnix(network string, laddr *UnixAddr) (*UnixListener, error) {
+func listenUnix(ctx context.Context, network string, laddr *UnixAddr) (*UnixListener, error) {
return nil, syscall.EPLAN9
}
-func listenUnixgram(network string, laddr *UnixAddr) (*UnixConn, error) {
+func listenUnixgram(ctx context.Context, network string, laddr *UnixAddr) (*UnixConn, error) {
return nil, syscall.EPLAN9
}
diff --git a/src/net/unixsock_posix.go b/src/net/unixsock_posix.go
index 9275e1034c..5f0999c4c2 100644
--- a/src/net/unixsock_posix.go
+++ b/src/net/unixsock_posix.go
@@ -7,13 +7,13 @@
package net
import (
+ "context"
"errors"
"os"
"syscall"
- "time"
)
-func unixSocket(net string, laddr, raddr sockaddr, mode string, deadline time.Time) (*netFD, error) {
+func unixSocket(ctx context.Context, net string, laddr, raddr sockaddr, mode string) (*netFD, error) {
var sotype int
switch net {
case "unix":
@@ -42,7 +42,7 @@ func unixSocket(net string, laddr, raddr sockaddr, mode string, deadline time.Ti
return nil, errors.New("unknown mode: " + mode)
}
- fd, err := socket(net, syscall.AF_UNIX, sotype, 0, false, laddr, raddr, deadline, noCancel)
+ fd, err := socket(ctx, net, syscall.AF_UNIX, sotype, 0, false, laddr, raddr)
if err != nil {
return nil, err
}
@@ -146,8 +146,8 @@ func (c *UnixConn) writeMsg(b, oob []byte, addr *UnixAddr) (n, oobn int, err err
return c.fd.writeMsg(b, oob, sa)
}
-func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn, error) {
- fd, err := unixSocket(net, laddr, raddr, "dial", deadline)
+func dialUnix(ctx context.Context, net string, laddr, raddr *UnixAddr) (*UnixConn, error) {
+ fd, err := unixSocket(ctx, net, laddr, raddr, "dial")
if err != nil {
return nil, err
}
@@ -187,16 +187,16 @@ func (ln *UnixListener) file() (*os.File, error) {
return f, nil
}
-func listenUnix(network string, laddr *UnixAddr) (*UnixListener, error) {
- fd, err := unixSocket(network, laddr, nil, "listen", noDeadline)
+func listenUnix(ctx context.Context, network string, laddr *UnixAddr) (*UnixListener, error) {
+ fd, err := unixSocket(ctx, network, laddr, nil, "listen")
if err != nil {
return nil, err
}
return &UnixListener{fd: fd, path: fd.laddr.String(), unlink: true}, nil
}
-func listenUnixgram(network string, laddr *UnixAddr) (*UnixConn, error) {
- fd, err := unixSocket(network, laddr, nil, "listen", noDeadline)
+func listenUnixgram(ctx context.Context, network string, laddr *UnixAddr) (*UnixConn, error) {
+ fd, err := unixSocket(ctx, network, laddr, nil, "listen")
if err != nil {
return nil, err
}
diff --git a/src/net/unixsock_test.go b/src/net/unixsock_test.go
index d70c0d1953..f0f88ed37b 100644
--- a/src/net/unixsock_test.go
+++ b/src/net/unixsock_test.go
@@ -8,6 +8,7 @@ package net
import (
"bytes"
+ "internal/testenv"
"os"
"reflect"
"runtime"
@@ -20,6 +21,9 @@ func TestReadUnixgramWithUnnamedSocket(t *testing.T) {
if !testableNetwork("unixgram") {
t.Skip("unixgram test")
}
+ if runtime.GOOS == "openbsd" {
+ testenv.SkipFlaky(t, 15157)
+ }
addr := testUnixAddr()
la, err := ResolveUnixAddr("unixgram", addr)
diff --git a/src/net/url/url.go b/src/net/url/url.go
index d9c8c49e94..05b41fa964 100644
--- a/src/net/url/url.go
+++ b/src/net/url/url.go
@@ -573,8 +573,12 @@ func parseHost(host string) (string, error) {
}
return host1 + host2 + host3, nil
}
+ } else if i := strings.LastIndex(host, ":"); i > 0 {
+ colonPort := host[i:]
+ if !validOptionalPort(colonPort) {
+ return "", fmt.Errorf("invalid port %q after host", colonPort)
+ }
}
-
var err error
if host, err = unescape(host, encodeHost); err != nil {
return "", err
diff --git a/src/net/url/url_test.go b/src/net/url/url_test.go
index 7560f22c4a..da6bc2843e 100644
--- a/src/net/url/url_test.go
+++ b/src/net/url/url_test.go
@@ -418,10 +418,10 @@ var urltests = []URLTest{
},
// worst case host, still round trips
{
- "scheme://!$&'()*+,;=hello!:port/path",
+ "scheme://!$&'()*+,;=hello!:8080/path",
&URL{
Scheme: "scheme",
- Host: "!$&'()*+,;=hello!:port",
+ Host: "!$&'()*+,;=hello!:8080",
Path: "/path",
},
"",
@@ -636,8 +636,10 @@ var parseRequestURLTests = []struct {
{"*", true},
{"http://192.168.0.1/", true},
{"http://192.168.0.1:8080/", true},
+ {"http://192.168.0.1:foo/", false},
{"http://[fe80::1]/", true},
{"http://[fe80::1]:8080/", true},
+ {"http://[fe80::1]:foo/", false},
// Tests exercising RFC 6874 compliance:
{"http://[fe80::1%25en0]/", true}, // with alphanum zone identifier
diff --git a/src/os/exec/exec_test.go b/src/os/exec/exec_test.go
index 1f2fd12add..ed2721bb5e 100644
--- a/src/os/exec/exec_test.go
+++ b/src/os/exec/exec_test.go
@@ -479,7 +479,7 @@ func TestExtraFiles(t *testing.T) {
if err != nil {
t.Fatalf("Write: %v", err)
}
- _, err = tf.Seek(0, os.SEEK_SET)
+ _, err = tf.Seek(0, io.SeekStart)
if err != nil {
t.Fatalf("Seek: %v", err)
}
diff --git a/src/os/exec_windows.go b/src/os/exec_windows.go
index 3264271b2e..72b5a93199 100644
--- a/src/os/exec_windows.go
+++ b/src/os/exec_windows.go
@@ -104,7 +104,7 @@ func init() {
defer syscall.LocalFree(syscall.Handle(uintptr(unsafe.Pointer(argv))))
Args = make([]string, argc)
for i, v := range (*argv)[:argc] {
- Args[i] = string(syscall.UTF16ToString((*v)[:]))
+ Args[i] = syscall.UTF16ToString((*v)[:])
}
}
diff --git a/src/os/file_windows.go b/src/os/file_windows.go
index 7d04477d42..137f24a0a9 100644
--- a/src/os/file_windows.go
+++ b/src/os/file_windows.go
@@ -181,9 +181,9 @@ func (file *file) close() error {
}
var e error
if file.isdir() {
- e = syscall.FindClose(syscall.Handle(file.fd))
+ e = syscall.FindClose(file.fd)
} else {
- e = syscall.CloseHandle(syscall.Handle(file.fd))
+ e = syscall.CloseHandle(file.fd)
}
var err error
if e != nil {
@@ -216,7 +216,7 @@ func (file *File) readdir(n int) (fi []FileInfo, err error) {
d := &file.dirinfo.data
for n != 0 && !file.dirinfo.isempty {
if file.dirinfo.needdata {
- e := syscall.FindNextFile(syscall.Handle(file.fd), d)
+ e := syscall.FindNextFile(file.fd, d)
if e != nil {
if e == syscall.ERROR_NO_MORE_FILES {
break
@@ -230,7 +230,7 @@ func (file *File) readdir(n int) (fi []FileInfo, err error) {
}
}
file.dirinfo.needdata = true
- name := string(syscall.UTF16ToString(d.FileName[0:]))
+ name := syscall.UTF16ToString(d.FileName[0:])
if name == "." || name == ".." { // Useless names
continue
}
@@ -288,7 +288,7 @@ func (f *File) readConsole(b []byte) (n int, err error) {
}
wchars := make([]uint16, nwc)
pwc := &wchars[0]
- nwc, err = windows.MultiByteToWideChar(acp, 2, pmb, int32(nmb), pwc, int32(nwc))
+ nwc, err = windows.MultiByteToWideChar(acp, 2, pmb, int32(nmb), pwc, nwc)
if err != nil {
return 0, err
}
@@ -335,7 +335,7 @@ func (f *File) pread(b []byte, off int64) (n int, err error) {
Offset: uint32(off),
}
var done uint32
- e = syscall.ReadFile(syscall.Handle(f.fd), b, &done, &o)
+ e = syscall.ReadFile(f.fd, b, &done, &o)
if e != nil {
if e == syscall.ERROR_HANDLE_EOF {
// end of file
@@ -415,7 +415,7 @@ func (f *File) pwrite(b []byte, off int64) (n int, err error) {
Offset: uint32(off),
}
var done uint32
- e = syscall.WriteFile(syscall.Handle(f.fd), b, &done, &o)
+ e = syscall.WriteFile(f.fd, b, &done, &o)
if e != nil {
return 0, e
}
diff --git a/src/os/stat_darwin.go b/src/os/stat_darwin.go
index 9dc7a99fb7..74214cefa4 100644
--- a/src/os/stat_darwin.go
+++ b/src/os/stat_darwin.go
@@ -11,7 +11,7 @@ import (
func fillFileStatFromSys(fs *fileStat, name string) {
fs.name = basename(name)
- fs.size = int64(fs.sys.Size)
+ fs.size = fs.sys.Size
fs.modTime = timespecToTime(fs.sys.Mtimespec)
fs.mode = FileMode(fs.sys.Mode & 0777)
switch fs.sys.Mode & syscall.S_IFMT {
diff --git a/src/os/stat_dragonfly.go b/src/os/stat_dragonfly.go
index 69e63230eb..217bc6726d 100644
--- a/src/os/stat_dragonfly.go
+++ b/src/os/stat_dragonfly.go
@@ -11,7 +11,7 @@ import (
func fillFileStatFromSys(fs *fileStat, name string) {
fs.name = basename(name)
- fs.size = int64(fs.sys.Size)
+ fs.size = fs.sys.Size
fs.modTime = timespecToTime(fs.sys.Mtim)
fs.mode = FileMode(fs.sys.Mode & 0777)
switch fs.sys.Mode & syscall.S_IFMT {
@@ -42,7 +42,7 @@ func fillFileStatFromSys(fs *fileStat, name string) {
}
func timespecToTime(ts syscall.Timespec) time.Time {
- return time.Unix(int64(ts.Sec), int64(ts.Nsec))
+ return time.Unix(ts.Sec, ts.Nsec)
}
// For testing.
diff --git a/src/os/stat_freebsd.go b/src/os/stat_freebsd.go
index e9d38aa722..bab4ffa798 100644
--- a/src/os/stat_freebsd.go
+++ b/src/os/stat_freebsd.go
@@ -11,7 +11,7 @@ import (
func fillFileStatFromSys(fs *fileStat, name string) {
fs.name = basename(name)
- fs.size = int64(fs.sys.Size)
+ fs.size = fs.sys.Size
fs.modTime = timespecToTime(fs.sys.Mtimespec)
fs.mode = FileMode(fs.sys.Mode & 0777)
switch fs.sys.Mode & syscall.S_IFMT {
diff --git a/src/os/stat_linux.go b/src/os/stat_linux.go
index 69e63230eb..d36afa9ffd 100644
--- a/src/os/stat_linux.go
+++ b/src/os/stat_linux.go
@@ -11,7 +11,7 @@ import (
func fillFileStatFromSys(fs *fileStat, name string) {
fs.name = basename(name)
- fs.size = int64(fs.sys.Size)
+ fs.size = fs.sys.Size
fs.modTime = timespecToTime(fs.sys.Mtim)
fs.mode = FileMode(fs.sys.Mode & 0777)
switch fs.sys.Mode & syscall.S_IFMT {
diff --git a/src/os/stat_nacl.go b/src/os/stat_nacl.go
index d3bed14e43..0c53f2faa4 100644
--- a/src/os/stat_nacl.go
+++ b/src/os/stat_nacl.go
@@ -11,7 +11,7 @@ import (
func fillFileStatFromSys(fs *fileStat, name string) {
fs.name = basename(name)
- fs.size = int64(fs.sys.Size)
+ fs.size = fs.sys.Size
fs.modTime = timespecToTime(fs.sys.Mtime, fs.sys.MtimeNsec)
fs.mode = FileMode(fs.sys.Mode & 0777)
switch fs.sys.Mode & syscall.S_IFMT {
diff --git a/src/os/stat_netbsd.go b/src/os/stat_netbsd.go
index e9d38aa722..11ebcacab8 100644
--- a/src/os/stat_netbsd.go
+++ b/src/os/stat_netbsd.go
@@ -11,7 +11,7 @@ import (
func fillFileStatFromSys(fs *fileStat, name string) {
fs.name = basename(name)
- fs.size = int64(fs.sys.Size)
+ fs.size = fs.sys.Size
fs.modTime = timespecToTime(fs.sys.Mtimespec)
fs.mode = FileMode(fs.sys.Mode & 0777)
switch fs.sys.Mode & syscall.S_IFMT {
@@ -42,7 +42,7 @@ func fillFileStatFromSys(fs *fileStat, name string) {
}
func timespecToTime(ts syscall.Timespec) time.Time {
- return time.Unix(int64(ts.Sec), int64(ts.Nsec))
+ return time.Unix(ts.Sec, int64(ts.Nsec))
}
// For testing.
diff --git a/src/os/stat_openbsd.go b/src/os/stat_openbsd.go
index 69e63230eb..9df2d7f773 100644
--- a/src/os/stat_openbsd.go
+++ b/src/os/stat_openbsd.go
@@ -11,7 +11,7 @@ import (
func fillFileStatFromSys(fs *fileStat, name string) {
fs.name = basename(name)
- fs.size = int64(fs.sys.Size)
+ fs.size = fs.sys.Size
fs.modTime = timespecToTime(fs.sys.Mtim)
fs.mode = FileMode(fs.sys.Mode & 0777)
switch fs.sys.Mode & syscall.S_IFMT {
@@ -42,7 +42,7 @@ func fillFileStatFromSys(fs *fileStat, name string) {
}
func timespecToTime(ts syscall.Timespec) time.Time {
- return time.Unix(int64(ts.Sec), int64(ts.Nsec))
+ return time.Unix(ts.Sec, int64(ts.Nsec))
}
// For testing.
diff --git a/src/os/stat_plan9.go b/src/os/stat_plan9.go
index a2df5fe139..96f056c111 100644
--- a/src/os/stat_plan9.go
+++ b/src/os/stat_plan9.go
@@ -20,7 +20,7 @@ func sameFile(fs1, fs2 *fileStat) bool {
func fileInfoFromStat(d *syscall.Dir) FileInfo {
fs := &fileStat{
name: d.Name,
- size: int64(d.Length),
+ size: d.Length,
modTime: time.Unix(int64(d.Mtime), 0),
sys: d,
}
diff --git a/src/os/stat_solaris.go b/src/os/stat_solaris.go
index 69e63230eb..217bc6726d 100644
--- a/src/os/stat_solaris.go
+++ b/src/os/stat_solaris.go
@@ -11,7 +11,7 @@ import (
func fillFileStatFromSys(fs *fileStat, name string) {
fs.name = basename(name)
- fs.size = int64(fs.sys.Size)
+ fs.size = fs.sys.Size
fs.modTime = timespecToTime(fs.sys.Mtim)
fs.mode = FileMode(fs.sys.Mode & 0777)
switch fs.sys.Mode & syscall.S_IFMT {
@@ -42,7 +42,7 @@ func fillFileStatFromSys(fs *fileStat, name string) {
}
func timespecToTime(ts syscall.Timespec) time.Time {
- return time.Unix(int64(ts.Sec), int64(ts.Nsec))
+ return time.Unix(ts.Sec, ts.Nsec)
}
// For testing.
diff --git a/src/os/stat_windows.go b/src/os/stat_windows.go
index b8f97ad60a..e55eeb0fdd 100644
--- a/src/os/stat_windows.go
+++ b/src/os/stat_windows.go
@@ -35,7 +35,7 @@ func (file *File) Stat() (FileInfo, error) {
}
var d syscall.ByHandleFileInformation
- err = syscall.GetFileInformationByHandle(syscall.Handle(file.fd), &d)
+ err = syscall.GetFileInformationByHandle(file.fd, &d)
if err != nil {
return nil, &PathError{"GetFileInformationByHandle", file.name, err}
}
diff --git a/src/os/types_windows.go b/src/os/types_windows.go
index 900d444b0e..ad4e863fcb 100644
--- a/src/os/types_windows.go
+++ b/src/os/types_windows.go
@@ -73,7 +73,7 @@ func (fs *fileStat) loadFileId() error {
}
defer syscall.CloseHandle(h)
var i syscall.ByHandleFileInformation
- err = syscall.GetFileInformationByHandle(syscall.Handle(h), &i)
+ err = syscall.GetFileInformationByHandle(h, &i)
if err != nil {
return err
}
diff --git a/src/os/user/lookup_unix.go b/src/os/user/lookup_unix.go
index 97b649cdad..58ecf32405 100644
--- a/src/os/user/lookup_unix.go
+++ b/src/os/user/lookup_unix.go
@@ -37,6 +37,11 @@ static int mygetgrgid_r(int gid, struct group *grp,
char *buf, size_t buflen, struct group **result) {
return getgrgid_r(gid, grp, buf, buflen, result);
}
+
+static int mygetgrnam_r(const char *name, struct group *grp,
+ char *buf, size_t buflen, struct group **result) {
+ return getgrnam_r(name, grp, buf, buflen, result);
+}
*/
import "C"
@@ -139,7 +144,7 @@ func lookupGroup(groupname string) (*Group, error) {
defer C.free(unsafe.Pointer(cname))
err := retryWithBuffer(buf, func() syscall.Errno {
- return syscall.Errno(C.getgrnam_r(cname,
+ return syscall.Errno(C.mygetgrnam_r(cname,
&grp,
(*C.char)(buf.ptr),
C.size_t(buf.size),
diff --git a/src/path/filepath/path_test.go b/src/path/filepath/path_test.go
index 3622f9178e..1a4a9d2a1a 100644
--- a/src/path/filepath/path_test.go
+++ b/src/path/filepath/path_test.go
@@ -1015,7 +1015,7 @@ func TestAbs(t *testing.T) {
vol := filepath.VolumeName(root)
var extra []string
for _, path := range absTests {
- if strings.Index(path, "$") != -1 {
+ if strings.Contains(path, "$") {
continue
}
path = vol + path
diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go
index 4a76ef8608..f8ffaae8e1 100644
--- a/src/reflect/all_test.go
+++ b/src/reflect/all_test.go
@@ -1476,6 +1476,12 @@ func TestFunc(t *testing.T) {
if i != 10 || j != 20 || k != 30 || l != (two{40, 50}) || m != 60 || n != 70 || o != 80 {
t.Errorf("Call returned %d, %d, %d, %v, %d, %g, %d; want 10, 20, 30, [40, 50], 60, 70, 80", i, j, k, l, m, n, o)
}
+
+ for i, v := range ret {
+ if v.CanAddr() {
+ t.Errorf("result %d is addressable", i)
+ }
+ }
}
type emptyStruct struct{}
@@ -3896,6 +3902,9 @@ func TestSliceOf(t *testing.T) {
// check construction and use of type not in binary
type T int
st := SliceOf(TypeOf(T(1)))
+ if got, want := st.String(), "[]reflect_test.T"; got != want {
+ t.Errorf("SliceOf(T(1)).String()=%q, want %q", got, want)
+ }
v := MakeSlice(st, 10, 10)
runtime.GC()
for i := 0; i < v.Len(); i++ {
@@ -4169,12 +4178,12 @@ func TestStructOfExportRules(t *testing.T) {
},
{
field: StructField{Name: "", Type: TypeOf(ΦType{})},
- mustPanic: true, // TODO(sbinet): creating a struct with UTF-8 fields not supported
+ mustPanic: false,
exported: true,
},
{
field: StructField{Name: "", Type: TypeOf(φType{})},
- mustPanic: true, // TODO(sbinet): creating a struct with UTF-8 fields not supported
+ mustPanic: false,
exported: false,
},
{
@@ -5645,25 +5654,69 @@ func TestChanAlloc(t *testing.T) {
// allocs < 0.5 condition will trigger and this test should be fixed.
}
+type TheNameOfThisTypeIsExactly255BytesLongSoWhenTheCompilerPrependsTheReflectTestPackageNameAndExtraStarTheLinkerRuntimeAndReflectPackagesWillHaveToCorrectlyDecodeTheSecondLengthByte0123456789_0123456789_0123456789_0123456789_0123456789_012345678 int
+
type nameTest struct {
v interface{}
want string
}
var nameTests = []nameTest{
- {int32(0), "int32"},
- {D1{}, "D1"},
- {[]D1{}, ""},
- {(chan D1)(nil), ""},
- {(func() D1)(nil), ""},
- {(<-chan D1)(nil), ""},
- {(chan<- D1)(nil), ""},
+ {(*int32)(nil), "int32"},
+ {(*D1)(nil), "D1"},
+ {(*[]D1)(nil), ""},
+ {(*chan D1)(nil), ""},
+ {(*func() D1)(nil), ""},
+ {(*<-chan D1)(nil), ""},
+ {(*chan<- D1)(nil), ""},
+ {(*interface{})(nil), ""},
+ {(*interface {
+ F()
+ })(nil), ""},
+ {(*TheNameOfThisTypeIsExactly255BytesLongSoWhenTheCompilerPrependsTheReflectTestPackageNameAndExtraStarTheLinkerRuntimeAndReflectPackagesWillHaveToCorrectlyDecodeTheSecondLengthByte0123456789_0123456789_0123456789_0123456789_0123456789_012345678)(nil), "TheNameOfThisTypeIsExactly255BytesLongSoWhenTheCompilerPrependsTheReflectTestPackageNameAndExtraStarTheLinkerRuntimeAndReflectPackagesWillHaveToCorrectlyDecodeTheSecondLengthByte0123456789_0123456789_0123456789_0123456789_0123456789_012345678"},
}
func TestNames(t *testing.T) {
for _, test := range nameTests {
- if got := TypeOf(test.v).Name(); got != test.want {
- t.Errorf("%T Name()=%q, want %q", test.v, got, test.want)
+ typ := TypeOf(test.v).Elem()
+ if got := typ.Name(); got != test.want {
+ t.Errorf("%v Name()=%q, want %q", typ, got, test.want)
+ }
+ }
+}
+
+func TestExported(t *testing.T) {
+ type ΦExported struct{}
+ type φUnexported struct{}
+ type BigP *big
+ type P int
+ type p *P
+ type P2 p
+ type p3 p
+
+ type exportTest struct {
+ v interface{}
+ want bool
+ }
+ exportTests := []exportTest{
+ {D1{}, true},
+ {(*D1)(nil), true},
+ {big{}, false},
+ {(*big)(nil), false},
+ {(BigP)(nil), true},
+ {(*BigP)(nil), true},
+ {ΦExported{}, true},
+ {φUnexported{}, false},
+ {P(0), true},
+ {(p)(nil), false},
+ {(P2)(nil), true},
+ {(p3)(nil), false},
+ }
+
+ for i, test := range exportTests {
+ typ := TypeOf(test.v)
+ if got := IsExported(typ); got != test.want {
+ t.Errorf("%d: %s exported=%v, want %v", i, typ.Name(), got, test.want)
}
}
}
@@ -5680,3 +5733,14 @@ func TestNameBytesAreAligned(t *testing.T) {
t.Errorf("reflect.name.bytes pointer is not aligned: %x", v)
}
}
+
+func TestMethodPkgPathReadable(t *testing.T) {
+ // Reading the Method type for an unexported method triggers an
+ // offset resolution via p.name.pkgPath(). Make sure it uses a
+ // valid base pointer for the offset.
+ v := ValueOf(embed{})
+ m := v.Type().Method(0)
+ if m.PkgPath != "reflect" {
+ t.Errorf(`PkgPath=%q, want "reflect"`, m.PkgPath)
+ }
+}
diff --git a/src/reflect/asm_s390x.s b/src/reflect/asm_s390x.s
new file mode 100644
index 0000000000..e6b86cfaa9
--- /dev/null
+++ b/src/reflect/asm_s390x.s
@@ -0,0 +1,30 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+#include "funcdata.h"
+
+// makeFuncStub is the code half of the function returned by MakeFunc.
+// See the comment on the declaration of makeFuncStub in makefunc.go
+// for more details.
+// No arg size here, runtime pulls arg map out of the func value.
+TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$16
+ NO_LOCAL_POINTERS
+ MOVD R12, 8(R15)
+ MOVD $argframe+0(FP), R3
+ MOVD R3, 16(R15)
+ BL ·callReflect(SB)
+ RET
+
+// methodValueCall is the code half of the function returned by makeMethodValue.
+// See the comment on the declaration of methodValueCall in makefunc.go
+// for more details.
+// No arg size here; runtime pulls arg map out of the func value.
+TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$16
+ NO_LOCAL_POINTERS
+ MOVD R12, 8(R15)
+ MOVD $argframe+0(FP), R3
+ MOVD R3, 16(R15)
+ BL ·callMethod(SB)
+ RET
diff --git a/src/reflect/export_test.go b/src/reflect/export_test.go
index ddc64b46be..00189f3353 100644
--- a/src/reflect/export_test.go
+++ b/src/reflect/export_test.go
@@ -46,9 +46,12 @@ func FuncLayout(t Type, rcvr Type) (frametype Type, argSize, retOffset uintptr,
func TypeLinks() []string {
var r []string
- for _, m := range typelinks() {
- for _, t := range m {
- r = append(r, t.string)
+ sections, offset := typelinks()
+ for i, offs := range offset {
+ rodata := sections[i]
+ for _, off := range offs {
+ typ := (*rtype)(resolveTypeOff(unsafe.Pointer(rodata), off))
+ r = append(r, typ.String())
}
}
return r
@@ -88,14 +91,21 @@ func FirstMethodNameBytes(t Type) *byte {
if ut == nil {
panic("type has no methods")
}
- m := ut.methods[0]
- if *m.name.data(0)&(1<<2) == 0 {
+ m := ut.methods()[0]
+ mname := t.(*rtype).nameOff(m.name)
+ if *mname.data(0)&(1<<2) == 0 {
panic("method name does not have pkgPath *string")
}
- return m.name.bytes
+ return mname.bytes
}
type OtherPkgFields struct {
OtherExported int
otherUnexported int
}
+
+func IsExported(t Type) bool {
+ typ := t.(*rtype)
+ n := typ.nameOff(typ.str)
+ return n.isExported()
+}
diff --git a/src/reflect/type.go b/src/reflect/type.go
index 8f13acf26e..2ceb3d3f66 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -242,6 +242,11 @@ const (
// tflag is used by an rtype to signal what extra type information is
// available in the memory directly following the rtype value.
+//
+// tflag values must be kept in sync with copies in:
+// cmd/compile/internal/gc/reflect.go
+// cmd/link/internal/ld/decodesym.go
+// runtime/type.go
type tflag uint8
const (
@@ -256,7 +261,13 @@ const (
// u uncommonType
// }
// u := &(*tUncommon)(unsafe.Pointer(t)).u
- tflagUncommon tflag = 1
+ tflagUncommon tflag = 1 << 0
+
+ // tflagExtraStar means the name in the str field has an
+ // extraneous '*' prefix. This is because for most types T in
+ // a program, the type *T also exists and reusing the str data
+ // saves binary size.
+ tflagExtraStar tflag = 1 << 1
)
// rtype is the common implementation of most values.
@@ -273,7 +284,8 @@ type rtype struct {
kind uint8 // enumeration for C
alg *typeAlg // algorithm table
gcdata *byte // garbage collection data
- string string // string form; unnecessary but undeniably useful
+ str nameOff // string form
+ _ int32 // unused; keeps rtype always a multiple of ptrSize
}
// a copy of runtime.typeAlg
@@ -288,10 +300,10 @@ type typeAlg struct {
// Method on non-interface type
type method struct {
- name name // name of method
- mtyp *rtype // method type (without receiver)
- ifn unsafe.Pointer // fn used in interface call (one-word receiver)
- tfn unsafe.Pointer // fn used for normal method call
+ name nameOff // name of method
+ mtyp typeOff // method type (without receiver)
+ ifn textOff // fn used in interface call (one-word receiver)
+ tfn textOff // fn used for normal method call
}
// uncommonType is present only for types with names or methods
@@ -299,8 +311,9 @@ type method struct {
// Using a pointer to this struct reduces the overall size required
// to describe an unnamed type with no methods.
type uncommonType struct {
- pkgPath *string // import path; nil for built-in types like int, string
- methods []method // methods associated with type
+ pkgPath nameOff // import path; empty for built-in types like int, string
+ mcount uint16 // number of methods
+ moff uint16 // offset from this uncommontype to [mcount]method
}
// ChanDir represents a channel type's direction.
@@ -346,14 +359,14 @@ type funcType struct {
// imethod represents a method on an interface type
type imethod struct {
- name name // name of method
- typ *rtype // .(*FuncType) underneath
+ name nameOff // name of method
+ typ typeOff // .(*FuncType) underneath
}
// interfaceType represents an interface type.
type interfaceType struct {
rtype `reflect:"interface"`
- pkgPath *string // import path
+ pkgPath name // import path
methods []imethod // sorted by hash
}
@@ -395,7 +408,7 @@ type structField struct {
// structType represents a struct type.
type structType struct {
rtype `reflect:"struct"`
- pkgPath *string
+ pkgPath name
fields []structField // sorted by offset
}
@@ -405,7 +418,7 @@ type structType struct {
//
// 1<<0 the name is exported
// 1<<1 tag data follows the name
-// 1<<2 pkgPath *string follow the name and tag
+// 1<<2 pkgPath nameOff follows the name and tag
//
// The next two bytes are the data length:
//
@@ -416,27 +429,29 @@ type structType struct {
// If tag data follows then bytes 3+l and 3+l+1 are the tag length,
// with the data following.
//
-// If the import path follows, then ptrSize bytes at the end of
-// the data form a *string. The pointer is aligned to its width.
-// The import path is only set for concrete methods that are defined
-// in a different package than their type.
+// If the import path follows, then 4 bytes at the end of
+// the data form a nameOff. The import path is only set for concrete
+// methods that are defined in a different package than their type.
+//
+// If a name starts with "*", then the exported bit represents
+// whether the pointed to type is exported.
type name struct {
bytes *byte
}
-func (n *name) data(off int) *byte {
+func (n name) data(off int) *byte {
return (*byte)(add(unsafe.Pointer(n.bytes), uintptr(off)))
}
-func (n *name) isExported() bool {
+func (n name) isExported() bool {
return (*n.bytes)&(1<<0) != 0
}
-func (n *name) nameLen() int {
+func (n name) nameLen() int {
return int(uint16(*n.data(1))<<8 | uint16(*n.data(2)))
}
-func (n *name) tagLen() int {
+func (n name) tagLen() int {
if *n.data(0)&(1<<1) == 0 {
return 0
}
@@ -444,7 +459,10 @@ func (n *name) tagLen() int {
return int(uint16(*n.data(off))<<8 | uint16(*n.data(off + 1)))
}
-func (n *name) name() (s string) {
+func (n name) name() (s string) {
+ if n.bytes == nil {
+ return ""
+ }
nl := n.nameLen()
if nl == 0 {
return ""
@@ -455,7 +473,7 @@ func (n *name) name() (s string) {
return s
}
-func (n *name) tag() (s string) {
+func (n name) tag() (s string) {
tl := n.tagLen()
if tl == 0 {
return ""
@@ -467,16 +485,18 @@ func (n *name) tag() (s string) {
return s
}
-func (n *name) pkgPath() *string {
- if *n.data(0)&(1<<2) == 0 {
- return nil
+func (n name) pkgPath() string {
+ if n.bytes == nil || *n.data(0)&(1<<2) == 0 {
+ return ""
}
off := 3 + n.nameLen()
if tl := n.tagLen(); tl > 0 {
off += 2 + tl
}
- off = int(round(uintptr(off), ptrSize))
- return *(**string)(unsafe.Pointer(n.data(off)))
+ var nameOff int32
+ copy((*[4]byte)(unsafe.Pointer(&nameOff))[:], (*[4]byte)(unsafe.Pointer(n.data(off)))[:])
+ pkgPathName := name{(*byte)(resolveTypeOff(unsafe.Pointer(n.bytes), nameOff))}
+ return pkgPathName.name()
}
// round n up to a multiple of a. a must be a power of 2.
@@ -589,11 +609,69 @@ var kindNames = []string{
UnsafePointer: "unsafe.Pointer",
}
-func (t *uncommonType) PkgPath() string {
- if t == nil || t.pkgPath == nil {
- return ""
+func (t *uncommonType) methods() []method {
+ return (*[1 << 16]method)(add(unsafe.Pointer(t), uintptr(t.moff)))[:t.mcount:t.mcount]
+}
+
+// resolveNameOff resolves a name offset from a base pointer.
+// The (*rtype).nameOff method is a convenience wrapper for this function.
+// Implemented in the runtime package.
+func resolveNameOff(ptrInModule unsafe.Pointer, off int32) unsafe.Pointer
+
+// resolveTypeOff resolves an *rtype offset from a base type.
+// The (*rtype).typeOff method is a convenience wrapper for this function.
+// Implemented in the runtime package.
+func resolveTypeOff(rtype unsafe.Pointer, off int32) unsafe.Pointer
+
+// resolveTextOff resolves an function pointer offset from a base type.
+// The (*rtype).textOff method is a convenience wrapper for this function.
+// Implemented in the runtime package.
+func resolveTextOff(rtype unsafe.Pointer, off int32) unsafe.Pointer
+
+// addReflectOff adds a pointer to the reflection lookup map in the runtime.
+// It returns a new ID that can be used as a typeOff or textOff, and will
+// be resolved correctly. Implemented in the runtime package.
+func addReflectOff(ptr unsafe.Pointer) int32
+
+// resolveReflectType adds a name to the reflection lookup map in the runtime.
+// It returns a new nameOff that can be used to refer to the pointer.
+func resolveReflectName(n name) nameOff {
+ return nameOff(addReflectOff(unsafe.Pointer(n.bytes)))
+}
+
+// resolveReflectType adds a *rtype to the reflection lookup map in the runtime.
+// It returns a new typeOff that can be used to refer to the pointer.
+func resolveReflectType(t *rtype) typeOff {
+ return typeOff(addReflectOff(unsafe.Pointer(t)))
+}
+
+// resolveReflectText adds a function pointer to the reflection lookup map in
+// the runtime. It returns a new textOff that can be used to refer to the
+// pointer.
+func resolveReflectText(ptr unsafe.Pointer) textOff {
+ return textOff(addReflectOff(ptr))
+}
+
+type nameOff int32 // offset to a name
+type typeOff int32 // offset to an *rtype
+type textOff int32 // offset from top of text section
+
+func (t *rtype) nameOff(off nameOff) name {
+ if off == 0 {
+ return name{}
}
- return *t.pkgPath
+ return name{(*byte)(resolveNameOff(unsafe.Pointer(t), int32(off)))}
+}
+
+func (t *rtype) typeOff(off typeOff) *rtype {
+ if off == 0 {
+ return nil
+ }
+ return (*rtype)(resolveTypeOff(unsafe.Pointer(t), int32(off)))
+}
+
+func (t *rtype) textOff(off textOff) unsafe.Pointer {
+ return resolveTextOff(unsafe.Pointer(t), int32(off))
}
func (t *rtype) uncommon() *uncommonType {
@@ -602,7 +680,7 @@ func (t *rtype) uncommon() *uncommonType {
}
switch t.Kind() {
case Struct:
- return &(*structTypeWithMethods)(unsafe.Pointer(t)).u
+ return &(*structTypeUncommon)(unsafe.Pointer(t)).u
case Ptr:
type u struct {
ptrType
@@ -654,7 +732,13 @@ func (t *rtype) uncommon() *uncommonType {
}
}
-func (t *rtype) String() string { return t.string }
+func (t *rtype) String() string {
+ s := t.nameOff(t.str).name()
+ if t.tflag&tflagExtraStar != 0 {
+ return s[1:]
+ }
+ return s
+}
func (t *rtype) Size() uintptr { return t.size }
@@ -688,7 +772,7 @@ func (t *rtype) NumMethod() int {
if ut == nil {
return 0
}
- return len(ut.methods)
+ return int(ut.mcount)
}
func (t *rtype) Method(i int) (m Method) {
@@ -698,22 +782,23 @@ func (t *rtype) Method(i int) (m Method) {
}
ut := t.uncommon()
- if ut == nil || i < 0 || i >= len(ut.methods) {
+ if ut == nil || i < 0 || i >= int(ut.mcount) {
panic("reflect: Method index out of range")
}
- p := &ut.methods[i]
- m.Name = p.name.name()
+ p := ut.methods()[i]
+ pname := t.nameOff(p.name)
+ m.Name = pname.name()
fl := flag(Func)
- if !p.name.isExported() {
- pkgPath := p.name.pkgPath()
- if pkgPath == nil {
- pkgPath = ut.pkgPath
+ if !pname.isExported() {
+ m.PkgPath = pname.pkgPath()
+ if m.PkgPath == "" {
+ m.PkgPath = t.nameOff(ut.pkgPath).name()
}
- m.PkgPath = *pkgPath
fl |= flagStickyRO
}
- if p.mtyp != nil {
- ft := (*funcType)(unsafe.Pointer(p.mtyp))
+ if p.mtyp != 0 {
+ mtyp := t.typeOff(p.mtyp)
+ ft := (*funcType)(unsafe.Pointer(mtyp))
in := make([]Type, 0, 1+len(ft.in()))
in = append(in, t)
for _, arg := range ft.in() {
@@ -723,9 +808,10 @@ func (t *rtype) Method(i int) (m Method) {
for _, ret := range ft.out() {
out = append(out, ret)
}
- mt := FuncOf(in, out, p.mtyp.IsVariadic())
+ mt := FuncOf(in, out, ft.IsVariadic())
m.Type = mt
- fn := unsafe.Pointer(&p.tfn)
+ tfn := t.textOff(p.tfn)
+ fn := unsafe.Pointer(&tfn)
m.Func = Value{mt.(*rtype), fn, fl}
}
m.Index = i
@@ -741,9 +827,11 @@ func (t *rtype) MethodByName(name string) (m Method, ok bool) {
if ut == nil {
return Method{}, false
}
- for i := range ut.methods {
- p := &ut.methods[i]
- if p.name.name() == name {
+ utmethods := ut.methods()
+ for i := 0; i < int(ut.mcount); i++ {
+ p := utmethods[i]
+ pname := t.nameOff(p.name)
+ if pname.name() == name {
return t.Method(i), true
}
}
@@ -751,7 +839,11 @@ func (t *rtype) MethodByName(name string) (m Method, ok bool) {
}
func (t *rtype) PkgPath() string {
- return t.uncommon().PkgPath()
+ ut := t.uncommon()
+ if ut == nil {
+ return ""
+ }
+ return t.nameOff(ut.pkgPath).name()
}
func hasPrefix(s, prefix string) bool {
@@ -759,33 +851,37 @@ func hasPrefix(s, prefix string) bool {
}
func (t *rtype) Name() string {
- if hasPrefix(t.string, "map[") {
+ s := t.String()
+ if hasPrefix(s, "map[") {
+ return ""
+ }
+ if hasPrefix(s, "struct {") {
return ""
}
- if hasPrefix(t.string, "struct {") {
+ if hasPrefix(s, "chan ") {
return ""
}
- if hasPrefix(t.string, "chan ") {
+ if hasPrefix(s, "chan<-") {
return ""
}
- if hasPrefix(t.string, "chan<-") {
+ if hasPrefix(s, "func(") {
return ""
}
- if hasPrefix(t.string, "func(") {
+ if hasPrefix(s, "interface {") {
return ""
}
- switch t.string[0] {
+ switch s[0] {
case '[', '*', '<':
return ""
}
- i := len(t.string) - 1
+ i := len(s) - 1
for i >= 0 {
- if t.string[i] == '.' {
+ if s[i] == '.' {
break
}
i--
}
- return t.string[i+1:]
+ return s[i+1:]
}
func (t *rtype) ChanDir() ChanDir {
@@ -914,7 +1010,7 @@ func (t *rtype) Out(i int) Type {
}
func (t *funcType) in() []*rtype {
- uadd := uintptr(unsafe.Sizeof(*t))
+ uadd := unsafe.Sizeof(*t)
if t.tflag&tflagUncommon != 0 {
uadd += unsafe.Sizeof(uncommonType{})
}
@@ -922,7 +1018,7 @@ func (t *funcType) in() []*rtype {
}
func (t *funcType) out() []*rtype {
- uadd := uintptr(unsafe.Sizeof(*t))
+ uadd := unsafe.Sizeof(*t)
if t.tflag&tflagUncommon != 0 {
uadd += unsafe.Sizeof(uncommonType{})
}
@@ -952,15 +1048,15 @@ func (t *interfaceType) Method(i int) (m Method) {
return
}
p := &t.methods[i]
- m.Name = p.name.name()
- if !p.name.isExported() {
- pkgPath := p.name.pkgPath()
- if pkgPath == nil {
- pkgPath = t.pkgPath
+ pname := t.nameOff(p.name)
+ m.Name = pname.name()
+ if !pname.isExported() {
+ m.PkgPath = pname.pkgPath()
+ if m.PkgPath == "" {
+ m.PkgPath = t.pkgPath.name()
}
- m.PkgPath = *pkgPath
}
- m.Type = toType(p.typ)
+ m.Type = toType(t.typeOff(p.typ))
m.Index = i
return
}
@@ -976,7 +1072,7 @@ func (t *interfaceType) MethodByName(name string) (m Method, ok bool) {
var p *imethod
for i := range t.methods {
p = &t.methods[i]
- if p.name.name() == name {
+ if t.nameOff(p.name).name() == name {
return t.Method(i), true
}
}
@@ -1096,9 +1192,9 @@ func (t *structType) Field(i int) (f StructField) {
f.Name = t.Name()
f.Anonymous = true
}
- if t.pkgPath != nil && !p.name.isExported() {
+ if !p.name.isExported() {
// Fields never have an import path in their name.
- f.PkgPath = *t.pkgPath
+ f.PkgPath = t.pkgPath.name()
}
if tag := p.name.tag(); tag != "" {
f.Tag = StructTag(tag)
@@ -1317,7 +1413,7 @@ func (t *rtype) ptrTo() *rtype {
}
// Look in known types.
- s := "*" + t.string
+ s := "*" + t.String()
for _, tt := range typesByString(s) {
p = (*ptrType)(unsafe.Pointer(tt))
if p.elem == t {
@@ -1334,7 +1430,7 @@ func (t *rtype) ptrTo() *rtype {
prototype := *(**ptrType)(unsafe.Pointer(&iptr))
*p = *prototype
- p.string = s
+ p.str = resolveReflectName(newName(s, "", "", false))
// For the type structures linked into the binary, the
// compiler provides a good hash of the string.
@@ -1416,7 +1512,7 @@ func implements(T, V *rtype) bool {
for j := 0; j < len(v.methods); j++ {
tm := &t.methods[i]
vm := &v.methods[j]
- if vm.name.name() == tm.name.name() && vm.typ == tm.typ {
+ if V.nameOff(vm.name).name() == t.nameOff(tm.name).name() && V.typeOff(vm.typ) == t.typeOff(tm.typ) {
if i++; i >= len(t.methods) {
return true
}
@@ -1430,10 +1526,11 @@ func implements(T, V *rtype) bool {
return false
}
i := 0
- for j := 0; j < len(v.methods); j++ {
+ vmethods := v.methods()
+ for j := 0; j < int(v.mcount); j++ {
tm := &t.methods[i]
- vm := &v.methods[j]
- if vm.name.name() == tm.name.name() && vm.mtyp == tm.typ {
+ vm := vmethods[j]
+ if V.nameOff(vm.name).name() == t.nameOff(tm.name).name() && V.typeOff(vm.mtyp) == t.typeOff(tm.typ) {
if i++; i >= len(t.methods) {
return true
}
@@ -1558,30 +1655,48 @@ func haveIdenticalUnderlyingType(T, V *rtype) bool {
}
// typelinks is implemented in package runtime.
-// It returns a slice of all the 'typelink' information in the binary,
-// which is to say a slice of known types, sorted by string.
+// It returns a slice of the sections in each module,
+// and a slice of *rtype offsets in each module.
+//
+// The types in each module are sorted by string. That is, the first
+// two linked types of the first module are:
+//
+// d0 := sections[0]
+// t1 := (*rtype)(add(d0, offset[0][0]))
+// t2 := (*rtype)(add(d0, offset[0][1]))
+//
+// and
+//
+// t1.String() < t2.String()
+//
// Note that strings are not unique identifiers for types:
// there can be more than one with a given string.
// Only types we might want to look up are included:
// pointers, channels, maps, slices, and arrays.
-func typelinks() [][]*rtype
+func typelinks() (sections []unsafe.Pointer, offset [][]int32)
+
+func rtypeOff(section unsafe.Pointer, off int32) *rtype {
+ return (*rtype)(add(section, uintptr(off)))
+}
// typesByString returns the subslice of typelinks() whose elements have
// the given string representation.
// It may be empty (no known types with that string) or may have
// multiple elements (multiple types with that string).
func typesByString(s string) []*rtype {
- typs := typelinks()
+ sections, offset := typelinks()
var ret []*rtype
- for _, typ := range typs {
+ for offsI, offs := range offset {
+ section := sections[offsI]
+
// We are looking for the first index i where the string becomes >= s.
- // This is a copy of sort.Search, with f(h) replaced by (*typ[h].string >= s).
- i, j := 0, len(typ)
+ // This is a copy of sort.Search, with f(h) replaced by (*typ[h].String() >= s).
+ i, j := 0, len(offs)
for i < j {
h := i + (j-i)/2 // avoid overflow when computing h
// i ≤ h < j
- if !(typ[h].string >= s) {
+ if !(rtypeOff(section, offs[h]).String() >= s) {
i = h + 1 // preserves f(i-1) == false
} else {
j = h // preserves f(j) == true
@@ -1592,17 +1707,12 @@ func typesByString(s string) []*rtype {
// Having found the first, linear scan forward to find the last.
// We could do a second binary search, but the caller is going
// to do a linear scan anyway.
- j = i
- for j < len(typ) && typ[j].string == s {
- j++
- }
-
- if j > i {
- if ret == nil {
- ret = typ[i:j:j]
- } else {
- ret = append(ret, typ[i:j]...)
+ for j := i; j < len(offs); j++ {
+ typ := rtypeOff(section, offs[j])
+ if typ.String() != s {
+ break
}
+ ret = append(ret, typ)
}
}
return ret
@@ -1695,11 +1805,11 @@ func ChanOf(dir ChanDir, t Type) Type {
lookupCache.Unlock()
panic("reflect.ChanOf: invalid dir")
case SendDir:
- s = "chan<- " + typ.string
+ s = "chan<- " + typ.String()
case RecvDir:
- s = "<-chan " + typ.string
+ s = "<-chan " + typ.String()
case BothDir:
- s = "chan " + typ.string
+ s = "chan " + typ.String()
}
for _, tt := range typesByString(s) {
ch := (*chanType)(unsafe.Pointer(tt))
@@ -1714,7 +1824,7 @@ func ChanOf(dir ChanDir, t Type) Type {
ch := new(chanType)
*ch = *prototype
ch.dir = uintptr(dir)
- ch.string = s
+ ch.str = resolveReflectName(newName(s, "", "", false))
ch.hash = fnv1(typ.hash, 'c', byte(dir))
ch.elem = typ
@@ -1744,7 +1854,7 @@ func MapOf(key, elem Type) Type {
}
// Look in known types.
- s := "map[" + ktyp.string + "]" + etyp.string
+ s := "map[" + ktyp.String() + "]" + etyp.String()
for _, tt := range typesByString(s) {
mt := (*mapType)(unsafe.Pointer(tt))
if mt.key == ktyp && mt.elem == etyp {
@@ -1756,7 +1866,7 @@ func MapOf(key, elem Type) Type {
var imap interface{} = (map[unsafe.Pointer]unsafe.Pointer)(nil)
mt := new(mapType)
*mt = **(**mapType)(unsafe.Pointer(&imap))
- mt.string = s
+ mt.str = resolveReflectName(newName(s, "", "", false))
mt.hash = fnv1(etyp.hash, 'm', byte(ktyp.hash>>24), byte(ktyp.hash>>16), byte(ktyp.hash>>8), byte(ktyp.hash))
mt.key = ktyp
mt.elem = etyp
@@ -1914,7 +2024,7 @@ func FuncOf(in, out []Type, variadic bool) Type {
}
// Populate the remaining fields of ft and store in cache.
- ft.string = str
+ ft.str = resolveReflectName(newName(str, "", "", false))
funcLookupCache.m[hash] = append(funcLookupCache.m[hash], &ft.rtype)
return &ft.rtype
@@ -1930,9 +2040,9 @@ func funcStr(ft *funcType) string {
}
if ft.IsVariadic() && i == int(ft.inCount)-1 {
repr = append(repr, "..."...)
- repr = append(repr, (*sliceType)(unsafe.Pointer(t)).elem.string...)
+ repr = append(repr, (*sliceType)(unsafe.Pointer(t)).elem.String()...)
} else {
- repr = append(repr, t.string...)
+ repr = append(repr, t.String()...)
}
}
repr = append(repr, ')')
@@ -1946,7 +2056,7 @@ func funcStr(ft *funcType) string {
if i > 0 {
repr = append(repr, ", "...)
}
- repr = append(repr, t.string...)
+ repr = append(repr, t.String()...)
}
if len(out) > 1 {
repr = append(repr, ')')
@@ -2111,8 +2221,8 @@ func bucketOf(ktyp, etyp *rtype) *rtype {
b.ptrdata = ptrdata
b.kind = kind
b.gcdata = gcdata
- s := "bucket(" + ktyp.string + "," + etyp.string + ")"
- b.string = s
+ s := "bucket(" + ktyp.String() + "," + etyp.String() + ")"
+ b.str = resolveReflectName(newName(s, "", "", false))
return b
}
@@ -2128,7 +2238,7 @@ func SliceOf(t Type) Type {
}
// Look in known types.
- s := "[]" + typ.string
+ s := "[]" + typ.String()
for _, tt := range typesByString(s) {
slice := (*sliceType)(unsafe.Pointer(tt))
if slice.elem == typ {
@@ -2141,28 +2251,63 @@ func SliceOf(t Type) Type {
prototype := *(**sliceType)(unsafe.Pointer(&islice))
slice := new(sliceType)
*slice = *prototype
- slice.string = s
+ slice.tflag = 0
+ slice.str = resolveReflectName(newName(s, "", "", false))
slice.hash = fnv1(typ.hash, '[')
slice.elem = typ
return cachePut(ckey, &slice.rtype)
}
-// structTypeWithMethods is a structType created at runtime with StructOf.
-// It is needed to pin the []method slice from its associated uncommonType struct.
-// Keep in sync with the memory layout of structType.
-type structTypeWithMethods struct {
- structType
- u uncommonType
-}
-
// The structLookupCache caches StructOf lookups.
// StructOf does not share the common lookupCache since we need to pin
-// the *structType and its associated *uncommonType (especially the
-// []method slice field of that uncommonType.)
+// the memory associated with *structTypeFixedN.
var structLookupCache struct {
sync.RWMutex
- m map[uint32][]*structTypeWithMethods // keyed by hash calculated in StructOf
+ m map[uint32][]interface {
+ common() *rtype
+ } // keyed by hash calculated in StructOf
+}
+
+type structTypeUncommon struct {
+ structType
+ u uncommonType
+}
+
+// A *rtype representing a struct is followed directly in memory by an
+// array of method objects representing the methods attached to the
+// struct. To get the same layout for a run time generated type, we
+// need an array directly following the uncommonType memory. The types
+// structTypeFixed4, ...structTypeFixedN are used to do this.
+//
+// A similar strategy is used for funcTypeFixed4, ...funcTypeFixedN.
+
+// TODO(crawshaw): as these structTypeFixedN and funcTypeFixedN structs
+// have no methods, they could be defined at runtime using the StructOf
+// function.
+
+type structTypeFixed4 struct {
+ structType
+ u uncommonType
+ m [4]method
+}
+
+type structTypeFixed8 struct {
+ structType
+ u uncommonType
+ m [8]method
+}
+
+type structTypeFixed16 struct {
+ structType
+ u uncommonType
+ m [16]method
+}
+
+type structTypeFixed32 struct {
+ structType
+ u uncommonType
+ m [32]method
}
// StructOf returns the struct type containing fields.
@@ -2179,7 +2324,7 @@ func StructOf(fields []StructField) Type {
typalign uint8
comparable = true
hashable = true
- typ = new(structTypeWithMethods)
+ methods []method
fs = make([]structField, len(fields))
repr = make([]byte, 0, 64)
@@ -2215,11 +2360,11 @@ func StructOf(fields []StructField) Type {
// Embedded ** and *interface{} are illegal
elem := ft.Elem()
if k := elem.Kind(); k == Ptr || k == Interface {
- panic("reflect.StructOf: illegal anonymous field type " + ft.string)
+ panic("reflect.StructOf: illegal anonymous field type " + ft.String())
}
name = elem.String()
} else {
- name = ft.string
+ name = ft.String()
}
// TODO(sbinet) check for syntactically impossible type names?
@@ -2227,12 +2372,13 @@ func StructOf(fields []StructField) Type {
case Interface:
ift := (*interfaceType)(unsafe.Pointer(ft))
for im, m := range ift.methods {
- if m.name.pkgPath() != nil {
+ if ift.nameOff(m.name).pkgPath() != "" {
// TODO(sbinet)
panic("reflect: embedded interface with unexported method(s) not implemented")
}
var (
+ mtyp = ift.typeOff(m.typ)
ifield = i
imethod = im
ifn Value
@@ -2240,7 +2386,7 @@ func StructOf(fields []StructField) Type {
)
if ft.kind&kindDirectIface != 0 {
- tfn = MakeFunc(m.typ, func(in []Value) []Value {
+ tfn = MakeFunc(mtyp, func(in []Value) []Value {
var args []Value
var recv = in[0]
if len(in) > 1 {
@@ -2248,7 +2394,7 @@ func StructOf(fields []StructField) Type {
}
return recv.Field(ifield).Method(imethod).Call(args)
})
- ifn = MakeFunc(m.typ, func(in []Value) []Value {
+ ifn = MakeFunc(mtyp, func(in []Value) []Value {
var args []Value
var recv = in[0]
if len(in) > 1 {
@@ -2256,9 +2402,8 @@ func StructOf(fields []StructField) Type {
}
return recv.Field(ifield).Method(imethod).Call(args)
})
-
} else {
- tfn = MakeFunc(m.typ, func(in []Value) []Value {
+ tfn = MakeFunc(mtyp, func(in []Value) []Value {
var args []Value
var recv = in[0]
if len(in) > 1 {
@@ -2266,7 +2411,7 @@ func StructOf(fields []StructField) Type {
}
return recv.Field(ifield).Method(imethod).Call(args)
})
- ifn = MakeFunc(m.typ, func(in []Value) []Value {
+ ifn = MakeFunc(mtyp, func(in []Value) []Value {
var args []Value
var recv = Indirect(in[0])
if len(in) > 1 {
@@ -2274,47 +2419,62 @@ func StructOf(fields []StructField) Type {
}
return recv.Field(ifield).Method(imethod).Call(args)
})
-
}
- typ.u.methods = append(
- typ.u.methods,
- method{
- name: m.name,
- mtyp: m.typ,
- ifn: unsafe.Pointer(&ifn),
- tfn: unsafe.Pointer(&tfn),
- },
- )
+ methods = append(methods, method{
+ name: resolveReflectName(ift.nameOff(m.name)),
+ mtyp: resolveReflectType(mtyp),
+ ifn: resolveReflectText(unsafe.Pointer(&ifn)),
+ tfn: resolveReflectText(unsafe.Pointer(&tfn)),
+ })
}
case Ptr:
ptr := (*ptrType)(unsafe.Pointer(ft))
if unt := ptr.uncommon(); unt != nil {
- for _, m := range unt.methods {
- if m.name.pkgPath() != nil {
+ for _, m := range unt.methods() {
+ mname := ptr.nameOff(m.name)
+ if mname.pkgPath() != "" {
// TODO(sbinet)
panic("reflect: embedded interface with unexported method(s) not implemented")
}
- typ.u.methods = append(typ.u.methods, m)
+ methods = append(methods, method{
+ name: resolveReflectName(mname),
+ mtyp: resolveReflectType(ptr.typeOff(m.mtyp)),
+ ifn: resolveReflectText(ptr.textOff(m.ifn)),
+ tfn: resolveReflectText(ptr.textOff(m.tfn)),
+ })
}
}
if unt := ptr.elem.uncommon(); unt != nil {
- for _, m := range unt.methods {
- if m.name.pkgPath() != nil {
+ for _, m := range unt.methods() {
+ mname := ptr.nameOff(m.name)
+ if mname.pkgPath() != "" {
// TODO(sbinet)
panic("reflect: embedded interface with unexported method(s) not implemented")
}
- typ.u.methods = append(typ.u.methods, m)
+ methods = append(methods, method{
+ name: resolveReflectName(mname),
+ mtyp: resolveReflectType(ptr.elem.typeOff(m.mtyp)),
+ ifn: resolveReflectText(ptr.elem.textOff(m.ifn)),
+ tfn: resolveReflectText(ptr.elem.textOff(m.tfn)),
+ })
}
}
default:
if unt := ft.uncommon(); unt != nil {
- for _, m := range unt.methods {
- if m.name.pkgPath() != nil {
+ for _, m := range unt.methods() {
+ mname := ft.nameOff(m.name)
+ if mname.pkgPath() != "" {
// TODO(sbinet)
panic("reflect: embedded interface with unexported method(s) not implemented")
}
- typ.u.methods = append(typ.u.methods, m)
+ methods = append(methods, method{
+ name: resolveReflectName(mname),
+ mtyp: resolveReflectType(ft.typeOff(m.mtyp)),
+ ifn: resolveReflectText(ft.textOff(m.ifn)),
+ tfn: resolveReflectText(ft.textOff(m.tfn)),
+ })
+
}
}
}
@@ -2326,7 +2486,7 @@ func StructOf(fields []StructField) Type {
hash = fnv1(hash, byte(ft.hash>>24), byte(ft.hash>>16), byte(ft.hash>>8), byte(ft.hash))
- repr = append(repr, (" " + ft.string)...)
+ repr = append(repr, (" " + ft.String())...)
if f.name.tagLen() > 0 {
hash = fnv1(hash, []byte(f.name.tag())...)
repr = append(repr, (" " + strconv.Quote(f.name.tag()))...)
@@ -2346,6 +2506,49 @@ func StructOf(fields []StructField) Type {
fs[i] = f
}
+
+ var typ *structType
+ var ut *uncommonType
+ var typPin interface {
+ common() *rtype
+ } // structTypeFixedN
+
+ switch {
+ case len(methods) == 0:
+ t := new(structTypeUncommon)
+ typ = &t.structType
+ ut = &t.u
+ typPin = t
+ case len(methods) <= 4:
+ t := new(structTypeFixed4)
+ typ = &t.structType
+ ut = &t.u
+ copy(t.m[:], methods)
+ typPin = t
+ case len(methods) <= 8:
+ t := new(structTypeFixed8)
+ typ = &t.structType
+ ut = &t.u
+ copy(t.m[:], methods)
+ typPin = t
+ case len(methods) <= 16:
+ t := new(structTypeFixed16)
+ typ = &t.structType
+ ut = &t.u
+ copy(t.m[:], methods)
+ typPin = t
+ case len(methods) <= 32:
+ t := new(structTypeFixed32)
+ typ = &t.structType
+ ut = &t.u
+ copy(t.m[:], methods)
+ typPin = t
+ default:
+ panic("reflect.StructOf: too many methods")
+ }
+ ut.mcount = uint16(len(methods))
+ ut.moff = uint16(unsafe.Sizeof(uncommonType{}))
+
if len(fs) > 0 {
repr = append(repr, ' ')
}
@@ -2359,15 +2562,16 @@ func StructOf(fields []StructField) Type {
// Make the struct type.
var istruct interface{} = struct{}{}
prototype := *(**structType)(unsafe.Pointer(&istruct))
- typ.structType = *prototype
- typ.structType.fields = fs
+ *typ = *prototype
+ typ.fields = fs
// Look in cache
structLookupCache.RLock()
- for _, t := range structLookupCache.m[hash] {
- if haveIdenticalUnderlyingType(&typ.rtype, &t.rtype) {
+ for _, st := range structLookupCache.m[hash] {
+ t := st.common()
+ if haveIdenticalUnderlyingType(&typ.rtype, t) {
structLookupCache.RUnlock()
- return &t.rtype
+ return t
}
}
structLookupCache.RUnlock()
@@ -2376,11 +2580,14 @@ func StructOf(fields []StructField) Type {
structLookupCache.Lock()
defer structLookupCache.Unlock()
if structLookupCache.m == nil {
- structLookupCache.m = make(map[uint32][]*structTypeWithMethods)
+ structLookupCache.m = make(map[uint32][]interface {
+ common() *rtype
+ })
}
- for _, t := range structLookupCache.m[hash] {
- if haveIdenticalUnderlyingType(&typ.rtype, &t.rtype) {
- return &t.rtype
+ for _, st := range structLookupCache.m[hash] {
+ t := st.common()
+ if haveIdenticalUnderlyingType(&typ.rtype, t) {
+ return t
}
}
@@ -2390,18 +2597,17 @@ func StructOf(fields []StructField) Type {
// even if 't' wasn't a structType with methods, we should be ok
// as the 'u uncommonType' field won't be accessed except when
// tflag&tflagUncommon is set.
- tt := (*structTypeWithMethods)(unsafe.Pointer(t))
- structLookupCache.m[hash] = append(structLookupCache.m[hash], tt)
- return &tt.rtype
+ structLookupCache.m[hash] = append(structLookupCache.m[hash], t)
+ return t
}
}
- typ.string = str
+ typ.str = resolveReflectName(newName(str, "", "", false))
typ.hash = hash
typ.size = size
typ.align = typalign
typ.fieldAlign = typalign
- if len(typ.u.methods) > 0 {
+ if len(methods) > 0 {
typ.tflag |= tflagUncommon
}
if !hasPtr {
@@ -2501,18 +2707,18 @@ func StructOf(fields []StructField) Type {
typ.kind &^= kindDirectIface
}
- structLookupCache.m[hash] = append(structLookupCache.m[hash], typ)
+ structLookupCache.m[hash] = append(structLookupCache.m[hash], typPin)
return &typ.rtype
}
func runtimeStructField(field StructField) structField {
exported := field.PkgPath == ""
if field.Name == "" {
- t := field.Type
+ t := field.Type.(*rtype)
if t.Kind() == Ptr {
- t = t.Elem()
+ t = t.Elem().(*rtype)
}
- exported = isExported(t.Name())
+ exported = t.nameOff(t.str).isExported()
} else if exported {
b0 := field.Name[0]
if ('a' <= b0 && b0 <= 'z') || b0 == '_' {
@@ -2520,6 +2726,7 @@ func runtimeStructField(field StructField) structField {
}
}
+ _ = resolveReflectType(field.Type.common())
return structField{
name: newName(field.Name, string(field.Tag), field.PkgPath, exported),
typ: field.Type.common(),
@@ -2527,25 +2734,6 @@ func runtimeStructField(field StructField) structField {
}
}
-func isExported(s string) bool {
- if s == "" {
- return false
- }
- // FIXME(sbinet): handle utf8/runes (see https://golang.org/issue/15064)
- // TODO: turn rtype.string into a reflect.name type, and put the exported
- // bit on there which can be checked here with field.Type.(*rtype).string.isExported()
- // When done, remove the documented limitation of StructOf.
- r := s[0]
- switch {
- case 'A' <= r && r <= 'Z':
- return true
- case r == '_' || 'a' <= r && r <= 'z':
- return false
- default:
- panic("reflect.StructOf: creating a struct with UTF-8 fields is not supported yet")
- }
-}
-
// typeptrdata returns the length in bytes of the prefix of t
// containing pointer data. Anything after this offset is scalar data.
// keep in sync with ../cmd/compile/internal/gc/reflect.go
@@ -2595,7 +2783,7 @@ func ArrayOf(count int, elem Type) Type {
}
// Look in known types.
- s := "[" + strconv.Itoa(count) + "]" + typ.string
+ s := "[" + strconv.Itoa(count) + "]" + typ.String()
for _, tt := range typesByString(s) {
array := (*arrayType)(unsafe.Pointer(tt))
if array.elem == typ {
@@ -2608,7 +2796,7 @@ func ArrayOf(count int, elem Type) Type {
prototype := *(**arrayType)(unsafe.Pointer(&iarray))
array := new(arrayType)
*array = *prototype
- array.string = s
+ array.str = resolveReflectName(newName(s, "", "", false))
array.hash = fnv1(typ.hash, '[')
for n := uint32(count); n > 0; n >>= 8 {
array.hash = fnv1(array.hash, byte(n))
@@ -2862,11 +3050,11 @@ func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uin
var s string
if rcvr != nil {
- s = "methodargs(" + rcvr.string + ")(" + t.string + ")"
+ s = "methodargs(" + rcvr.String() + ")(" + t.String() + ")"
} else {
- s = "funcargs(" + t.string + ")"
+ s = "funcargs(" + t.String() + ")"
}
- x.string = s
+ x.str = resolveReflectName(newName(s, "", "", false))
// cache result for future callers
if layoutCache.m == nil {
diff --git a/src/reflect/value.go b/src/reflect/value.go
index 262545d973..e6b846e5d1 100644
--- a/src/reflect/value.go
+++ b/src/reflect/value.go
@@ -553,7 +553,7 @@ func methodReceiver(op string, v Value, methodIndex int) (rcvrtype, t *rtype, fn
panic("reflect: internal error: invalid method index")
}
m := &tt.methods[i]
- if !m.name.isExported() {
+ if !tt.nameOff(m.name).isExported() {
panic("reflect: " + op + " of unexported method")
}
iface := (*nonEmptyInterface)(v.ptr)
@@ -562,19 +562,20 @@ func methodReceiver(op string, v Value, methodIndex int) (rcvrtype, t *rtype, fn
}
rcvrtype = iface.itab.typ
fn = unsafe.Pointer(&iface.itab.fun[i])
- t = m.typ
+ t = tt.typeOff(m.typ)
} else {
rcvrtype = v.typ
ut := v.typ.uncommon()
- if ut == nil || uint(i) >= uint(len(ut.methods)) {
+ if ut == nil || uint(i) >= uint(ut.mcount) {
panic("reflect: internal error: invalid method index")
}
- m := &ut.methods[i]
- if !m.name.isExported() {
+ m := ut.methods()[i]
+ if !v.typ.nameOff(m.name).isExported() {
panic("reflect: " + op + " of unexported method")
}
- fn = unsafe.Pointer(&m.ifn)
- t = m.mtyp
+ ifn := v.typ.textOff(m.ifn)
+ fn = unsafe.Pointer(&ifn)
+ t = v.typ.typeOff(m.mtyp)
}
return
}
@@ -665,7 +666,7 @@ func (v Value) Cap() int {
case Array:
return v.typ.Len()
case Chan:
- return int(chancap(v.pointer()))
+ return chancap(v.pointer())
case Slice:
// Slice is always bigger than a word; assume flagIndir.
return (*sliceHeader)(v.ptr).Cap
@@ -884,7 +885,7 @@ func (v Value) Int() int64 {
case Int32:
return int64(*(*int32)(p))
case Int64:
- return int64(*(*int64)(p))
+ return *(*int64)(p)
}
panic(&ValueError{"reflect.Value.Int", v.kind()})
}
@@ -1435,7 +1436,7 @@ func (v Value) SetCap(n int) {
v.mustBeAssignable()
v.mustBe(Slice)
s := (*sliceHeader)(v.ptr)
- if n < int(s.Len) || n > int(s.Cap) {
+ if n < s.Len || n > s.Cap {
panic("reflect: slice capacity out of range in SetCap")
}
s.Cap = n
@@ -1537,7 +1538,7 @@ func (v Value) Slice(i, j int) Value {
case Slice:
typ = (*sliceType)(unsafe.Pointer(v.typ))
s := (*sliceHeader)(v.ptr)
- base = unsafe.Pointer(s.Data)
+ base = s.Data
cap = s.Cap
case String:
@@ -1683,15 +1684,15 @@ func (v Value) Type() Type {
panic("reflect: internal error: invalid method index")
}
m := &tt.methods[i]
- return m.typ
+ return v.typ.typeOff(m.typ)
}
// Method on concrete type.
ut := v.typ.uncommon()
- if ut == nil || uint(i) >= uint(len(ut.methods)) {
+ if ut == nil || uint(i) >= uint(ut.mcount) {
panic("reflect: internal error: invalid method index")
}
- m := &ut.methods[i]
- return m.mtyp
+ m := ut.methods()[i]
+ return v.typ.typeOff(m.mtyp)
}
// Uint returns v's underlying value, as a uint64.
@@ -1709,7 +1710,7 @@ func (v Value) Uint() uint64 {
case Uint32:
return uint64(*(*uint32)(p))
case Uint64:
- return uint64(*(*uint64)(p))
+ return *(*uint64)(p)
case Uintptr:
return uint64(*(*uintptr)(p))
}
@@ -2266,13 +2267,13 @@ func makeInt(f flag, bits uint64, t Type) Value {
ptr := unsafe_New(typ)
switch typ.size {
case 1:
- *(*uint8)(unsafe.Pointer(ptr)) = uint8(bits)
+ *(*uint8)(ptr) = uint8(bits)
case 2:
- *(*uint16)(unsafe.Pointer(ptr)) = uint16(bits)
+ *(*uint16)(ptr) = uint16(bits)
case 4:
- *(*uint32)(unsafe.Pointer(ptr)) = uint32(bits)
+ *(*uint32)(ptr) = uint32(bits)
case 8:
- *(*uint64)(unsafe.Pointer(ptr)) = bits
+ *(*uint64)(ptr) = bits
}
return Value{typ, ptr, f | flagIndir | flag(typ.Kind())}
}
@@ -2284,9 +2285,9 @@ func makeFloat(f flag, v float64, t Type) Value {
ptr := unsafe_New(typ)
switch typ.size {
case 4:
- *(*float32)(unsafe.Pointer(ptr)) = float32(v)
+ *(*float32)(ptr) = float32(v)
case 8:
- *(*float64)(unsafe.Pointer(ptr)) = v
+ *(*float64)(ptr) = v
}
return Value{typ, ptr, f | flagIndir | flag(typ.Kind())}
}
@@ -2298,9 +2299,9 @@ func makeComplex(f flag, v complex128, t Type) Value {
ptr := unsafe_New(typ)
switch typ.size {
case 8:
- *(*complex64)(unsafe.Pointer(ptr)) = complex64(v)
+ *(*complex64)(ptr) = complex64(v)
case 16:
- *(*complex128)(unsafe.Pointer(ptr)) = v
+ *(*complex128)(ptr) = v
}
return Value{typ, ptr, f | flagIndir | flag(typ.Kind())}
}
diff --git a/src/regexp/exec_test.go b/src/regexp/exec_test.go
index cfc1e147c1..463fcf1848 100644
--- a/src/regexp/exec_test.go
+++ b/src/regexp/exec_test.go
@@ -672,9 +672,11 @@ func benchmark(b *testing.B, re string, n int) {
const (
easy0 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ$"
+ easy0i = "(?i)ABCDEFGHIJklmnopqrstuvwxyz$"
easy1 = "A[AB]B[BC]C[CD]D[DE]E[EF]F[FG]G[GH]H[HI]I[IJ]J$"
medium = "[XYZ]ABCDEFGHIJKLMNOPQRSTUVWXYZ$"
hard = "[ -~]*ABCDEFGHIJKLMNOPQRSTUVWXYZ$"
+ hard1 = "ABCD|CDEF|EFGH|GHIJ|IJKL|KLMN|MNOP|OPQR|QRST|STUV|UVWX|WXYZ"
)
func BenchmarkMatchEasy0_32(b *testing.B) { benchmark(b, easy0, 32<<0) }
@@ -682,6 +684,11 @@ func BenchmarkMatchEasy0_1K(b *testing.B) { benchmark(b, easy0, 1<<10) }
func BenchmarkMatchEasy0_32K(b *testing.B) { benchmark(b, easy0, 32<<10) }
func BenchmarkMatchEasy0_1M(b *testing.B) { benchmark(b, easy0, 1<<20) }
func BenchmarkMatchEasy0_32M(b *testing.B) { benchmark(b, easy0, 32<<20) }
+func BenchmarkMatchEasy0i_32(b *testing.B) { benchmark(b, easy0i, 32<<0) }
+func BenchmarkMatchEasy0i_1K(b *testing.B) { benchmark(b, easy0i, 1<<10) }
+func BenchmarkMatchEasy0i_32K(b *testing.B) { benchmark(b, easy0i, 32<<10) }
+func BenchmarkMatchEasy0i_1M(b *testing.B) { benchmark(b, easy0i, 1<<20) }
+func BenchmarkMatchEasy0i_32M(b *testing.B) { benchmark(b, easy0i, 32<<20) }
func BenchmarkMatchEasy1_32(b *testing.B) { benchmark(b, easy1, 32<<0) }
func BenchmarkMatchEasy1_1K(b *testing.B) { benchmark(b, easy1, 1<<10) }
func BenchmarkMatchEasy1_32K(b *testing.B) { benchmark(b, easy1, 32<<10) }
@@ -697,6 +704,11 @@ func BenchmarkMatchHard_1K(b *testing.B) { benchmark(b, hard, 1<<10) }
func BenchmarkMatchHard_32K(b *testing.B) { benchmark(b, hard, 32<<10) }
func BenchmarkMatchHard_1M(b *testing.B) { benchmark(b, hard, 1<<20) }
func BenchmarkMatchHard_32M(b *testing.B) { benchmark(b, hard, 32<<20) }
+func BenchmarkMatchHard1_32(b *testing.B) { benchmark(b, hard1, 32<<0) }
+func BenchmarkMatchHard1_1K(b *testing.B) { benchmark(b, hard1, 1<<10) }
+func BenchmarkMatchHard1_32K(b *testing.B) { benchmark(b, hard1, 32<<10) }
+func BenchmarkMatchHard1_1M(b *testing.B) { benchmark(b, hard1, 1<<20) }
+func BenchmarkMatchHard1_32M(b *testing.B) { benchmark(b, hard1, 32<<20) }
func TestLongest(t *testing.T) {
re, err := Compile(`a(|b)`)
diff --git a/src/regexp/onepass.go b/src/regexp/onepass.go
index 5b82f9666e..4991954820 100644
--- a/src/regexp/onepass.go
+++ b/src/regexp/onepass.go
@@ -450,7 +450,7 @@ func makeOnePass(p *onePassProg) *onePassProg {
for !instQueue.empty() {
visitQueue.clear()
pc := instQueue.next()
- if !check(uint32(pc), m) {
+ if !check(pc, m) {
p = notOnePass
break
}
diff --git a/src/runtime/alg.go b/src/runtime/alg.go
index 7aacc8cf9b..66943495b5 100644
--- a/src/runtime/alg.go
+++ b/src/runtime/alg.go
@@ -146,7 +146,7 @@ func interhash(p unsafe.Pointer, h uintptr) uintptr {
t := tab._type
fn := t.alg.hash
if fn == nil {
- panic(errorString("hash of unhashable type " + t._string))
+ panic(errorString("hash of unhashable type " + t.string()))
}
if isDirectIface(t) {
return c1 * fn(unsafe.Pointer(&a.data), h^c0)
@@ -163,7 +163,7 @@ func nilinterhash(p unsafe.Pointer, h uintptr) uintptr {
}
fn := t.alg.hash
if fn == nil {
- panic(errorString("hash of unhashable type " + t._string))
+ panic(errorString("hash of unhashable type " + t.string()))
}
if isDirectIface(t) {
return c1 * fn(unsafe.Pointer(&a.data), h^c0)
@@ -221,7 +221,7 @@ func efaceeq(x, y eface) bool {
}
eq := t.alg.equal
if eq == nil {
- panic(errorString("comparing uncomparable type " + t._string))
+ panic(errorString("comparing uncomparable type " + t.string()))
}
if isDirectIface(t) {
return eq(noescape(unsafe.Pointer(&x.data)), noescape(unsafe.Pointer(&y.data)))
@@ -239,7 +239,7 @@ func ifaceeq(x, y iface) bool {
t := xtab._type
eq := t.alg.equal
if eq == nil {
- panic(errorString("comparing uncomparable type " + t._string))
+ panic(errorString("comparing uncomparable type " + t.string()))
}
if isDirectIface(t) {
return eq(noescape(unsafe.Pointer(&x.data)), noescape(unsafe.Pointer(&y.data)))
diff --git a/src/runtime/append_test.go b/src/runtime/append_test.go
index 3170870b0e..cd28e3dca6 100644
--- a/src/runtime/append_test.go
+++ b/src/runtime/append_test.go
@@ -7,6 +7,14 @@ import "testing"
const N = 20
+func BenchmarkMakeSlice(b *testing.B) {
+ var x []byte
+ for i := 0; i < b.N; i++ {
+ x = make([]byte, 32)
+ _ = x
+ }
+}
+
func BenchmarkGrowSliceBytes(b *testing.B) {
b.StopTimer()
var x = make([]byte, 9)
@@ -226,3 +234,132 @@ func BenchmarkCopy16String(b *testing.B) { benchmarkCopyStr(b, 16) }
func BenchmarkCopy32String(b *testing.B) { benchmarkCopyStr(b, 32) }
func BenchmarkCopy128String(b *testing.B) { benchmarkCopyStr(b, 128) }
func BenchmarkCopy1024String(b *testing.B) { benchmarkCopyStr(b, 1024) }
+
+var (
+ sByte []byte
+ s1Ptr []uintptr
+ s2Ptr [][2]uintptr
+ s3Ptr [][3]uintptr
+ s4Ptr [][4]uintptr
+)
+
+// BenchmarkAppendInPlace tests the performance of append
+// when the result is being written back to the same slice.
+// In order for the in-place optimization to occur,
+// the slice must be referred to by address;
+// using a global is an easy way to trigger that.
+// We test the "grow" and "no grow" paths separately,
+// but not the "normal" (occasionally grow) path,
+// because it is a blend of the other two.
+// We use small numbers and small sizes in an attempt
+// to avoid benchmarking memory allocation and copying.
+// We use scalars instead of pointers in an attempt
+// to avoid benchmarking the write barriers.
+// We benchmark four common sizes (byte, pointer, string/interface, slice),
+// and one larger size.
+func BenchmarkAppendInPlace(b *testing.B) {
+ b.Run("NoGrow", func(b *testing.B) {
+ const C = 128
+
+ b.Run("Byte", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ sByte = make([]byte, C)
+ for j := 0; j < C; j++ {
+ sByte = append(sByte, 0x77)
+ }
+ }
+ })
+
+ b.Run("1Ptr", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ s1Ptr = make([]uintptr, C)
+ for j := 0; j < C; j++ {
+ s1Ptr = append(s1Ptr, 0x77)
+ }
+ }
+ })
+
+ b.Run("2Ptr", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ s2Ptr = make([][2]uintptr, C)
+ for j := 0; j < C; j++ {
+ s2Ptr = append(s2Ptr, [2]uintptr{0x77, 0x88})
+ }
+ }
+ })
+
+ b.Run("3Ptr", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ s3Ptr = make([][3]uintptr, C)
+ for j := 0; j < C; j++ {
+ s3Ptr = append(s3Ptr, [3]uintptr{0x77, 0x88, 0x99})
+ }
+ }
+ })
+
+ b.Run("4Ptr", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ s4Ptr = make([][4]uintptr, C)
+ for j := 0; j < C; j++ {
+ s4Ptr = append(s4Ptr, [4]uintptr{0x77, 0x88, 0x99, 0xAA})
+ }
+ }
+ })
+
+ })
+
+ b.Run("Grow", func(b *testing.B) {
+ const C = 5
+
+ b.Run("Byte", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ sByte = make([]byte, 0)
+ for j := 0; j < C; j++ {
+ sByte = append(sByte, 0x77)
+ sByte = sByte[:cap(sByte)]
+ }
+ }
+ })
+
+ b.Run("1Ptr", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ s1Ptr = make([]uintptr, 0)
+ for j := 0; j < C; j++ {
+ s1Ptr = append(s1Ptr, 0x77)
+ s1Ptr = s1Ptr[:cap(s1Ptr)]
+ }
+ }
+ })
+
+ b.Run("2Ptr", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ s2Ptr = make([][2]uintptr, 0)
+ for j := 0; j < C; j++ {
+ s2Ptr = append(s2Ptr, [2]uintptr{0x77, 0x88})
+ s2Ptr = s2Ptr[:cap(s2Ptr)]
+ }
+ }
+ })
+
+ b.Run("3Ptr", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ s3Ptr = make([][3]uintptr, 0)
+ for j := 0; j < C; j++ {
+ s3Ptr = append(s3Ptr, [3]uintptr{0x77, 0x88, 0x99})
+ s3Ptr = s3Ptr[:cap(s3Ptr)]
+ }
+ }
+ })
+
+ b.Run("4Ptr", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ s4Ptr = make([][4]uintptr, 0)
+ for j := 0; j < C; j++ {
+ s4Ptr = append(s4Ptr, [4]uintptr{0x77, 0x88, 0x99, 0xAA})
+ s4Ptr = s4Ptr[:cap(s4Ptr)]
+ }
+ }
+ })
+
+ })
+}
diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s
index 83db4d3e81..cdda29f347 100644
--- a/src/runtime/asm_amd64.s
+++ b/src/runtime/asm_amd64.s
@@ -1666,122 +1666,126 @@ big_loop_avx2_exit:
// TODO: Also use this in bytes.Index
TEXT strings·indexShortStr(SB),NOSPLIT,$0-40
MOVQ s+0(FP), DI
- MOVQ s_len+8(FP), CX
- MOVQ c+16(FP), AX
- MOVQ c_len+24(FP), BX
- CMPQ BX, CX
+ // We want len in DX and AX, because PCMPESTRI implicitly consumes them
+ MOVQ s_len+8(FP), DX
+ MOVQ c+16(FP), BP
+ MOVQ c_len+24(FP), AX
+ CMPQ AX, DX
JA fail
- CMPQ BX, $2
+ CMPQ DX, $16
+ JAE sse42
+no_sse42:
+ CMPQ AX, $2
JA _3_or_more
- MOVW (AX), AX
- LEAQ -1(DI)(CX*1), CX
+ MOVW (BP), BP
+ LEAQ -1(DI)(DX*1), DX
loop2:
MOVW (DI), SI
- CMPW SI,AX
+ CMPW SI,BP
JZ success
ADDQ $1,DI
- CMPQ DI,CX
+ CMPQ DI,DX
JB loop2
JMP fail
_3_or_more:
- CMPQ BX, $3
+ CMPQ AX, $3
JA _4_or_more
- MOVW 1(AX), DX
- MOVW (AX), AX
- LEAQ -2(DI)(CX*1), CX
+ MOVW 1(BP), BX
+ MOVW (BP), BP
+ LEAQ -2(DI)(DX*1), DX
loop3:
MOVW (DI), SI
- CMPW SI,AX
+ CMPW SI,BP
JZ partial_success3
ADDQ $1,DI
- CMPQ DI,CX
+ CMPQ DI,DX
JB loop3
JMP fail
partial_success3:
MOVW 1(DI), SI
- CMPW SI,DX
+ CMPW SI,BX
JZ success
ADDQ $1,DI
- CMPQ DI,CX
+ CMPQ DI,DX
JB loop3
JMP fail
_4_or_more:
- CMPQ BX, $4
+ CMPQ AX, $4
JA _5_or_more
- MOVL (AX), AX
- LEAQ -3(DI)(CX*1), CX
+ MOVL (BP), BP
+ LEAQ -3(DI)(DX*1), DX
loop4:
MOVL (DI), SI
- CMPL SI,AX
+ CMPL SI,BP
JZ success
ADDQ $1,DI
- CMPQ DI,CX
+ CMPQ DI,DX
JB loop4
JMP fail
_5_or_more:
- CMPQ BX, $7
+ CMPQ AX, $7
JA _8_or_more
- LEAQ 1(DI)(CX*1), CX
- SUBQ BX, CX
- MOVL -4(AX)(BX*1), DX
- MOVL (AX), AX
+ LEAQ 1(DI)(DX*1), DX
+ SUBQ AX, DX
+ MOVL -4(BP)(AX*1), BX
+ MOVL (BP), BP
loop5to7:
MOVL (DI), SI
- CMPL SI,AX
+ CMPL SI,BP
JZ partial_success5to7
ADDQ $1,DI
- CMPQ DI,CX
+ CMPQ DI,DX
JB loop5to7
JMP fail
partial_success5to7:
- MOVL -4(BX)(DI*1), SI
- CMPL SI,DX
+ MOVL -4(AX)(DI*1), SI
+ CMPL SI,BX
JZ success
ADDQ $1,DI
- CMPQ DI,CX
+ CMPQ DI,DX
JB loop5to7
JMP fail
_8_or_more:
- CMPQ BX, $8
+ CMPQ AX, $8
JA _9_or_more
- MOVQ (AX), AX
- LEAQ -7(DI)(CX*1), CX
+ MOVQ (BP), BP
+ LEAQ -7(DI)(DX*1), DX
loop8:
MOVQ (DI), SI
- CMPQ SI,AX
+ CMPQ SI,BP
JZ success
ADDQ $1,DI
- CMPQ DI,CX
+ CMPQ DI,DX
JB loop8
JMP fail
_9_or_more:
- CMPQ BX, $16
+ CMPQ AX, $16
JA _16_or_more
- LEAQ 1(DI)(CX*1), CX
- SUBQ BX, CX
- MOVQ -8(AX)(BX*1), DX
- MOVQ (AX), AX
+ LEAQ 1(DI)(DX*1), DX
+ SUBQ AX, DX
+ MOVQ -8(BP)(AX*1), BX
+ MOVQ (BP), BP
loop9to15:
MOVQ (DI), SI
- CMPQ SI,AX
+ CMPQ SI,BP
JZ partial_success9to15
ADDQ $1,DI
- CMPQ DI,CX
+ CMPQ DI,DX
JB loop9to15
JMP fail
partial_success9to15:
- MOVQ -8(BX)(DI*1), SI
- CMPQ SI,DX
+ MOVQ -8(AX)(DI*1), SI
+ CMPQ SI,BX
JZ success
ADDQ $1,DI
- CMPQ DI,CX
+ CMPQ DI,DX
JB loop9to15
JMP fail
_16_or_more:
- CMPQ BX, $16
+ CMPQ AX, $17
JA _17_to_31
- MOVOU (AX), X1
- LEAQ -15(DI)(CX*1), CX
+ MOVOU (BP), X1
+ LEAQ -15(DI)(DX*1), DX
loop16:
MOVOU (DI), X2
PCMPEQB X1, X2
@@ -1789,14 +1793,14 @@ loop16:
CMPQ SI, $0xffff
JE success
ADDQ $1,DI
- CMPQ DI,CX
+ CMPQ DI,DX
JB loop16
JMP fail
_17_to_31:
- LEAQ 1(DI)(CX*1), CX
- SUBQ BX, CX
- MOVOU -16(AX)(BX*1), X0
- MOVOU (AX), X1
+ LEAQ 1(DI)(DX*1), DX
+ SUBQ AX, DX
+ MOVOU -16(BP)(AX*1), X0
+ MOVOU (BP), X1
loop17to31:
MOVOU (DI), X2
PCMPEQB X1,X2
@@ -1804,21 +1808,58 @@ loop17to31:
CMPQ SI, $0xffff
JE partial_success17to31
ADDQ $1,DI
- CMPQ DI,CX
+ CMPQ DI,DX
JB loop17to31
JMP fail
partial_success17to31:
- MOVOU -16(BX)(DI*1), X3
+ MOVOU -16(AX)(DI*1), X3
PCMPEQB X0, X3
PMOVMSKB X3, SI
CMPQ SI, $0xffff
JE success
ADDQ $1,DI
- CMPQ DI,CX
+ CMPQ DI,DX
JB loop17to31
fail:
MOVQ $-1, ret+32(FP)
RET
+sse42:
+ MOVL runtime·cpuid_ecx(SB), CX
+ ANDL $0x100000, CX
+ JZ no_sse42
+ CMPQ AX, $12
+ // PCMPESTRI is slower than normal compare,
+ // so using it makes sense only if we advance 4+ bytes per compare
+ // This value was determined experimentally and is the ~same
+ // on Nehalem (first with SSE42) and Haswell.
+ JAE _9_or_more
+ LEAQ 16(BP), SI
+ TESTW $0xff0, SI
+ JEQ no_sse42
+ MOVOU (BP), X1
+ LEAQ -15(DI)(DX*1), SI
+ MOVQ $16, R9
+ SUBQ AX, R9 // We advance by 16-len(sep) each iteration, so precalculate it into R9
+loop_sse42:
+ // 0x0c means: unsigned byte compare (bits 0,1 are 00)
+ // for equality (bits 2,3 are 11)
+ // result is not masked or inverted (bits 4,5 are 00)
+ // and corresponds to first matching byte (bit 6 is 0)
+ PCMPESTRI $0x0c, (DI), X1
+ // CX == 16 means no match,
+ // CX > R9 means partial match at the end of the string,
+ // otherwise sep is at offset CX from X1 start
+ CMPQ CX, R9
+ JBE sse42_success
+ ADDQ R9, DI
+ CMPQ DI, SI
+ JB loop_sse42
+ PCMPESTRI $0x0c, -1(SI), X1
+ CMPQ CX, R9
+ JA fail
+ LEAQ -1(SI), DI
+sse42_success:
+ ADDQ CX, DI
success:
SUBQ s+0(FP), DI
MOVQ DI, ret+32(FP)
diff --git a/src/runtime/asm_s390x.s b/src/runtime/asm_s390x.s
new file mode 100644
index 0000000000..fc74b0ddf9
--- /dev/null
+++ b/src/runtime/asm_s390x.s
@@ -0,0 +1,1130 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "go_asm.h"
+#include "go_tls.h"
+#include "funcdata.h"
+#include "textflag.h"
+
+// Indicate the status of vector facility
+// -1: init value
+// 0: vector not installed
+// 1: vector installed and enabled
+// 2: vector installed but not enabled
+
+DATA runtime·vectorfacility+0x00(SB)/4, $-1
+GLOBL runtime·vectorfacility(SB), NOPTR, $4
+
+TEXT runtime·checkvectorfacility(SB),NOSPLIT,$32-0
+ MOVD $2, R0
+ MOVD R1, tmp-32(SP)
+ MOVD $x-24(SP), R1
+// STFLE 0(R1)
+ WORD $0xB2B01000
+ MOVBZ z-8(SP), R1
+ AND $0x40, R1
+ BNE vectorinstalled
+ MOVB $0, runtime·vectorfacility(SB) //Vector not installed
+ MOVD tmp-32(SP), R1
+ MOVD $0, R0
+ RET
+vectorinstalled:
+ // check if the vector instruction has been enabled
+ VLEIB $0, $0xF, V16
+ VLGVB $0, V16, R0
+ CMPBEQ R0, $0xF, vectorenabled
+ MOVB $2, runtime·vectorfacility(SB) //Vector installed but not enabled
+ MOVD tmp-32(SP), R1
+ MOVD $0, R0
+ RET
+vectorenabled:
+ MOVB $1, runtime·vectorfacility(SB) //Vector installed and enabled
+ MOVD tmp-32(SP), R1
+ MOVD $0, R0
+ RET
+
+TEXT runtime·rt0_go(SB),NOSPLIT,$0
+ // R2 = argc; R3 = argv; R11 = temp; R13 = g; R15 = stack pointer
+ // C TLS base pointer in AR0:AR1
+
+ // initialize essential registers
+ XOR R0, R0
+
+ SUB $24, R15
+ MOVW R2, 8(R15) // argc
+ MOVD R3, 16(R15) // argv
+
+ // create istack out of the given (operating system) stack.
+ // _cgo_init may update stackguard.
+ MOVD $runtime·g0(SB), g
+ MOVD R15, R11
+ SUB $(64*1024), R11
+ MOVD R11, g_stackguard0(g)
+ MOVD R11, g_stackguard1(g)
+ MOVD R11, (g_stack+stack_lo)(g)
+ MOVD R15, (g_stack+stack_hi)(g)
+
+ // if there is a _cgo_init, call it using the gcc ABI.
+ MOVD _cgo_init(SB), R11
+ CMPBEQ R11, $0, nocgo
+ MOVW AR0, R4 // (AR0 << 32 | AR1) is the TLS base pointer; MOVD is translated to EAR
+ SLD $32, R4, R4
+ MOVW AR1, R4 // arg 2: TLS base pointer
+ MOVD $setg_gcc<>(SB), R3 // arg 1: setg
+ MOVD g, R2 // arg 0: G
+ // C functions expect 160 bytes of space on caller stack frame
+ // and an 8-byte aligned stack pointer
+ MOVD R15, R9 // save current stack (R9 is preserved in the Linux ABI)
+ SUB $160, R15 // reserve 160 bytes
+ MOVD $~7, R6
+ AND R6, R15 // 8-byte align
+ BL R11 // this call clobbers volatile registers according to Linux ABI (R0-R5, R14)
+ MOVD R9, R15 // restore stack
+ XOR R0, R0 // zero R0
+
+nocgo:
+ // update stackguard after _cgo_init
+ MOVD (g_stack+stack_lo)(g), R2
+ ADD $const__StackGuard, R2
+ MOVD R2, g_stackguard0(g)
+ MOVD R2, g_stackguard1(g)
+
+ // set the per-goroutine and per-mach "registers"
+ MOVD $runtime·m0(SB), R2
+
+ // save m->g0 = g0
+ MOVD g, m_g0(R2)
+ // save m0 to g0->m
+ MOVD R2, g_m(g)
+
+ BL runtime·check(SB)
+
+ // argc/argv are already prepared on stack
+ BL runtime·args(SB)
+ BL runtime·osinit(SB)
+ BL runtime·schedinit(SB)
+
+ // create a new goroutine to start program
+ MOVD $runtime·mainPC(SB), R2 // entry
+ SUB $24, R15
+ MOVD R2, 16(R15)
+ MOVD R0, 8(R15)
+ MOVD R0, 0(R15)
+ BL runtime·newproc(SB)
+ ADD $24, R15
+
+ // start this M
+ BL runtime·mstart(SB)
+
+ MOVD R0, 1(R0)
+ RET
+
+DATA runtime·mainPC+0(SB)/8,$runtime·main(SB)
+GLOBL runtime·mainPC(SB),RODATA,$8
+
+TEXT runtime·breakpoint(SB),NOSPLIT|NOFRAME,$0-0
+ MOVD R0, 2(R0)
+ RET
+
+TEXT runtime·asminit(SB),NOSPLIT|NOFRAME,$0-0
+ RET
+
+/*
+ * go-routine
+ */
+
+// void gosave(Gobuf*)
+// save state in Gobuf; setjmp
+TEXT runtime·gosave(SB), NOSPLIT, $-8-8
+ MOVD buf+0(FP), R3
+ MOVD R15, gobuf_sp(R3)
+ MOVD LR, gobuf_pc(R3)
+ MOVD g, gobuf_g(R3)
+ MOVD $0, gobuf_lr(R3)
+ MOVD $0, gobuf_ret(R3)
+ MOVD $0, gobuf_ctxt(R3)
+ RET
+
+// void gogo(Gobuf*)
+// restore state from Gobuf; longjmp
+TEXT runtime·gogo(SB), NOSPLIT, $-8-8
+ MOVD buf+0(FP), R5
+ MOVD gobuf_g(R5), g // make sure g is not nil
+ BL runtime·save_g(SB)
+
+ MOVD 0(g), R4
+ MOVD gobuf_sp(R5), R15
+ MOVD gobuf_lr(R5), LR
+ MOVD gobuf_ret(R5), R3
+ MOVD gobuf_ctxt(R5), R12
+ MOVD $0, gobuf_sp(R5)
+ MOVD $0, gobuf_ret(R5)
+ MOVD $0, gobuf_lr(R5)
+ MOVD $0, gobuf_ctxt(R5)
+ CMP R0, R0 // set condition codes for == test, needed by stack split
+ MOVD gobuf_pc(R5), R6
+ BR (R6)
+
+// void mcall(fn func(*g))
+// Switch to m->g0's stack, call fn(g).
+// Fn must never return. It should gogo(&g->sched)
+// to keep running g.
+TEXT runtime·mcall(SB), NOSPLIT, $-8-8
+ // Save caller state in g->sched
+ MOVD R15, (g_sched+gobuf_sp)(g)
+ MOVD LR, (g_sched+gobuf_pc)(g)
+ MOVD R0, (g_sched+gobuf_lr)(g)
+ MOVD g, (g_sched+gobuf_g)(g)
+
+ // Switch to m->g0 & its stack, call fn.
+ MOVD g, R3
+ MOVD g_m(g), R8
+ MOVD m_g0(R8), g
+ BL runtime·save_g(SB)
+ CMP g, R3
+ BNE 2(PC)
+ BR runtime·badmcall(SB)
+ MOVD fn+0(FP), R12 // context
+ MOVD 0(R12), R4 // code pointer
+ MOVD (g_sched+gobuf_sp)(g), R15 // sp = m->g0->sched.sp
+ SUB $16, R15
+ MOVD R3, 8(R15)
+ MOVD $0, 0(R15)
+ BL (R4)
+ BR runtime·badmcall2(SB)
+
+// systemstack_switch is a dummy routine that systemstack leaves at the bottom
+// of the G stack. We need to distinguish the routine that
+// lives at the bottom of the G stack from the one that lives
+// at the top of the system stack because the one at the top of
+// the system stack terminates the stack walk (see topofstack()).
+TEXT runtime·systemstack_switch(SB), NOSPLIT, $0-0
+ UNDEF
+ BL (LR) // make sure this function is not leaf
+ RET
+
+// func systemstack(fn func())
+TEXT runtime·systemstack(SB), NOSPLIT, $0-8
+ MOVD fn+0(FP), R3 // R3 = fn
+ MOVD R3, R12 // context
+ MOVD g_m(g), R4 // R4 = m
+
+ MOVD m_gsignal(R4), R5 // R5 = gsignal
+ CMPBEQ g, R5, noswitch
+
+ MOVD m_g0(R4), R5 // R5 = g0
+ CMPBEQ g, R5, noswitch
+
+ MOVD m_curg(R4), R6
+ CMPBEQ g, R6, switch
+
+ // Bad: g is not gsignal, not g0, not curg. What is it?
+ // Hide call from linker nosplit analysis.
+ MOVD $runtime·badsystemstack(SB), R3
+ BL (R3)
+
+switch:
+ // save our state in g->sched. Pretend to
+ // be systemstack_switch if the G stack is scanned.
+ MOVD $runtime·systemstack_switch(SB), R6
+ ADD $16, R6 // get past prologue
+ MOVD R6, (g_sched+gobuf_pc)(g)
+ MOVD R15, (g_sched+gobuf_sp)(g)
+ MOVD R0, (g_sched+gobuf_lr)(g)
+ MOVD g, (g_sched+gobuf_g)(g)
+
+ // switch to g0
+ MOVD R5, g
+ BL runtime·save_g(SB)
+ MOVD (g_sched+gobuf_sp)(g), R3
+ // make it look like mstart called systemstack on g0, to stop traceback
+ SUB $8, R3
+ MOVD $runtime·mstart(SB), R4
+ MOVD R4, 0(R3)
+ MOVD R3, R15
+
+ // call target function
+ MOVD 0(R12), R3 // code pointer
+ BL (R3)
+
+ // switch back to g
+ MOVD g_m(g), R3
+ MOVD m_curg(R3), g
+ BL runtime·save_g(SB)
+ MOVD (g_sched+gobuf_sp)(g), R15
+ MOVD $0, (g_sched+gobuf_sp)(g)
+ RET
+
+noswitch:
+ // already on m stack, just call directly
+ MOVD 0(R12), R3 // code pointer
+ BL (R3)
+ RET
+
+/*
+ * support for morestack
+ */
+
+// Called during function prolog when more stack is needed.
+// Caller has already loaded:
+// R3: framesize, R4: argsize, R5: LR
+//
+// The traceback routines see morestack on a g0 as being
+// the top of a stack (for example, morestack calling newstack
+// calling the scheduler calling newm calling gc), so we must
+// record an argument size. For that purpose, it has no arguments.
+TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0
+ // Cannot grow scheduler stack (m->g0).
+ MOVD g_m(g), R7
+ MOVD m_g0(R7), R8
+ CMPBNE g, R8, 2(PC)
+ BL runtime·abort(SB)
+
+ // Cannot grow signal stack (m->gsignal).
+ MOVD m_gsignal(R7), R8
+ CMP g, R8
+ BNE 2(PC)
+ BL runtime·abort(SB)
+
+ // Called from f.
+ // Set g->sched to context in f.
+ MOVD R12, (g_sched+gobuf_ctxt)(g)
+ MOVD R15, (g_sched+gobuf_sp)(g)
+ MOVD LR, R8
+ MOVD R8, (g_sched+gobuf_pc)(g)
+ MOVD R5, (g_sched+gobuf_lr)(g)
+
+ // Called from f.
+ // Set m->morebuf to f's caller.
+ MOVD R5, (m_morebuf+gobuf_pc)(R7) // f's caller's PC
+ MOVD R15, (m_morebuf+gobuf_sp)(R7) // f's caller's SP
+ MOVD g, (m_morebuf+gobuf_g)(R7)
+
+ // Call newstack on m->g0's stack.
+ MOVD m_g0(R7), g
+ BL runtime·save_g(SB)
+ MOVD (g_sched+gobuf_sp)(g), R15
+ BL runtime·newstack(SB)
+
+ // Not reached, but make sure the return PC from the call to newstack
+ // is still in this function, and not the beginning of the next.
+ UNDEF
+
+TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
+ MOVD $0, R12
+ BR runtime·morestack(SB)
+
+TEXT runtime·stackBarrier(SB),NOSPLIT,$0
+ // We came here via a RET to an overwritten LR.
+ // R3 may be live. Other registers are available.
+
+ // Get the original return PC, g.stkbar[g.stkbarPos].savedLRVal.
+ MOVD (g_stkbar+slice_array)(g), R4
+ MOVD g_stkbarPos(g), R5
+ MOVD $stkbar__size, R6
+ MULLD R5, R6
+ ADD R4, R6
+ MOVD stkbar_savedLRVal(R6), R6
+ // Record that this stack barrier was hit.
+ ADD $1, R5
+ MOVD R5, g_stkbarPos(g)
+ // Jump to the original return PC.
+ BR (R6)
+
+// reflectcall: call a function with the given argument list
+// func call(argtype *_type, f *FuncVal, arg *byte, argsize, retoffset uint32).
+// we don't have variable-sized frames, so we use a small number
+// of constant-sized-frame functions to encode a few bits of size in the pc.
+// Caution: ugly multiline assembly macros in your future!
+
+#define DISPATCH(NAME,MAXSIZE) \
+ MOVD $MAXSIZE, R4; \
+ CMP R3, R4; \
+ BGT 3(PC); \
+ MOVD $NAME(SB), R5; \
+ BR (R5)
+// Note: can't just "BR NAME(SB)" - bad inlining results.
+
+TEXT reflect·call(SB), NOSPLIT, $0-0
+ BR ·reflectcall(SB)
+
+TEXT ·reflectcall(SB), NOSPLIT, $-8-32
+ MOVWZ argsize+24(FP), R3
+ // NOTE(rsc): No call16, because CALLFN needs four words
+ // of argument space to invoke callwritebarrier.
+ DISPATCH(runtime·call32, 32)
+ DISPATCH(runtime·call64, 64)
+ DISPATCH(runtime·call128, 128)
+ DISPATCH(runtime·call256, 256)
+ DISPATCH(runtime·call512, 512)
+ DISPATCH(runtime·call1024, 1024)
+ DISPATCH(runtime·call2048, 2048)
+ DISPATCH(runtime·call4096, 4096)
+ DISPATCH(runtime·call8192, 8192)
+ DISPATCH(runtime·call16384, 16384)
+ DISPATCH(runtime·call32768, 32768)
+ DISPATCH(runtime·call65536, 65536)
+ DISPATCH(runtime·call131072, 131072)
+ DISPATCH(runtime·call262144, 262144)
+ DISPATCH(runtime·call524288, 524288)
+ DISPATCH(runtime·call1048576, 1048576)
+ DISPATCH(runtime·call2097152, 2097152)
+ DISPATCH(runtime·call4194304, 4194304)
+ DISPATCH(runtime·call8388608, 8388608)
+ DISPATCH(runtime·call16777216, 16777216)
+ DISPATCH(runtime·call33554432, 33554432)
+ DISPATCH(runtime·call67108864, 67108864)
+ DISPATCH(runtime·call134217728, 134217728)
+ DISPATCH(runtime·call268435456, 268435456)
+ DISPATCH(runtime·call536870912, 536870912)
+ DISPATCH(runtime·call1073741824, 1073741824)
+ MOVD $runtime·badreflectcall(SB), R5
+ BR (R5)
+
+#define CALLFN(NAME,MAXSIZE) \
+TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \
+ NO_LOCAL_POINTERS; \
+ /* copy arguments to stack */ \
+ MOVD arg+16(FP), R3; \
+ MOVWZ argsize+24(FP), R4; \
+ MOVD R15, R5; \
+ ADD $(8-1), R5; \
+ SUB $1, R3; \
+ ADD R5, R4; \
+ CMP R5, R4; \
+ BEQ 6(PC); \
+ ADD $1, R3; \
+ ADD $1, R5; \
+ MOVBZ 0(R3), R6; \
+ MOVBZ R6, 0(R5); \
+ BR -6(PC); \
+ /* call function */ \
+ MOVD f+8(FP), R12; \
+ MOVD (R12), R8; \
+ PCDATA $PCDATA_StackMapIndex, $0; \
+ BL (R8); \
+ /* copy return values back */ \
+ MOVD arg+16(FP), R3; \
+ MOVWZ n+24(FP), R4; \
+ MOVWZ retoffset+28(FP), R6; \
+ MOVD R15, R5; \
+ ADD R6, R5; \
+ ADD R6, R3; \
+ SUB R6, R4; \
+ ADD $(8-1), R5; \
+ SUB $1, R3; \
+ ADD R5, R4; \
+loop: \
+ CMP R5, R4; \
+ BEQ end; \
+ ADD $1, R5; \
+ ADD $1, R3; \
+ MOVBZ 0(R5), R6; \
+ MOVBZ R6, 0(R3); \
+ BR loop; \
+end: \
+ /* execute write barrier updates */ \
+ MOVD argtype+0(FP), R7; \
+ MOVD arg+16(FP), R3; \
+ MOVWZ n+24(FP), R4; \
+ MOVWZ retoffset+28(FP), R6; \
+ MOVD R7, 8(R15); \
+ MOVD R3, 16(R15); \
+ MOVD R4, 24(R15); \
+ MOVD R6, 32(R15); \
+ BL runtime·callwritebarrier(SB); \
+ RET
+
+CALLFN(·call32, 32)
+CALLFN(·call64, 64)
+CALLFN(·call128, 128)
+CALLFN(·call256, 256)
+CALLFN(·call512, 512)
+CALLFN(·call1024, 1024)
+CALLFN(·call2048, 2048)
+CALLFN(·call4096, 4096)
+CALLFN(·call8192, 8192)
+CALLFN(·call16384, 16384)
+CALLFN(·call32768, 32768)
+CALLFN(·call65536, 65536)
+CALLFN(·call131072, 131072)
+CALLFN(·call262144, 262144)
+CALLFN(·call524288, 524288)
+CALLFN(·call1048576, 1048576)
+CALLFN(·call2097152, 2097152)
+CALLFN(·call4194304, 4194304)
+CALLFN(·call8388608, 8388608)
+CALLFN(·call16777216, 16777216)
+CALLFN(·call33554432, 33554432)
+CALLFN(·call67108864, 67108864)
+CALLFN(·call134217728, 134217728)
+CALLFN(·call268435456, 268435456)
+CALLFN(·call536870912, 536870912)
+CALLFN(·call1073741824, 1073741824)
+
+TEXT runtime·procyield(SB),NOSPLIT,$0-0
+ RET
+
+// void jmpdefer(fv, sp);
+// called from deferreturn.
+// 1. grab stored LR for caller
+// 2. sub 6 bytes to get back to BL deferreturn (size of BRASL instruction)
+// 3. BR to fn
+TEXT runtime·jmpdefer(SB),NOSPLIT|NOFRAME,$0-16
+ MOVD 0(R15), R1
+ SUB $6, R1, LR
+
+ MOVD fv+0(FP), R12
+ MOVD argp+8(FP), R15
+ SUB $8, R15
+ MOVD 0(R12), R3
+ BR (R3)
+
+// Save state of caller into g->sched. Smashes R31.
+TEXT gosave<>(SB),NOSPLIT|NOFRAME,$0
+ MOVD LR, (g_sched+gobuf_pc)(g)
+ MOVD R15, (g_sched+gobuf_sp)(g)
+ MOVD $0, (g_sched+gobuf_lr)(g)
+ MOVD $0, (g_sched+gobuf_ret)(g)
+ MOVD $0, (g_sched+gobuf_ctxt)(g)
+ RET
+
+// func asmcgocall(fn, arg unsafe.Pointer) int32
+// Call fn(arg) on the scheduler stack,
+// aligned appropriately for the gcc ABI.
+// See cgocall.go for more details.
+TEXT ·asmcgocall(SB),NOSPLIT,$0-20
+ // R2 = argc; R3 = argv; R11 = temp; R13 = g; R15 = stack pointer
+ // C TLS base pointer in AR0:AR1
+ MOVD fn+0(FP), R3
+ MOVD arg+8(FP), R4
+
+ MOVD R15, R2 // save original stack pointer
+ MOVD g, R5
+
+ // Figure out if we need to switch to m->g0 stack.
+ // We get called to create new OS threads too, and those
+ // come in on the m->g0 stack already.
+ MOVD g_m(g), R6
+ MOVD m_g0(R6), R6
+ CMPBEQ R6, g, g0
+ BL gosave<>(SB)
+ MOVD R6, g
+ BL runtime·save_g(SB)
+ MOVD (g_sched+gobuf_sp)(g), R15
+
+ // Now on a scheduling stack (a pthread-created stack).
+g0:
+ // Save room for two of our pointers, plus 160 bytes of callee
+ // save area that lives on the caller stack.
+ SUB $176, R15
+ MOVD $~7, R6
+ AND R6, R15 // 8-byte alignment for gcc ABI
+ MOVD R5, 168(R15) // save old g on stack
+ MOVD (g_stack+stack_hi)(R5), R5
+ SUB R2, R5
+ MOVD R5, 160(R15) // save depth in old g stack (can't just save SP, as stack might be copied during a callback)
+ MOVD R0, 0(R15) // clear back chain pointer (TODO can we give it real back trace information?)
+ MOVD R4, R2 // arg in R2
+ BL R3 // can clobber: R0-R5, R14, F0-F3, F5, F7-F15
+
+ XOR R0, R0 // set R0 back to 0.
+ // Restore g, stack pointer.
+ MOVD 168(R15), g
+ BL runtime·save_g(SB)
+ MOVD (g_stack+stack_hi)(g), R5
+ MOVD 160(R15), R6
+ SUB R6, R5
+ MOVD R5, R15
+
+ MOVW R2, ret+16(FP)
+ RET
+
+// cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
+// Turn the fn into a Go func (by taking its address) and call
+// cgocallback_gofunc.
+TEXT runtime·cgocallback(SB),NOSPLIT,$24-24
+ MOVD $fn+0(FP), R3
+ MOVD R3, 8(R15)
+ MOVD frame+8(FP), R3
+ MOVD R3, 16(R15)
+ MOVD framesize+16(FP), R3
+ MOVD R3, 24(R15)
+ MOVD $runtime·cgocallback_gofunc(SB), R3
+ BL (R3)
+ RET
+
+// cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize)
+// See cgocall.go for more details.
+TEXT ·cgocallback_gofunc(SB),NOSPLIT,$16-24
+ NO_LOCAL_POINTERS
+
+ // Load m and g from thread-local storage.
+ MOVB runtime·iscgo(SB), R3
+ CMPBEQ R3, $0, nocgo
+ BL runtime·load_g(SB)
+
+nocgo:
+ // If g is nil, Go did not create the current thread.
+ // Call needm to obtain one for temporary use.
+ // In this case, we're running on the thread stack, so there's
+ // lots of space, but the linker doesn't know. Hide the call from
+ // the linker analysis by using an indirect call.
+ CMPBEQ g, $0, needm
+
+ MOVD g_m(g), R8
+ MOVD R8, savedm-8(SP)
+ BR havem
+
+needm:
+ MOVD g, savedm-8(SP) // g is zero, so is m.
+ MOVD $runtime·needm(SB), R3
+ BL (R3)
+
+ // Set m->sched.sp = SP, so that if a panic happens
+ // during the function we are about to execute, it will
+ // have a valid SP to run on the g0 stack.
+ // The next few lines (after the havem label)
+ // will save this SP onto the stack and then write
+ // the same SP back to m->sched.sp. That seems redundant,
+ // but if an unrecovered panic happens, unwindm will
+ // restore the g->sched.sp from the stack location
+ // and then systemstack will try to use it. If we don't set it here,
+ // that restored SP will be uninitialized (typically 0) and
+ // will not be usable.
+ MOVD g_m(g), R8
+ MOVD m_g0(R8), R3
+ MOVD R15, (g_sched+gobuf_sp)(R3)
+
+havem:
+ // Now there's a valid m, and we're running on its m->g0.
+ // Save current m->g0->sched.sp on stack and then set it to SP.
+ // Save current sp in m->g0->sched.sp in preparation for
+ // switch back to m->curg stack.
+ // NOTE: unwindm knows that the saved g->sched.sp is at 8(R1) aka savedsp-16(SP).
+ MOVD m_g0(R8), R3
+ MOVD (g_sched+gobuf_sp)(R3), R4
+ MOVD R4, savedsp-16(SP)
+ MOVD R15, (g_sched+gobuf_sp)(R3)
+
+ // Switch to m->curg stack and call runtime.cgocallbackg.
+ // Because we are taking over the execution of m->curg
+ // but *not* resuming what had been running, we need to
+ // save that information (m->curg->sched) so we can restore it.
+ // We can restore m->curg->sched.sp easily, because calling
+ // runtime.cgocallbackg leaves SP unchanged upon return.
+ // To save m->curg->sched.pc, we push it onto the stack.
+ // This has the added benefit that it looks to the traceback
+ // routine like cgocallbackg is going to return to that
+ // PC (because the frame we allocate below has the same
+ // size as cgocallback_gofunc's frame declared above)
+ // so that the traceback will seamlessly trace back into
+ // the earlier calls.
+ //
+ // In the new goroutine, -16(SP) and -8(SP) are unused.
+ MOVD m_curg(R8), g
+ BL runtime·save_g(SB)
+ MOVD (g_sched+gobuf_sp)(g), R4 // prepare stack as R4
+ MOVD (g_sched+gobuf_pc)(g), R5
+ MOVD R5, -24(R4)
+ MOVD $-24(R4), R15
+ BL runtime·cgocallbackg(SB)
+
+ // Restore g->sched (== m->curg->sched) from saved values.
+ MOVD 0(R15), R5
+ MOVD R5, (g_sched+gobuf_pc)(g)
+ MOVD $24(R15), R4
+ MOVD R4, (g_sched+gobuf_sp)(g)
+
+ // Switch back to m->g0's stack and restore m->g0->sched.sp.
+ // (Unlike m->curg, the g0 goroutine never uses sched.pc,
+ // so we do not have to restore it.)
+ MOVD g_m(g), R8
+ MOVD m_g0(R8), g
+ BL runtime·save_g(SB)
+ MOVD (g_sched+gobuf_sp)(g), R15
+ MOVD savedsp-16(SP), R4
+ MOVD R4, (g_sched+gobuf_sp)(g)
+
+ // If the m on entry was nil, we called needm above to borrow an m
+ // for the duration of the call. Since the call is over, return it with dropm.
+ MOVD savedm-8(SP), R6
+ CMPBNE R6, $0, droppedm
+ MOVD $runtime·dropm(SB), R3
+ BL (R3)
+droppedm:
+
+ // Done!
+ RET
+
+// void setg(G*); set g. for use by needm.
+TEXT runtime·setg(SB), NOSPLIT, $0-8
+ MOVD gg+0(FP), g
+ // This only happens if iscgo, so jump straight to save_g
+ BL runtime·save_g(SB)
+ RET
+
+// void setg_gcc(G*); set g in C TLS.
+// Must obey the gcc calling convention.
+TEXT setg_gcc<>(SB),NOSPLIT|NOFRAME,$0-0
+ // The standard prologue clobbers LR (R14), which is callee-save in
+ // the C ABI, so we have to use NOFRAME and save LR ourselves.
+ MOVD LR, R1
+ // Also save g, R10, and R11 since they're callee-save in C ABI
+ MOVD R10, R3
+ MOVD g, R4
+ MOVD R11, R5
+
+ MOVD R2, g
+ BL runtime·save_g(SB)
+
+ MOVD R5, R11
+ MOVD R4, g
+ MOVD R3, R10
+ MOVD R1, LR
+ RET
+
+TEXT runtime·getcallerpc(SB),NOSPLIT,$8-16
+ MOVD 16(R15), R3 // LR saved by caller
+ MOVD runtime·stackBarrierPC(SB), R4
+ CMPBNE R3, R4, nobar
+ // Get original return PC.
+ BL runtime·nextBarrierPC(SB)
+ MOVD 8(R15), R3
+nobar:
+ MOVD R3, ret+8(FP)
+ RET
+
+TEXT runtime·setcallerpc(SB),NOSPLIT,$8-16
+ MOVD pc+8(FP), R3
+ MOVD 16(R15), R4
+ MOVD runtime·stackBarrierPC(SB), R5
+ CMPBEQ R4, R5, setbar
+ MOVD R3, 16(R15) // set LR in caller
+ RET
+setbar:
+ // Set the stack barrier return PC.
+ MOVD R3, 8(R15)
+ BL runtime·setNextBarrierPC(SB)
+ RET
+
+TEXT runtime·getcallersp(SB),NOSPLIT,$0-16
+ MOVD argp+0(FP), R3
+ SUB $8, R3
+ MOVD R3, ret+8(FP)
+ RET
+
+TEXT runtime·abort(SB),NOSPLIT|NOFRAME,$0-0
+ MOVW (R0), R0
+ UNDEF
+
+// int64 runtime·cputicks(void)
+TEXT runtime·cputicks(SB),NOSPLIT,$0-8
+ // The TOD clock on s390 counts from the year 1900 in ~250ps intervals.
+ // This means that since about 1972 the msb has been set, making the
+ // result of a call to STORE CLOCK (stck) a negative number.
+ // We clear the msb to make it positive.
+ STCK ret+0(FP) // serialises before and after call
+ MOVD ret+0(FP), R3 // R3 will wrap to 0 in the year 2043
+ SLD $1, R3
+ SRD $1, R3
+ MOVD R3, ret+0(FP)
+ RET
+
+// memhash_varlen(p unsafe.Pointer, h seed) uintptr
+// redirects to memhash(p, h, size) using the size
+// stored in the closure.
+TEXT runtime·memhash_varlen(SB),NOSPLIT,$40-24
+ GO_ARGS
+ NO_LOCAL_POINTERS
+ MOVD p+0(FP), R3
+ MOVD h+8(FP), R4
+ MOVD 8(R12), R5
+ MOVD R3, 8(R15)
+ MOVD R4, 16(R15)
+ MOVD R5, 24(R15)
+ BL runtime·memhash(SB)
+ MOVD 32(R15), R3
+ MOVD R3, ret+16(FP)
+ RET
+
+// AES hashing not implemented for s390x
+TEXT runtime·aeshash(SB),NOSPLIT|NOFRAME,$0-0
+ MOVW (R0), R15
+TEXT runtime·aeshash32(SB),NOSPLIT|NOFRAME,$0-0
+ MOVW (R0), R15
+TEXT runtime·aeshash64(SB),NOSPLIT|NOFRAME,$0-0
+ MOVW (R0), R15
+TEXT runtime·aeshashstr(SB),NOSPLIT|NOFRAME,$0-0
+ MOVW (R0), R15
+
+// memequal(p, q unsafe.Pointer, size uintptr) bool
+TEXT runtime·memequal(SB),NOSPLIT|NOFRAME,$0-25
+ MOVD p+0(FP), R3
+ MOVD q+8(FP), R5
+ MOVD size+16(FP), R6
+ LA ret+24(FP), R7
+ BR runtime·memeqbody(SB)
+
+// memequal_varlen(a, b unsafe.Pointer) bool
+TEXT runtime·memequal_varlen(SB),NOSPLIT|NOFRAME,$0-17
+ MOVD a+0(FP), R3
+ MOVD b+8(FP), R5
+ MOVD 8(R12), R6 // compiler stores size at offset 8 in the closure
+ LA ret+16(FP), R7
+ BR runtime·memeqbody(SB)
+
+// eqstring tests whether two strings are equal.
+// The compiler guarantees that strings passed
+// to eqstring have equal length.
+// See runtime_test.go:eqstring_generic for
+// equivalent Go code.
+TEXT runtime·eqstring(SB),NOSPLIT|NOFRAME,$0-33
+ MOVD s1str+0(FP), R3
+ MOVD s1len+8(FP), R6
+ MOVD s2str+16(FP), R5
+ LA ret+32(FP), R7
+ BR runtime·memeqbody(SB)
+
+TEXT bytes·Equal(SB),NOSPLIT|NOFRAME,$0-49
+ MOVD a_len+8(FP), R2
+ MOVD b_len+32(FP), R6
+ MOVD a+0(FP), R3
+ MOVD b+24(FP), R5
+ LA ret+48(FP), R7
+ CMPBNE R2, R6, notequal
+ BR runtime·memeqbody(SB)
+notequal:
+ MOVB $0, ret+48(FP)
+ RET
+
+// input:
+// R3 = a
+// R5 = b
+// R6 = len
+// R7 = address of output byte (stores 0 or 1 here)
+// a and b have the same length
+TEXT runtime·memeqbody(SB),NOSPLIT|NOFRAME,$0-0
+ CMPBEQ R3, R5, equal
+loop:
+ CMPBEQ R6, $0, equal
+ CMPBLT R6, $32, tiny
+ CMP R6, $256
+ BLT tail
+ CLC $256, 0(R3), 0(R5)
+ BNE notequal
+ SUB $256, R6
+ LA 256(R3), R3
+ LA 256(R5), R5
+ BR loop
+tail:
+ SUB $1, R6, R8
+ EXRL $runtime·memeqbodyclc(SB), R8
+ BEQ equal
+notequal:
+ MOVB $0, 0(R7)
+ RET
+equal:
+ MOVB $1, 0(R7)
+ RET
+tiny:
+ MOVD $0, R2
+ CMPBLT R6, $16, lt16
+ MOVD 0(R3), R8
+ MOVD 0(R5), R9
+ CMPBNE R8, R9, notequal
+ MOVD 8(R3), R8
+ MOVD 8(R5), R9
+ CMPBNE R8, R9, notequal
+ LA 16(R2), R2
+ SUB $16, R6
+lt16:
+ CMPBLT R6, $8, lt8
+ MOVD 0(R3)(R2*1), R8
+ MOVD 0(R5)(R2*1), R9
+ CMPBNE R8, R9, notequal
+ LA 8(R2), R2
+ SUB $8, R6
+lt8:
+ CMPBLT R6, $4, lt4
+ MOVWZ 0(R3)(R2*1), R8
+ MOVWZ 0(R5)(R2*1), R9
+ CMPBNE R8, R9, notequal
+ LA 4(R2), R2
+ SUB $4, R6
+lt4:
+#define CHECK(n) \
+ CMPBEQ R6, $n, equal \
+ MOVB n(R3)(R2*1), R8 \
+ MOVB n(R5)(R2*1), R9 \
+ CMPBNE R8, R9, notequal
+ CHECK(0)
+ CHECK(1)
+ CHECK(2)
+ CHECK(3)
+ BR equal
+
+TEXT runtime·memeqbodyclc(SB),NOSPLIT|NOFRAME,$0-0
+ CLC $1, 0(R3), 0(R5)
+ RET
+
+TEXT runtime·fastrand1(SB), NOSPLIT, $0-4
+ MOVD g_m(g), R4
+ MOVWZ m_fastrand(R4), R3
+ ADD R3, R3
+ CMPW R3, $0
+ BGE 2(PC)
+ XOR $0x88888eef, R3
+ MOVW R3, m_fastrand(R4)
+ MOVW R3, ret+0(FP)
+ RET
+
+TEXT bytes·IndexByte(SB),NOSPLIT,$0-40
+ MOVD s+0(FP), R3 // s => R3
+ MOVD s_len+8(FP), R4 // s_len => R4
+ MOVBZ c+24(FP), R5 // c => R5
+ MOVD $ret+32(FP), R2 // &ret => R9
+ BR runtime·indexbytebody(SB)
+
+TEXT strings·IndexByte(SB),NOSPLIT,$0-32
+ MOVD s+0(FP), R3 // s => R3
+ MOVD s_len+8(FP), R4 // s_len => R4
+ MOVBZ c+16(FP), R5 // c => R5
+ MOVD $ret+24(FP), R2 // &ret => R9
+ BR runtime·indexbytebody(SB)
+
+// input:
+// R3: s
+// R4: s_len
+// R5: c -- byte sought
+// R2: &ret -- address to put index into
+TEXT runtime·indexbytebody(SB),NOSPLIT,$0
+ CMPBEQ R4, $0, notfound
+ MOVD R3, R6 // store base for later
+ ADD R3, R4, R8 // the address after the end of the string
+ //if the length is small, use loop; otherwise, use vector or srst search
+ CMPBGE R4, $16, large
+
+residual:
+ CMPBEQ R3, R8, notfound
+ MOVBZ 0(R3), R7
+ LA 1(R3), R3
+ CMPBNE R7, R5, residual
+
+found:
+ SUB R6, R3
+ SUB $1, R3
+ MOVD R3, 0(R2)
+ RET
+
+notfound:
+ MOVD $-1, 0(R2)
+ RET
+
+large:
+ MOVB runtime·vectorfacility(SB), R1
+ CMPBEQ R1, $-1, checkvector // vectorfacility = -1, vector not checked yet
+vectorchecked:
+ CMPBEQ R1, $1, vectorimpl // vectorfacility = 1, vector supported
+
+srstimpl: // vectorfacility != 1, not support or enable vector
+ MOVBZ R5, R0 // c needs to be in R0, leave until last minute as currently R0 is expected to be 0
+srstloop:
+ WORD $0xB25E0083 // srst %r8, %r3 (search the range [R3, R8))
+ BVS srstloop // interrupted - continue
+ BGT notfoundr0
+foundr0:
+ XOR R0, R0 // reset R0
+ SUB R6, R8 // remove base
+ MOVD R8, 0(R2)
+ RET
+notfoundr0:
+ XOR R0, R0 // reset R0
+ MOVD $-1, 0(R2)
+ RET
+
+vectorimpl:
+ //if the address is not 16byte aligned, use loop for the header
+ AND $15, R3, R8
+ CMPBGT R8, $0, notaligned
+
+aligned:
+ ADD R6, R4, R8
+ AND $-16, R8, R7
+ // replicate c across V17
+ VLVGB $0, R5, V19
+ VREPB $0, V19, V17
+
+vectorloop:
+ CMPBGE R3, R7, residual
+ VL 0(R3), V16 // load string to be searched into V16
+ ADD $16, R3
+ VFEEBS V16, V17, V18 // search V17 in V16 and set conditional code accordingly
+ BVS vectorloop
+
+ // when vector search found c in the string
+ VLGVB $7, V18, R7 // load 7th element of V18 containing index into R7
+ SUB $16, R3
+ SUB R6, R3
+ ADD R3, R7
+ MOVD R7, 0(R2)
+ RET
+
+notaligned:
+ AND $-16, R3, R8
+ ADD $16, R8
+notalignedloop:
+ CMPBEQ R3, R8, aligned
+ MOVBZ 0(R3), R7
+ LA 1(R3), R3
+ CMPBNE R7, R5, notalignedloop
+ BR found
+
+checkvector:
+ CALL runtime·checkvectorfacility(SB)
+ MOVB runtime·vectorfacility(SB), R1
+ BR vectorchecked
+
+TEXT runtime·return0(SB), NOSPLIT, $0
+ MOVW $0, R3
+ RET
+
+// Called from cgo wrappers, this function returns g->m->curg.stack.hi.
+// Must obey the gcc calling convention.
+TEXT _cgo_topofstack(SB),NOSPLIT|NOFRAME,$0
+ // g (R13), R10, R11 and LR (R14) are callee-save in the C ABI, so save them
+ MOVD g, R1
+ MOVD R10, R3
+ MOVD LR, R4
+ MOVD R11, R5
+
+ BL runtime·load_g(SB) // clobbers g (R13), R10, R11
+ MOVD g_m(g), R2
+ MOVD m_curg(R2), R2
+ MOVD (g_stack+stack_hi)(R2), R2
+
+ MOVD R1, g
+ MOVD R3, R10
+ MOVD R4, LR
+ MOVD R5, R11
+ RET
+
+// The top-most function running on a goroutine
+// returns to goexit+PCQuantum.
+TEXT runtime·goexit(SB),NOSPLIT|NOFRAME,$0-0
+ BYTE $0x07; BYTE $0x00; // 2-byte nop
+ BL runtime·goexit1(SB) // does not return
+ // traceback from goexit1 must hit code range of goexit
+ BYTE $0x07; BYTE $0x00; // 2-byte nop
+
+TEXT runtime·prefetcht0(SB),NOSPLIT,$0-8
+ RET
+
+TEXT runtime·prefetcht1(SB),NOSPLIT,$0-8
+ RET
+
+TEXT runtime·prefetcht2(SB),NOSPLIT,$0-8
+ RET
+
+TEXT runtime·prefetchnta(SB),NOSPLIT,$0-8
+ RET
+
+TEXT runtime·sigreturn(SB),NOSPLIT,$0-8
+ RET
+
+TEXT ·publicationBarrier(SB),NOSPLIT|NOFRAME,$0-0
+ SYNC
+ RET
+
+TEXT runtime·cmpstring(SB),NOSPLIT|NOFRAME,$0-40
+ MOVD s1_base+0(FP), R3
+ MOVD s1_len+8(FP), R4
+ MOVD s2_base+16(FP), R5
+ MOVD s2_len+24(FP), R6
+ LA ret+32(FP), R7
+ BR runtime·cmpbody(SB)
+
+TEXT bytes·Compare(SB),NOSPLIT|NOFRAME,$0-56
+ MOVD s1+0(FP), R3
+ MOVD s1+8(FP), R4
+ MOVD s2+24(FP), R5
+ MOVD s2+32(FP), R6
+ LA res+48(FP), R7
+ BR runtime·cmpbody(SB)
+
+// input:
+// R3 = a
+// R4 = alen
+// R5 = b
+// R6 = blen
+// R7 = address of output word (stores -1/0/1 here)
+TEXT runtime·cmpbody(SB),NOSPLIT|NOFRAME,$0-0
+ CMPBEQ R3, R5, cmplengths
+ MOVD R4, R8
+ CMPBLE R4, R6, amin
+ MOVD R6, R8
+amin:
+ CMPBEQ R8, $0, cmplengths
+ CMP R8, $256
+ BLE tail
+loop:
+ CLC $256, 0(R3), 0(R5)
+ BGT gt
+ BLT lt
+ SUB $256, R8
+ CMP R8, $256
+ BGT loop
+tail:
+ SUB $1, R8
+ EXRL $runtime·cmpbodyclc(SB), R8
+ BGT gt
+ BLT lt
+cmplengths:
+ CMP R4, R6
+ BEQ eq
+ BLT lt
+gt:
+ MOVD $1, 0(R7)
+ RET
+lt:
+ MOVD $-1, 0(R7)
+ RET
+eq:
+ MOVD $0, 0(R7)
+ RET
+
+TEXT runtime·cmpbodyclc(SB),NOSPLIT|NOFRAME,$0-0
+ CLC $1, 0(R3), 0(R5)
+ RET
+
+// This is called from .init_array and follows the platform, not Go, ABI.
+// We are overly conservative. We could only save the registers we use.
+// However, since this function is only called once per loaded module
+// performance is unimportant.
+TEXT runtime·addmoduledata(SB),NOSPLIT|NOFRAME,$0-0
+ // Save R6-R15, F0, F2, F4 and F6 in the
+ // register save area of the calling function
+ STMG R6, R15, 48(R15)
+ FMOVD F0, 128(R15)
+ FMOVD F2, 136(R15)
+ FMOVD F4, 144(R15)
+ FMOVD F6, 152(R15)
+
+ // append the argument (passed in R2, as per the ELF ABI) to the
+ // moduledata linked list.
+ MOVD runtime·lastmoduledatap(SB), R1
+ MOVD R2, moduledata_next(R1)
+ MOVD R2, runtime·lastmoduledatap(SB)
+
+ // Restore R6-R15, F0, F2, F4 and F6
+ LMG 48(R15), R6, R15
+ FMOVD F0, 128(R15)
+ FMOVD F2, 136(R15)
+ FMOVD F4, 144(R15)
+ FMOVD F6, 152(R15)
+ RET
+
+TEXT ·checkASM(SB),NOSPLIT,$0-1
+ MOVB $1, ret+0(FP)
+ RET
diff --git a/src/runtime/atomic_pointer.go b/src/runtime/atomic_pointer.go
index bd21b49945..4fe334014d 100644
--- a/src/runtime/atomic_pointer.go
+++ b/src/runtime/atomic_pointer.go
@@ -15,13 +15,12 @@ import (
// escape analysis decisions about the pointer value being stored.
// Instead, these are wrappers around the actual atomics (casp1 and so on)
// that use noescape to convey which arguments do not escape.
-//
-// Additionally, these functions must update the shadow heap for
-// write barrier checking.
+// atomicstorep performs *ptr = new atomically and invokes a write barrier.
+//
//go:nosplit
func atomicstorep(ptr unsafe.Pointer, new unsafe.Pointer) {
- atomic.Storep1(noescape(ptr), new)
+ atomic.StorepNoWB(noescape(ptr), new)
writebarrierptr_nostore((*uintptr)(ptr), uintptr(new))
}
@@ -45,7 +44,6 @@ func sync_atomic_StoreUintptr(ptr *uintptr, new uintptr)
//go:nosplit
func sync_atomic_StorePointer(ptr *unsafe.Pointer, new unsafe.Pointer) {
sync_atomic_StoreUintptr((*uintptr)(unsafe.Pointer(ptr)), uintptr(new))
- atomic.Storep1(noescape(unsafe.Pointer(ptr)), new)
writebarrierptr_nostore((*uintptr)(unsafe.Pointer(ptr)), uintptr(new))
}
@@ -54,9 +52,9 @@ func sync_atomic_SwapUintptr(ptr *uintptr, new uintptr) uintptr
//go:linkname sync_atomic_SwapPointer sync/atomic.SwapPointer
//go:nosplit
-func sync_atomic_SwapPointer(ptr unsafe.Pointer, new unsafe.Pointer) unsafe.Pointer {
- old := unsafe.Pointer(sync_atomic_SwapUintptr((*uintptr)(noescape(ptr)), uintptr(new)))
- writebarrierptr_nostore((*uintptr)(ptr), uintptr(new))
+func sync_atomic_SwapPointer(ptr *unsafe.Pointer, new unsafe.Pointer) unsafe.Pointer {
+ old := unsafe.Pointer(sync_atomic_SwapUintptr((*uintptr)(noescape(unsafe.Pointer(ptr))), uintptr(new)))
+ writebarrierptr_nostore((*uintptr)(unsafe.Pointer(ptr)), uintptr(new))
return old
}
diff --git a/src/runtime/cgo/asm_s390x.s b/src/runtime/cgo/asm_s390x.s
new file mode 100644
index 0000000000..5ed13cfe1e
--- /dev/null
+++ b/src/runtime/cgo/asm_s390x.s
@@ -0,0 +1,44 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+/*
+ * void crosscall2(void (*fn)(void*, int32), void*, int32)
+ * Save registers and call fn with two arguments.
+ * crosscall2 obeys the C ABI; fn obeys the Go ABI.
+ */
+TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0
+ // Start with standard C stack frame layout and linkage
+
+ // Save R6-R15, F0, F2, F4 and F6 in the
+ // register save area of the calling function
+ STMG R6, R15, 48(R15)
+ FMOVD F0, 128(R15)
+ FMOVD F2, 136(R15)
+ FMOVD F4, 144(R15)
+ FMOVD F6, 152(R15)
+
+ // Initialize Go ABI environment
+ XOR R0, R0
+ BL runtime·load_g(SB)
+
+ // Allocate 24 bytes on the stack
+ SUB $24, R15
+
+ MOVD R3, 8(R15) // arg1
+ MOVW R4, 16(R15) // arg2
+ BL (R2) // fn(arg1, arg2)
+
+ ADD $24, R15
+
+ // Restore R6-R15, F0, F2, F4 and F6
+ LMG 48(R15), R6, R15
+ FMOVD F0, 128(R15)
+ FMOVD F2, 136(R15)
+ FMOVD F4, 144(R15)
+ FMOVD F6, 152(R15)
+
+ RET
+
diff --git a/src/runtime/cgo/gcc_libinit.c b/src/runtime/cgo/gcc_libinit.c
index bdbaa2973c..06b9557709 100644
--- a/src/runtime/cgo/gcc_libinit.c
+++ b/src/runtime/cgo/gcc_libinit.c
@@ -4,7 +4,6 @@
// +build cgo
// +build darwin dragonfly freebsd linux netbsd solaris
-// +build !ppc64,!ppc64le
#include <pthread.h>
#include <stdio.h>
diff --git a/src/runtime/cgo/gcc_libinit_linux_ppc64x.c b/src/runtime/cgo/gcc_libinit_linux_ppc64x.c
deleted file mode 100644
index c133142f93..0000000000
--- a/src/runtime/cgo/gcc_libinit_linux_ppc64x.c
+++ /dev/null
@@ -1,26 +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.
-
-// TODO: see issue #10410
-// +build linux
-// +build ppc64 ppc64le
-
-#include <stdio.h>
-#include <stdlib.h>
-
-void
-x_cgo_sys_thread_create(void* (*func)(void*), void* arg) {
- fprintf(stderr, "x_cgo_sys_thread_create not implemented");
- abort();
-}
-
-void
-_cgo_wait_runtime_init_done() {
- // TODO(spetrovic): implement this method.
-}
-
-void
-x_cgo_notify_runtime_init_done(void* dummy) {
- // TODO(spetrovic): implement this method.
-} \ No newline at end of file
diff --git a/src/runtime/cgo/gcc_linux_s390x.c b/src/runtime/cgo/gcc_linux_s390x.c
new file mode 100644
index 0000000000..81e3b339b0
--- /dev/null
+++ b/src/runtime/cgo/gcc_linux_s390x.c
@@ -0,0 +1,68 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <pthread.h>
+#include <string.h>
+#include <signal.h>
+#include "libcgo.h"
+
+static void *threadentry(void*);
+
+void (*x_cgo_inittls)(void **tlsg, void **tlsbase);
+static void (*setg_gcc)(void*);
+
+void
+x_cgo_init(G *g, void (*setg)(void*), void **tlsbase)
+{
+ pthread_attr_t attr;
+ size_t size;
+
+ setg_gcc = setg;
+ pthread_attr_init(&attr);
+ pthread_attr_getstacksize(&attr, &size);
+ g->stacklo = (uintptr)&attr - size + 4096;
+ pthread_attr_destroy(&attr);
+}
+
+void
+_cgo_sys_thread_start(ThreadStart *ts)
+{
+ pthread_attr_t attr;
+ sigset_t ign, oset;
+ pthread_t p;
+ size_t size;
+ int err;
+
+ sigfillset(&ign);
+ pthread_sigmask(SIG_SETMASK, &ign, &oset);
+
+ pthread_attr_init(&attr);
+ pthread_attr_getstacksize(&attr, &size);
+ // Leave stacklo=0 and set stackhi=size; mstack will do the rest.
+ ts->g->stackhi = size;
+ err = pthread_create(&p, &attr, threadentry, ts);
+
+ pthread_sigmask(SIG_SETMASK, &oset, nil);
+
+ if (err != 0) {
+ fatalf("pthread_create failed: %s", strerror(err));
+ }
+}
+
+extern void crosscall_s390x(void (*fn)(void), void *g);
+
+static void*
+threadentry(void *v)
+{
+ ThreadStart ts;
+
+ ts = *(ThreadStart*)v;
+ free(v);
+
+ // Save g for this thread in C TLS
+ setg_gcc((void*)ts.g);
+
+ crosscall_s390x(ts.fn, (void*)ts.g);
+ return nil;
+}
diff --git a/src/runtime/cgo/gcc_s390x.S b/src/runtime/cgo/gcc_s390x.S
new file mode 100644
index 0000000000..6b163d0d21
--- /dev/null
+++ b/src/runtime/cgo/gcc_s390x.S
@@ -0,0 +1,43 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+ * void crosscall_s390x(void (*fn)(void), void *g)
+ *
+ * Calling into the go tool chain, where all registers are caller save.
+ * Called from standard s390x C ABI, where r6-r13, r15, and f0, f2, f4 and f6 are
+ * callee-save, so they must be saved explicitly.
+ */
+.globl crosscall_s390x
+crosscall_s390x:
+ /*
+ * save r6-r15, f0, f2, f4 and f6 in the
+ * register save area of the calling function
+ */
+ stmg %r6, %r15, 48(%r15)
+ stdy %f0, 128(%r15)
+ stdy %f2, 136(%r15)
+ stdy %f4, 144(%r15)
+ stdy %f6, 152(%r15)
+
+ /* assumes this call does not clobber r2 or r15 */
+ xgr %r0, %r0
+
+ /* grow stack 8 bytes and call fn */
+ agfi %r15, -8
+ basr %r14, %r2
+ agfi %r15, 8
+
+ /* restore registers */
+ lmg %r6, %r15, 48(%r15)
+ ldy %f0, 128(%r15)
+ ldy %f2, 136(%r15)
+ ldy %f4, 144(%r15)
+ ldy %f6, 152(%r15)
+
+ br %r14 /* restored by lmg */
+
+#ifdef __ELF__
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/src/runtime/cgo/signal_darwin_armx.go b/src/runtime/cgo/signal_darwin_armx.go
index 9c1ba5dee1..9f6741eb08 100644
--- a/src/runtime/cgo/signal_darwin_armx.go
+++ b/src/runtime/cgo/signal_darwin_armx.go
@@ -13,10 +13,14 @@ import "unsafe"
//go:linkname x_cgo_panicmem x_cgo_panicmem
var x_cgo_panicmem uintptr
+// use a pointer to avoid relocation of external symbol in __TEXT
+// make linker happy
+var _cgo_panicmem = &x_cgo_panicmem
+
// TODO(crawshaw): move this into x_cgo_init, it will not run until
// runtime has finished loading, which may be after its use.
func init() {
- x_cgo_panicmem = funcPC(panicmem)
+ *_cgo_panicmem = funcPC(panicmem)
}
func funcPC(f interface{}) uintptr {
diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go
index d5248803a4..c6000bf98f 100644
--- a/src/runtime/cgocall.go
+++ b/src/runtime/cgocall.go
@@ -246,8 +246,8 @@ func cgocallbackg1() {
case "386":
// On 386, stack frame is three words, plus caller PC.
cb = (*args)(unsafe.Pointer(sp + 4*sys.PtrSize))
- case "ppc64", "ppc64le":
- // On ppc64, the callback arguments are in the arguments area of
+ case "ppc64", "ppc64le", "s390x":
+ // On ppc64 and s390x, the callback arguments are in the arguments area of
// cgocallback's stack frame. The stack looks like this:
// +--------------------+------------------------------+
// | | ... |
@@ -300,7 +300,7 @@ func unwindm(restore *bool) {
switch GOARCH {
default:
throw("unwindm not implemented")
- case "386", "amd64", "arm", "ppc64", "ppc64le":
+ case "386", "amd64", "arm", "ppc64", "ppc64le", "s390x":
sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + sys.MinFrameSize))
case "arm64":
sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + 16))
diff --git a/src/runtime/chan.go b/src/runtime/chan.go
index 954b389f47..712ad8cef9 100644
--- a/src/runtime/chan.go
+++ b/src/runtime/chan.go
@@ -64,7 +64,7 @@ func makechan(t *chantype, size int64) *hchan {
throw("makechan: bad alignment")
}
if size < 0 || int64(uintptr(size)) != size || (elem.size > 0 && uintptr(size) > (_MaxMem-hchanSize)/elem.size) {
- panic("makechan: size out of range")
+ panic(plainError("makechan: size out of range"))
}
var c *hchan
@@ -74,7 +74,7 @@ func makechan(t *chantype, size int64) *hchan {
// buf points into the same allocation, elemtype is persistent.
// SudoG's are referenced from their owning thread so they can't be collected.
// TODO(dvyukov,rlh): Rethink when collector can move allocated objects.
- c = (*hchan)(mallocgc(hchanSize+uintptr(size)*elem.size, nil, flagNoScan))
+ c = (*hchan)(mallocgc(hchanSize+uintptr(size)*elem.size, nil, true))
if size > 0 && elem.size != 0 {
c.buf = add(unsafe.Pointer(c), hchanSize)
} else {
@@ -84,7 +84,7 @@ func makechan(t *chantype, size int64) *hchan {
}
} else {
c = new(hchan)
- c.buf = newarray(elem, uintptr(size))
+ c.buf = newarray(elem, int(size))
}
c.elemsize = uint16(elem.size)
c.elemtype = elem
@@ -171,7 +171,7 @@ func chansend(t *chantype, c *hchan, ep unsafe.Pointer, block bool, callerpc uin
if c.closed != 0 {
unlock(&c.lock)
- panic("send on closed channel")
+ panic(plainError("send on closed channel"))
}
if sg := c.recvq.dequeue(); sg != nil {
@@ -231,7 +231,7 @@ func chansend(t *chantype, c *hchan, ep unsafe.Pointer, block bool, callerpc uin
if c.closed == 0 {
throw("chansend: spurious wakeup")
}
- panic("send on closed channel")
+ panic(plainError("send on closed channel"))
}
gp.param = nil
if mysg.releasetime > 0 {
@@ -302,13 +302,13 @@ func sendDirect(t *_type, sg *sudog, src unsafe.Pointer) {
func closechan(c *hchan) {
if c == nil {
- panic("close of nil channel")
+ panic(plainError("close of nil channel"))
}
lock(&c.lock)
if c.closed != 0 {
unlock(&c.lock)
- panic("close of closed channel")
+ panic(plainError("close of closed channel"))
}
if raceenabled {
diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go
index 85fcc69fed..2941b8e8f8 100644
--- a/src/runtime/crash_test.go
+++ b/src/runtime/crash_test.go
@@ -273,6 +273,52 @@ func TestGoexitInPanic(t *testing.T) {
}
}
+// Issue 14965: Runtime panics should be of type runtime.Error
+func TestRuntimePanicWithRuntimeError(t *testing.T) {
+ testCases := [...]func(){
+ 0: func() {
+ var m map[uint64]bool
+ m[1234] = true
+ },
+ 1: func() {
+ ch := make(chan struct{})
+ close(ch)
+ close(ch)
+ },
+ 2: func() {
+ var ch = make(chan struct{})
+ close(ch)
+ ch <- struct{}{}
+ },
+ 3: func() {
+ var s = make([]int, 2)
+ _ = s[2]
+ },
+ 4: func() {
+ n := -1
+ _ = make(chan bool, n)
+ },
+ 5: func() {
+ close((chan bool)(nil))
+ },
+ }
+
+ for i, fn := range testCases {
+ got := panicValue(fn)
+ if _, ok := got.(runtime.Error); !ok {
+ t.Errorf("test #%d: recovered value %v(type %T) does not implement runtime.Error", i, got, got)
+ }
+ }
+}
+
+func panicValue(fn func()) (recovered interface{}) {
+ defer func() {
+ recovered = recover()
+ }()
+ fn()
+ return
+}
+
func TestPanicAfterGoexit(t *testing.T) {
// an uncaught panic should still work after goexit
output := runTestProg(t, "testprog", "PanicAfterGoexit")
diff --git a/src/runtime/defs_linux_s390x.go b/src/runtime/defs_linux_s390x.go
new file mode 100644
index 0000000000..5f55d5a889
--- /dev/null
+++ b/src/runtime/defs_linux_s390x.go
@@ -0,0 +1,167 @@
+// Copyright 2016 The Go 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 runtime
+
+const (
+ _EINTR = 0x4
+ _EAGAIN = 0xb
+ _ENOMEM = 0xc
+
+ _PROT_NONE = 0x0
+ _PROT_READ = 0x1
+ _PROT_WRITE = 0x2
+ _PROT_EXEC = 0x4
+
+ _MAP_ANON = 0x20
+ _MAP_PRIVATE = 0x2
+ _MAP_FIXED = 0x10
+
+ _MADV_DONTNEED = 0x4
+ _MADV_HUGEPAGE = 0xe
+ _MADV_NOHUGEPAGE = 0xf
+
+ _SA_RESTART = 0x10000000
+ _SA_ONSTACK = 0x8000000
+ _SA_SIGINFO = 0x4
+
+ _SIGHUP = 0x1
+ _SIGINT = 0x2
+ _SIGQUIT = 0x3
+ _SIGILL = 0x4
+ _SIGTRAP = 0x5
+ _SIGABRT = 0x6
+ _SIGBUS = 0x7
+ _SIGFPE = 0x8
+ _SIGKILL = 0x9
+ _SIGUSR1 = 0xa
+ _SIGSEGV = 0xb
+ _SIGUSR2 = 0xc
+ _SIGPIPE = 0xd
+ _SIGALRM = 0xe
+ _SIGSTKFLT = 0x10
+ _SIGCHLD = 0x11
+ _SIGCONT = 0x12
+ _SIGSTOP = 0x13
+ _SIGTSTP = 0x14
+ _SIGTTIN = 0x15
+ _SIGTTOU = 0x16
+ _SIGURG = 0x17
+ _SIGXCPU = 0x18
+ _SIGXFSZ = 0x19
+ _SIGVTALRM = 0x1a
+ _SIGPROF = 0x1b
+ _SIGWINCH = 0x1c
+ _SIGIO = 0x1d
+ _SIGPWR = 0x1e
+ _SIGSYS = 0x1f
+
+ _FPE_INTDIV = 0x1
+ _FPE_INTOVF = 0x2
+ _FPE_FLTDIV = 0x3
+ _FPE_FLTOVF = 0x4
+ _FPE_FLTUND = 0x5
+ _FPE_FLTRES = 0x6
+ _FPE_FLTINV = 0x7
+ _FPE_FLTSUB = 0x8
+
+ _BUS_ADRALN = 0x1
+ _BUS_ADRERR = 0x2
+ _BUS_OBJERR = 0x3
+
+ _SEGV_MAPERR = 0x1
+ _SEGV_ACCERR = 0x2
+
+ _ITIMER_REAL = 0x0
+ _ITIMER_VIRTUAL = 0x1
+ _ITIMER_PROF = 0x2
+
+ _EPOLLIN = 0x1
+ _EPOLLOUT = 0x4
+ _EPOLLERR = 0x8
+ _EPOLLHUP = 0x10
+ _EPOLLRDHUP = 0x2000
+ _EPOLLET = 0x80000000
+ _EPOLL_CLOEXEC = 0x80000
+ _EPOLL_CTL_ADD = 0x1
+ _EPOLL_CTL_DEL = 0x2
+ _EPOLL_CTL_MOD = 0x3
+)
+
+type timespec struct {
+ tv_sec int64
+ tv_nsec int64
+}
+
+func (ts *timespec) set_sec(x int64) {
+ ts.tv_sec = x
+}
+
+func (ts *timespec) set_nsec(x int32) {
+ ts.tv_nsec = int64(x)
+}
+
+type timeval struct {
+ tv_sec int64
+ tv_usec int64
+}
+
+func (tv *timeval) set_usec(x int32) {
+ tv.tv_usec = int64(x)
+}
+
+type sigactiont struct {
+ sa_handler uintptr
+ sa_flags uint64
+ sa_restorer uintptr
+ sa_mask uint64
+}
+
+type siginfo struct {
+ si_signo int32
+ si_errno int32
+ si_code int32
+ // below here is a union; si_addr is the only field we use
+ si_addr uint64
+}
+
+type itimerval struct {
+ it_interval timeval
+ it_value timeval
+}
+
+type epollevent struct {
+ events uint32
+ pad_cgo_0 [4]byte
+ data [8]byte // unaligned uintptr
+}
+
+const (
+ _O_RDONLY = 0x0
+ _O_CLOEXEC = 0x80000
+ _SA_RESTORER = 0
+)
+
+type sigaltstackt struct {
+ ss_sp *byte
+ ss_flags int32
+ ss_size uintptr
+}
+
+type sigcontext struct {
+ psw_mask uint64
+ psw_addr uint64
+ gregs [16]uint64
+ aregs [16]uint32
+ fpc uint32
+ fpregs [16]uint64
+}
+
+type ucontext struct {
+ uc_flags uint64
+ uc_link *ucontext
+ uc_stack sigaltstackt
+ uc_mcontext sigcontext
+ uc_sigmask uint64
+}
diff --git a/src/runtime/error.go b/src/runtime/error.go
index 3e1ec4bc5a..0238c5e592 100644
--- a/src/runtime/error.go
+++ b/src/runtime/error.go
@@ -50,13 +50,24 @@ func (e errorString) Error() string {
return "runtime error: " + string(e)
}
+// plainError represents a runtime error described a string without
+// the prefix "runtime error: " after invoking errorString.Error().
+// See Issue #14965.
+type plainError string
+
+func (e plainError) RuntimeError() {}
+
+func (e plainError) Error() string {
+ return string(e)
+}
+
type stringer interface {
String() string
}
func typestring(x interface{}) string {
e := efaceOf(&x)
- return e._type._string
+ return e._type.string()
}
// For calling from C.
@@ -82,5 +93,5 @@ func printany(i interface{}) {
// called from generated code
func panicwrap(pkg, typ, meth string) {
- panic("value method " + pkg + "." + typ + "." + meth + " called using nil *" + typ + " pointer")
+ panic(plainError("value method " + pkg + "." + typ + "." + meth + " called using nil *" + typ + " pointer"))
}
diff --git a/src/runtime/export_windows_test.go b/src/runtime/export_windows_test.go
index 66c103709c..536b398fd7 100644
--- a/src/runtime/export_windows_test.go
+++ b/src/runtime/export_windows_test.go
@@ -8,8 +8,11 @@ package runtime
import "unsafe"
-var TestingWER = &testingWER
-var OsYield = osyield
+var (
+ TestingWER = &testingWER
+ OsYield = osyield
+ TimeBeginPeriodRetValue = &timeBeginPeriodRetValue
+)
func NumberOfProcessors() int32 {
var info systeminfo
diff --git a/src/runtime/extern.go b/src/runtime/extern.go
index 984b0ca817..1df8691cfc 100644
--- a/src/runtime/extern.go
+++ b/src/runtime/extern.go
@@ -224,8 +224,8 @@ func Version() string {
// GOOS is the running program's operating system target:
// one of darwin, freebsd, linux, and so on.
-const GOOS string = sys.TheGoos
+const GOOS string = sys.GOOS
// GOARCH is the running program's architecture target:
-// 386, amd64, or arm.
-const GOARCH string = sys.TheGoarch
+// 386, amd64, arm, or s390x.
+const GOARCH string = sys.GOARCH
diff --git a/src/runtime/gcinfo_test.go b/src/runtime/gcinfo_test.go
index edb6361642..9a61b4f2b2 100644
--- a/src/runtime/gcinfo_test.go
+++ b/src/runtime/gcinfo_test.go
@@ -59,7 +59,7 @@ func TestGCInfo(t *testing.T) {
func verifyGCInfo(t *testing.T, name string, p interface{}, mask0 []byte) {
mask := runtime.GCMask(p)
- if bytes.Compare(mask, mask0) != 0 {
+ if !bytes.Equal(mask, mask0) {
t.Errorf("bad GC program for %v:\nwant %+v\ngot %+v", name, mask0, mask)
return
}
@@ -144,7 +144,7 @@ func infoBigStruct() []byte {
typeScalar, typeScalar, typeScalar, typeScalar, // t int; y uint16; u uint64
typePointer, typeScalar, // i string
}
- case "arm64", "amd64", "mips64", "mips64le", "ppc64", "ppc64le":
+ case "arm64", "amd64", "mips64", "mips64le", "ppc64", "ppc64le", "s390x":
return []byte{
typePointer, // q *int
typeScalar, typeScalar, typeScalar, // w byte; e [17]byte
diff --git a/src/runtime/hash64.go b/src/runtime/hash64.go
index fb3dba4000..d61f114475 100644
--- a/src/runtime/hash64.go
+++ b/src/runtime/hash64.go
@@ -6,7 +6,7 @@
// xxhash: https://code.google.com/p/xxhash/
// cityhash: https://code.google.com/p/cityhash/
-// +build amd64 amd64p32 arm64 mips64 mips64le ppc64 ppc64le
+// +build amd64 amd64p32 arm64 mips64 mips64le ppc64 ppc64le s390x
package runtime
diff --git a/src/runtime/hashmap.go b/src/runtime/hashmap.go
index 80b2b5338c..509cab2f0f 100644
--- a/src/runtime/hashmap.go
+++ b/src/runtime/hashmap.go
@@ -194,7 +194,7 @@ func makemap(t *maptype, hint int64, h *hmap, bucket unsafe.Pointer) *hmap {
}
if hint < 0 || int64(int32(hint)) != hint {
- panic("makemap: size out of range")
+ panic(plainError("makemap: size out of range"))
// TODO: make hint an int, then none of this nonsense
}
@@ -236,9 +236,6 @@ func makemap(t *maptype, hint int64, h *hmap, bucket unsafe.Pointer) *hmap {
throw("need padding in bucket (value)")
}
- // make sure zeroptr is large enough
- mapzero(t.elem)
-
// find size parameter which will hold the requested # of elements
B := uint8(0)
for ; hint > bucketCnt && float32(hint) > loadFactor*float32(uintptr(1)<<B); B++ {
@@ -249,7 +246,7 @@ func makemap(t *maptype, hint int64, h *hmap, bucket unsafe.Pointer) *hmap {
// If hint is large zeroing this memory could take a while.
buckets := bucket
if B != 0 {
- buckets = newarray(t.bucket, uintptr(1)<<B)
+ buckets = newarray(t.bucket, 1<<B)
}
// initialize Hmap
@@ -283,7 +280,7 @@ func mapaccess1(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {
msanread(key, t.key.size)
}
if h == nil || h.count == 0 {
- return atomic.Loadp(unsafe.Pointer(&zeroptr))
+ return unsafe.Pointer(&zeroVal[0])
}
if h.flags&hashWriting != 0 {
throw("concurrent map read and map write")
@@ -321,7 +318,7 @@ func mapaccess1(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {
}
b = b.overflow(t)
if b == nil {
- return atomic.Loadp(unsafe.Pointer(&zeroptr))
+ return unsafe.Pointer(&zeroVal[0])
}
}
}
@@ -337,7 +334,7 @@ func mapaccess2(t *maptype, h *hmap, key unsafe.Pointer) (unsafe.Pointer, bool)
msanread(key, t.key.size)
}
if h == nil || h.count == 0 {
- return atomic.Loadp(unsafe.Pointer(&zeroptr)), false
+ return unsafe.Pointer(&zeroVal[0]), false
}
if h.flags&hashWriting != 0 {
throw("concurrent map read and map write")
@@ -375,7 +372,7 @@ func mapaccess2(t *maptype, h *hmap, key unsafe.Pointer) (unsafe.Pointer, bool)
}
b = b.overflow(t)
if b == nil {
- return atomic.Loadp(unsafe.Pointer(&zeroptr)), false
+ return unsafe.Pointer(&zeroVal[0]), false
}
}
}
@@ -426,9 +423,25 @@ func mapaccessK(t *maptype, h *hmap, key unsafe.Pointer) (unsafe.Pointer, unsafe
}
}
+func mapaccess1_fat(t *maptype, h *hmap, key, zero unsafe.Pointer) unsafe.Pointer {
+ v := mapaccess1(t, h, key)
+ if v == unsafe.Pointer(&zeroVal[0]) {
+ return zero
+ }
+ return v
+}
+
+func mapaccess2_fat(t *maptype, h *hmap, key, zero unsafe.Pointer) (unsafe.Pointer, bool) {
+ v := mapaccess1(t, h, key)
+ if v == unsafe.Pointer(&zeroVal[0]) {
+ return zero, false
+ }
+ return v, true
+}
+
func mapassign1(t *maptype, h *hmap, key unsafe.Pointer, val unsafe.Pointer) {
if h == nil {
- panic("assignment to entry in nil map")
+ panic(plainError("assignment to entry in nil map"))
}
if raceenabled {
callerpc := getcallerpc(unsafe.Pointer(&t))
@@ -790,7 +803,9 @@ next:
}
}
it.bucket = bucket
- it.bptr = b
+ if it.bptr != b { // avoid unnecessary write barrier; see issue 14921
+ it.bptr = b
+ }
it.i = i + 1
it.checkBucket = checkBucket
return
@@ -806,7 +821,7 @@ func hashGrow(t *maptype, h *hmap) {
throw("evacuation not done in time")
}
oldbuckets := h.buckets
- newbuckets := newarray(t.bucket, uintptr(1)<<(h.B+1))
+ newbuckets := newarray(t.bucket, 1<<(h.B+1))
flags := h.flags &^ (iterator | oldIterator)
if h.flags&iterator != 0 {
flags |= oldIterator
@@ -1042,39 +1057,5 @@ func reflect_ismapkey(t *_type) bool {
return ismapkey(t)
}
-var zerolock mutex
-
-const initialZeroSize = 1024
-
-var zeroinitial [initialZeroSize]byte
-
-// All accesses to zeroptr and zerosize must be atomic so that they
-// can be accessed without locks in the common case.
-var zeroptr unsafe.Pointer = unsafe.Pointer(&zeroinitial)
-var zerosize uintptr = initialZeroSize
-
-// mapzero ensures that zeroptr points to a buffer large enough to
-// serve as the zero value for t.
-func mapzero(t *_type) {
- // Is the type small enough for existing buffer?
- cursize := uintptr(atomic.Loadp(unsafe.Pointer(&zerosize)))
- if t.size <= cursize {
- return
- }
-
- // Allocate a new buffer.
- lock(&zerolock)
- cursize = uintptr(atomic.Loadp(unsafe.Pointer(&zerosize)))
- if cursize < t.size {
- for cursize < t.size {
- cursize *= 2
- if cursize == 0 {
- // need >2GB zero on 32-bit machine
- throw("map element too large")
- }
- }
- atomic.Storep1(unsafe.Pointer(&zeroptr), persistentalloc(cursize, 64, &memstats.other_sys))
- atomic.Storep1(unsafe.Pointer(&zerosize), unsafe.Pointer(zerosize))
- }
- unlock(&zerolock)
-}
+const maxZero = 1024 // must match value in ../cmd/compile/internal/gc/walk.go
+var zeroVal [maxZero]byte
diff --git a/src/runtime/hashmap_fast.go b/src/runtime/hashmap_fast.go
index 6a5484edee..8f9bb5a6fc 100644
--- a/src/runtime/hashmap_fast.go
+++ b/src/runtime/hashmap_fast.go
@@ -5,7 +5,6 @@
package runtime
import (
- "runtime/internal/atomic"
"runtime/internal/sys"
"unsafe"
)
@@ -16,7 +15,7 @@ func mapaccess1_fast32(t *maptype, h *hmap, key uint32) unsafe.Pointer {
racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess1_fast32))
}
if h == nil || h.count == 0 {
- return atomic.Loadp(unsafe.Pointer(&zeroptr))
+ return unsafe.Pointer(&zeroVal[0])
}
if h.flags&hashWriting != 0 {
throw("concurrent map read and map write")
@@ -50,7 +49,7 @@ func mapaccess1_fast32(t *maptype, h *hmap, key uint32) unsafe.Pointer {
}
b = b.overflow(t)
if b == nil {
- return atomic.Loadp(unsafe.Pointer(&zeroptr))
+ return unsafe.Pointer(&zeroVal[0])
}
}
}
@@ -61,7 +60,7 @@ func mapaccess2_fast32(t *maptype, h *hmap, key uint32) (unsafe.Pointer, bool) {
racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_fast32))
}
if h == nil || h.count == 0 {
- return atomic.Loadp(unsafe.Pointer(&zeroptr)), false
+ return unsafe.Pointer(&zeroVal[0]), false
}
if h.flags&hashWriting != 0 {
throw("concurrent map read and map write")
@@ -95,7 +94,7 @@ func mapaccess2_fast32(t *maptype, h *hmap, key uint32) (unsafe.Pointer, bool) {
}
b = b.overflow(t)
if b == nil {
- return atomic.Loadp(unsafe.Pointer(&zeroptr)), false
+ return unsafe.Pointer(&zeroVal[0]), false
}
}
}
@@ -106,7 +105,7 @@ func mapaccess1_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer {
racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess1_fast64))
}
if h == nil || h.count == 0 {
- return atomic.Loadp(unsafe.Pointer(&zeroptr))
+ return unsafe.Pointer(&zeroVal[0])
}
if h.flags&hashWriting != 0 {
throw("concurrent map read and map write")
@@ -140,7 +139,7 @@ func mapaccess1_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer {
}
b = b.overflow(t)
if b == nil {
- return atomic.Loadp(unsafe.Pointer(&zeroptr))
+ return unsafe.Pointer(&zeroVal[0])
}
}
}
@@ -151,7 +150,7 @@ func mapaccess2_fast64(t *maptype, h *hmap, key uint64) (unsafe.Pointer, bool) {
racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_fast64))
}
if h == nil || h.count == 0 {
- return atomic.Loadp(unsafe.Pointer(&zeroptr)), false
+ return unsafe.Pointer(&zeroVal[0]), false
}
if h.flags&hashWriting != 0 {
throw("concurrent map read and map write")
@@ -185,7 +184,7 @@ func mapaccess2_fast64(t *maptype, h *hmap, key uint64) (unsafe.Pointer, bool) {
}
b = b.overflow(t)
if b == nil {
- return atomic.Loadp(unsafe.Pointer(&zeroptr)), false
+ return unsafe.Pointer(&zeroVal[0]), false
}
}
}
@@ -196,7 +195,7 @@ func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer {
racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess1_faststr))
}
if h == nil || h.count == 0 {
- return atomic.Loadp(unsafe.Pointer(&zeroptr))
+ return unsafe.Pointer(&zeroVal[0])
}
if h.flags&hashWriting != 0 {
throw("concurrent map read and map write")
@@ -220,7 +219,7 @@ func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer {
return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize))
}
}
- return atomic.Loadp(unsafe.Pointer(&zeroptr))
+ return unsafe.Pointer(&zeroVal[0])
}
// long key, try not to do more comparisons than necessary
keymaybe := uintptr(bucketCnt)
@@ -258,7 +257,7 @@ func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer {
return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+keymaybe*uintptr(t.valuesize))
}
}
- return atomic.Loadp(unsafe.Pointer(&zeroptr))
+ return unsafe.Pointer(&zeroVal[0])
}
dohash:
hash := t.key.alg.hash(noescape(unsafe.Pointer(&ky)), uintptr(h.hash0))
@@ -290,7 +289,7 @@ dohash:
}
b = b.overflow(t)
if b == nil {
- return atomic.Loadp(unsafe.Pointer(&zeroptr))
+ return unsafe.Pointer(&zeroVal[0])
}
}
}
@@ -301,7 +300,7 @@ func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) {
racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_faststr))
}
if h == nil || h.count == 0 {
- return atomic.Loadp(unsafe.Pointer(&zeroptr)), false
+ return unsafe.Pointer(&zeroVal[0]), false
}
if h.flags&hashWriting != 0 {
throw("concurrent map read and map write")
@@ -325,7 +324,7 @@ func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) {
return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)), true
}
}
- return atomic.Loadp(unsafe.Pointer(&zeroptr)), false
+ return unsafe.Pointer(&zeroVal[0]), false
}
// long key, try not to do more comparisons than necessary
keymaybe := uintptr(bucketCnt)
@@ -361,7 +360,7 @@ func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) {
return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+keymaybe*uintptr(t.valuesize)), true
}
}
- return atomic.Loadp(unsafe.Pointer(&zeroptr)), false
+ return unsafe.Pointer(&zeroVal[0]), false
}
dohash:
hash := t.key.alg.hash(noescape(unsafe.Pointer(&ky)), uintptr(h.hash0))
@@ -393,7 +392,7 @@ dohash:
}
b = b.overflow(t)
if b == nil {
- return atomic.Loadp(unsafe.Pointer(&zeroptr)), false
+ return unsafe.Pointer(&zeroVal[0]), false
}
}
}
diff --git a/src/runtime/heapdump.go b/src/runtime/heapdump.go
index 96dd6ff867..6085c6866c 100644
--- a/src/runtime/heapdump.go
+++ b/src/runtime/heapdump.go
@@ -183,10 +183,11 @@ func dumptype(t *_type) {
dumpint(tagType)
dumpint(uint64(uintptr(unsafe.Pointer(t))))
dumpint(uint64(t.size))
- if x := t.uncommon(); x == nil || x.pkgpath == nil {
- dumpstr(t._string)
+ if x := t.uncommon(); x == nil || t.nameOff(x.pkgpath).name() == "" {
+ dumpstr(t.string())
} else {
- pkgpath := stringStructOf(x.pkgpath)
+ pkgpathstr := t.nameOff(x.pkgpath).name()
+ pkgpath := stringStructOf(&pkgpathstr)
namestr := t.name()
name := stringStructOf(&namestr)
dumpint(uint64(uintptr(pkgpath.len) + 1 + uintptr(name.len)))
@@ -500,7 +501,7 @@ func dumpparams() {
dumpint(sys.PtrSize)
dumpint(uint64(mheap_.arena_start))
dumpint(uint64(mheap_.arena_used))
- dumpint(sys.TheChar)
+ dumpstr(sys.GOARCH)
dumpstr(sys.Goexperiment)
dumpint(uint64(ncpu))
}
diff --git a/src/runtime/iface.go b/src/runtime/iface.go
index 3ce1e237d3..b57d1cc63c 100644
--- a/src/runtime/iface.go
+++ b/src/runtime/iface.go
@@ -37,7 +37,8 @@ func getitab(inter *interfacetype, typ *_type, canfail bool) *itab {
if canfail {
return nil
}
- panic(&TypeAssertionError{"", typ._string, inter.typ._string, inter.mhdr[0].name.name()})
+ name := inter.typ.nameOff(inter.mhdr[0].name)
+ panic(&TypeAssertionError{"", typ.string(), inter.typ.string(), name.name()})
}
h := itabhash(inter, typ)
@@ -93,26 +94,30 @@ func additab(m *itab, locked, canfail bool) {
// so can iterate over both in lock step;
// the loop is O(ni+nt) not O(ni*nt).
ni := len(inter.mhdr)
- nt := len(x.mhdr)
+ nt := int(x.mcount)
+ xmhdr := (*[1 << 16]method)(add(unsafe.Pointer(x), uintptr(x.moff)))[:nt:nt]
j := 0
for k := 0; k < ni; k++ {
i := &inter.mhdr[k]
- iname := i.name.name()
- itype := i._type
- ipkg := i.name.pkgPath()
- if ipkg == nil {
- ipkg = inter.pkgpath
+ itype := inter.typ.typeOff(i.ityp)
+ name := inter.typ.nameOff(i.name)
+ iname := name.name()
+ ipkg := name.pkgPath()
+ if ipkg == "" {
+ ipkg = inter.pkgpath.name()
}
for ; j < nt; j++ {
- t := &x.mhdr[j]
- if t.mtyp == itype && t.name.name() == iname {
- pkgPath := t.name.pkgPath()
- if pkgPath == nil {
- pkgPath = x.pkgpath
+ t := &xmhdr[j]
+ tname := typ.nameOff(t.name)
+ if typ.typeOff(t.mtyp) == itype && tname.name() == iname {
+ pkgPath := tname.pkgPath()
+ if pkgPath == "" {
+ pkgPath = typ.nameOff(x.pkgpath).name()
}
- if t.name.isExported() || pkgPath == ipkg {
+ if tname.isExported() || pkgPath == ipkg {
if m != nil {
- *(*unsafe.Pointer)(add(unsafe.Pointer(&m.fun[0]), uintptr(k)*sys.PtrSize)) = t.ifn
+ ifn := typ.textOff(t.ifn)
+ *(*unsafe.Pointer)(add(unsafe.Pointer(&m.fun[0]), uintptr(k)*sys.PtrSize)) = ifn
}
goto nextimethod
}
@@ -123,7 +128,7 @@ func additab(m *itab, locked, canfail bool) {
if locked {
unlock(&ifaceLock)
}
- panic(&TypeAssertionError{"", typ._string, inter.typ._string, iname})
+ panic(&TypeAssertionError{"", typ.string(), inter.typ.string(), iname})
}
m.bad = 1
break
@@ -155,58 +160,54 @@ func convT2E(t *_type, elem unsafe.Pointer, x unsafe.Pointer) (e eface) {
msanread(elem, t.size)
}
if isDirectIface(t) {
- e._type = t
- typedmemmove(t, unsafe.Pointer(&e.data), elem)
- } else {
- if x == nil {
- x = newobject(t)
- }
+ throw("direct convT2E")
+ }
+ if x == nil {
+ x = newobject(t)
// TODO: We allocate a zeroed object only to overwrite it with
// actual data. Figure out how to avoid zeroing. Also below in convT2I.
- typedmemmove(t, x, elem)
- e._type = t
- e.data = x
}
+ typedmemmove(t, x, elem)
+ e._type = t
+ e.data = x
return
}
func convT2I(tab *itab, elem unsafe.Pointer, x unsafe.Pointer) (i iface) {
t := tab._type
if raceenabled {
- raceReadObjectPC(t, elem, getcallerpc(unsafe.Pointer(&t)), funcPC(convT2I))
+ raceReadObjectPC(t, elem, getcallerpc(unsafe.Pointer(&tab)), funcPC(convT2I))
}
if msanenabled {
msanread(elem, t.size)
}
if isDirectIface(t) {
- i.tab = tab
- typedmemmove(t, unsafe.Pointer(&i.data), elem)
- } else {
- if x == nil {
- x = newobject(t)
- }
- typedmemmove(t, x, elem)
- i.tab = tab
- i.data = x
+ throw("direct convT2I")
+ }
+ if x == nil {
+ x = newobject(t)
}
+ typedmemmove(t, x, elem)
+ i.tab = tab
+ i.data = x
return
}
func panicdottype(have, want, iface *_type) {
haveString := ""
if have != nil {
- haveString = have._string
+ haveString = have.string()
}
- panic(&TypeAssertionError{iface._string, haveString, want._string, ""})
+ panic(&TypeAssertionError{iface.string(), haveString, want.string(), ""})
}
func assertI2T(t *_type, i iface, r unsafe.Pointer) {
tab := i.tab
if tab == nil {
- panic(&TypeAssertionError{"", "", t._string, ""})
+ panic(&TypeAssertionError{"", "", t.string(), ""})
}
if tab._type != t {
- panic(&TypeAssertionError{tab.inter.typ._string, tab._type._string, t._string, ""})
+ panic(&TypeAssertionError{tab.inter.typ.string(), tab._type.string(), t.string(), ""})
}
if r != nil {
if isDirectIface(t) {
@@ -237,10 +238,10 @@ func assertI2T2(t *_type, i iface, r unsafe.Pointer) bool {
func assertE2T(t *_type, e eface, r unsafe.Pointer) {
if e._type == nil {
- panic(&TypeAssertionError{"", "", t._string, ""})
+ panic(&TypeAssertionError{"", "", t.string(), ""})
}
if e._type != t {
- panic(&TypeAssertionError{"", e._type._string, t._string, ""})
+ panic(&TypeAssertionError{"", e._type.string(), t.string(), ""})
}
if r != nil {
if isDirectIface(t) {
@@ -284,7 +285,7 @@ func assertI2E(inter *interfacetype, i iface, r *eface) {
tab := i.tab
if tab == nil {
// explicit conversions require non-nil interface value.
- panic(&TypeAssertionError{"", "", inter.typ._string, ""})
+ panic(&TypeAssertionError{"", "", inter.typ.string(), ""})
}
r._type = tab._type
r.data = i.data
@@ -321,7 +322,7 @@ func assertI2I(inter *interfacetype, i iface, r *iface) {
tab := i.tab
if tab == nil {
// explicit conversions require non-nil interface value.
- panic(&TypeAssertionError{"", "", inter.typ._string, ""})
+ panic(&TypeAssertionError{"", "", inter.typ.string(), ""})
}
if tab.inter == inter {
r.tab = tab
@@ -360,7 +361,7 @@ func assertE2I(inter *interfacetype, e eface, r *iface) {
t := e._type
if t == nil {
// explicit conversions require non-nil interface value.
- panic(&TypeAssertionError{"", "", inter.typ._string, ""})
+ panic(&TypeAssertionError{"", "", inter.typ.string(), ""})
}
r.tab = getitab(inter, t, false)
r.data = e.data
@@ -401,7 +402,7 @@ func reflect_ifaceE2I(inter *interfacetype, e eface, dst *iface) {
func assertE2E(inter *interfacetype, e eface, r *eface) {
if e._type == nil {
// explicit conversions require non-nil interface value.
- panic(&TypeAssertionError{"", "", inter.typ._string, ""})
+ panic(&TypeAssertionError{"", "", inter.typ.string(), ""})
}
*r = e
}
diff --git a/src/runtime/internal/atomic/asm_386.s b/src/runtime/internal/atomic/asm_386.s
index ce84fd83d1..ebecd0b4cb 100644
--- a/src/runtime/internal/atomic/asm_386.s
+++ b/src/runtime/internal/atomic/asm_386.s
@@ -102,7 +102,7 @@ TEXT runtime∕internal∕atomic·Xchguintptr(SB), NOSPLIT, $0-12
JMP runtime∕internal∕atomic·Xchg(SB)
-TEXT runtime∕internal∕atomic·Storep1(SB), NOSPLIT, $0-8
+TEXT runtime∕internal∕atomic·StorepNoWB(SB), NOSPLIT, $0-8
MOVL ptr+0(FP), BX
MOVL val+4(FP), AX
XCHGL AX, 0(BX)
diff --git a/src/runtime/internal/atomic/asm_amd64.s b/src/runtime/internal/atomic/asm_amd64.s
index 7463fec4a1..94d4ac2698 100644
--- a/src/runtime/internal/atomic/asm_amd64.s
+++ b/src/runtime/internal/atomic/asm_amd64.s
@@ -115,7 +115,7 @@ TEXT runtime∕internal∕atomic·Xchg64(SB), NOSPLIT, $0-24
TEXT runtime∕internal∕atomic·Xchguintptr(SB), NOSPLIT, $0-24
JMP runtime∕internal∕atomic·Xchg64(SB)
-TEXT runtime∕internal∕atomic·Storep1(SB), NOSPLIT, $0-16
+TEXT runtime∕internal∕atomic·StorepNoWB(SB), NOSPLIT, $0-16
MOVQ ptr+0(FP), BX
MOVQ val+8(FP), AX
XCHGQ AX, 0(BX)
diff --git a/src/runtime/internal/atomic/asm_amd64p32.s b/src/runtime/internal/atomic/asm_amd64p32.s
index f1e2c3aca6..74c79d08fd 100644
--- a/src/runtime/internal/atomic/asm_amd64p32.s
+++ b/src/runtime/internal/atomic/asm_amd64p32.s
@@ -115,7 +115,7 @@ TEXT runtime∕internal∕atomic·Xchg64(SB), NOSPLIT, $0-24
TEXT runtime∕internal∕atomic·Xchguintptr(SB), NOSPLIT, $0-12
JMP runtime∕internal∕atomic·Xchg(SB)
-TEXT runtime∕internal∕atomic·Storep1(SB), NOSPLIT, $0-8
+TEXT runtime∕internal∕atomic·StorepNoWB(SB), NOSPLIT, $0-8
MOVL ptr+0(FP), BX
MOVL val+4(FP), AX
XCHGL AX, 0(BX)
diff --git a/src/runtime/internal/atomic/asm_mips64x.s b/src/runtime/internal/atomic/asm_mips64x.s
index a454f284ab..d0f5c7bdd3 100644
--- a/src/runtime/internal/atomic/asm_mips64x.s
+++ b/src/runtime/internal/atomic/asm_mips64x.s
@@ -155,7 +155,7 @@ TEXT ·Xchg64(SB), NOSPLIT, $0-24
TEXT ·Xchguintptr(SB), NOSPLIT, $0-24
JMP ·Xchg64(SB)
-TEXT ·Storep1(SB), NOSPLIT, $0-16
+TEXT ·StorepNoWB(SB), NOSPLIT, $0-16
JMP ·Store64(SB)
TEXT ·Store(SB), NOSPLIT, $0-12
diff --git a/src/runtime/internal/atomic/asm_ppc64x.s b/src/runtime/internal/atomic/asm_ppc64x.s
index 45a48b6203..4a776787a2 100644
--- a/src/runtime/internal/atomic/asm_ppc64x.s
+++ b/src/runtime/internal/atomic/asm_ppc64x.s
@@ -150,7 +150,7 @@ TEXT runtime∕internal∕atomic·Xchguintptr(SB), NOSPLIT, $0-24
BR runtime∕internal∕atomic·Xchg64(SB)
-TEXT runtime∕internal∕atomic·Storep1(SB), NOSPLIT, $0-16
+TEXT runtime∕internal∕atomic·StorepNoWB(SB), NOSPLIT, $0-16
BR runtime∕internal∕atomic·Store64(SB)
TEXT runtime∕internal∕atomic·Store(SB), NOSPLIT, $0-12
diff --git a/src/runtime/internal/atomic/asm_s390x.s b/src/runtime/internal/atomic/asm_s390x.s
new file mode 100644
index 0000000000..c84718cb8f
--- /dev/null
+++ b/src/runtime/internal/atomic/asm_s390x.s
@@ -0,0 +1,174 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+// func Cas(ptr *uint32, old, new uint32) bool
+// Atomically:
+// if *ptr == old {
+// *val = new
+// return 1
+// } else {
+// return 0
+// }
+TEXT ·Cas(SB), NOSPLIT, $0-17
+ MOVD ptr+0(FP), R3
+ MOVWZ old+8(FP), R4
+ MOVWZ new+12(FP), R5
+ CS R4, R5, 0(R3) // if (R4 == 0(R3)) then 0(R3)= R5
+ BNE cas_fail
+ MOVB $1, ret+16(FP)
+ RET
+cas_fail:
+ MOVB $0, ret+16(FP)
+ RET
+
+// func Cas64(ptr *uint64, old, new uint64) bool
+// Atomically:
+// if *ptr == old {
+// *ptr = new
+// return 1
+// } else {
+// return 0
+// }
+TEXT ·Cas64(SB), NOSPLIT, $0-25
+ MOVD ptr+0(FP), R3
+ MOVD old+8(FP), R4
+ MOVD new+16(FP), R5
+ CSG R4, R5, 0(R3) // if (R4 == 0(R3)) then 0(R3)= R5
+ BNE cas64_fail
+ MOVB $1, ret+24(FP)
+ RET
+cas64_fail:
+ MOVB $0, ret+24(FP)
+ RET
+
+// func Casuintptr(ptr *uintptr, old, new uintptr) bool
+TEXT ·Casuintptr(SB), NOSPLIT, $0-25
+ BR ·Cas64(SB)
+
+// func Loaduintptr(ptr *uintptr) uintptr
+TEXT ·Loaduintptr(SB), NOSPLIT, $0-16
+ BR ·Load64(SB)
+
+// func Loaduint(ptr *uint) uint
+TEXT ·Loaduint(SB), NOSPLIT, $0-16
+ BR ·Load64(SB)
+
+// func Storeuintptr(ptr *uintptr, new uintptr)
+TEXT ·Storeuintptr(SB), NOSPLIT, $0-16
+ BR ·Store64(SB)
+
+// func Loadint64(ptr *int64) int64
+TEXT ·Loadint64(SB), NOSPLIT, $0-16
+ BR ·Load64(SB)
+
+// func Xadduintptr(ptr *uintptr, delta uintptr) uintptr
+TEXT ·Xadduintptr(SB), NOSPLIT, $0-24
+ BR ·Xadd64(SB)
+
+// func Xaddint64(ptr *int64, delta int64) int64
+TEXT ·Xaddint64(SB), NOSPLIT, $0-16
+ BR ·Xadd64(SB)
+
+// func Casp1(ptr *unsafe.Pointer, old, new unsafe.Pointer) bool
+// Atomically:
+// if *ptr == old {
+// *ptr = new
+// return 1
+// } else {
+// return 0
+// }
+TEXT ·Casp1(SB), NOSPLIT, $0-25
+ BR ·Cas64(SB)
+
+// func Xadd(ptr *uint32, delta int32) uint32
+// Atomically:
+// *ptr += delta
+// return *ptr
+TEXT ·Xadd(SB), NOSPLIT, $0-20
+ MOVD ptr+0(FP), R4
+ MOVW delta+8(FP), R5
+ MOVW (R4), R3
+repeat:
+ ADD R5, R3, R6
+ CS R3, R6, (R4) // if R3==(R4) then (R4)=R6 else R3=(R4)
+ BNE repeat
+ MOVW R6, ret+16(FP)
+ RET
+
+// func Xadd64(ptr *uint64, delta int64) uint64
+TEXT ·Xadd64(SB), NOSPLIT, $0-24
+ MOVD ptr+0(FP), R4
+ MOVD delta+8(FP), R5
+ MOVD (R4), R3
+repeat:
+ ADD R5, R3, R6
+ CSG R3, R6, (R4) // if R3==(R4) then (R4)=R6 else R3=(R4)
+ BNE repeat
+ MOVD R6, ret+16(FP)
+ RET
+
+// func Xchg(ptr *uint32, new uint32) uint32
+TEXT ·Xchg(SB), NOSPLIT, $0-20
+ MOVD ptr+0(FP), R4
+ MOVW new+8(FP), R3
+ MOVW (R4), R6
+repeat:
+ CS R6, R3, (R4) // if R6==(R4) then (R4)=R3 else R6=(R4)
+ BNE repeat
+ MOVW R6, ret+16(FP)
+ RET
+
+// func Xchg64(ptr *uint64, new uint64) uint64
+TEXT ·Xchg64(SB), NOSPLIT, $0-24
+ MOVD ptr+0(FP), R4
+ MOVD new+8(FP), R3
+ MOVD (R4), R6
+repeat:
+ CSG R6, R3, (R4) // if R6==(R4) then (R4)=R3 else R6=(R4)
+ BNE repeat
+ MOVD R6, ret+16(FP)
+ RET
+
+// func Xchguintptr(ptr *uintptr, new uintptr) uintptr
+TEXT ·Xchguintptr(SB), NOSPLIT, $0-24
+ BR ·Xchg64(SB)
+
+// func Or8(addr *uint8, v uint8)
+TEXT ·Or8(SB), NOSPLIT, $0-9
+ MOVD ptr+0(FP), R3
+ MOVBZ val+8(FP), R4
+ // Calculate shift.
+ AND $3, R3, R5
+ XOR $3, R5 // big endian - flip direction
+ SLD $3, R5 // MUL $8, R5
+ SLD R5, R4
+ // Align ptr down to 4 bytes so we can use 32-bit load/store.
+ AND $-4, R3
+ MOVWZ 0(R3), R6
+again:
+ OR R4, R6, R7
+ CS R6, R7, 0(R3) // if R6==(R3) then (R3)=R7 else R6=(R3)
+ BNE again
+ RET
+
+// func And8(addr *uint8, v uint8)
+TEXT ·And8(SB), NOSPLIT, $0-9
+ MOVD ptr+0(FP), R3
+ MOVBZ val+8(FP), R4
+ // Calculate shift.
+ AND $3, R3, R5
+ XOR $3, R5 // big endian - flip direction
+ SLD $3, R5 // MUL $8, R5
+ OR $-256, R4 // create 0xffffffffffffffxx
+ RLLG R5, R4
+ // Align ptr down to 4 bytes so we can use 32-bit load/store.
+ AND $-4, R3
+ MOVWZ 0(R3), R6
+again:
+ AND R4, R6, R7
+ CS R6, R7, 0(R3) // if R6==(R3) then (R3)=R7 else R6=(R3)
+ BNE again
+ RET
diff --git a/src/runtime/internal/atomic/atomic_386.go b/src/runtime/internal/atomic/atomic_386.go
index f4c50b0be1..23a8479515 100644
--- a/src/runtime/internal/atomic/atomic_386.go
+++ b/src/runtime/internal/atomic/atomic_386.go
@@ -73,4 +73,4 @@ func Store(ptr *uint32, val uint32)
func Store64(ptr *uint64, val uint64)
// NO go:noescape annotation; see atomic_pointer.go.
-func Storep1(ptr unsafe.Pointer, val unsafe.Pointer)
+func StorepNoWB(ptr unsafe.Pointer, val unsafe.Pointer)
diff --git a/src/runtime/internal/atomic/atomic_amd64x.go b/src/runtime/internal/atomic/atomic_amd64x.go
index bd40fb3ea2..54851d30f4 100644
--- a/src/runtime/internal/atomic/atomic_amd64x.go
+++ b/src/runtime/internal/atomic/atomic_amd64x.go
@@ -61,5 +61,8 @@ func Store(ptr *uint32, val uint32)
//go:noescape
func Store64(ptr *uint64, val uint64)
+// StorepNoWB performs *ptr = val atomically and without a write
+// barrier.
+//
// NO go:noescape annotation; see atomic_pointer.go.
-func Storep1(ptr unsafe.Pointer, val unsafe.Pointer)
+func StorepNoWB(ptr unsafe.Pointer, val unsafe.Pointer)
diff --git a/src/runtime/internal/atomic/atomic_arm.go b/src/runtime/internal/atomic/atomic_arm.go
index c361aef382..244237df4d 100644
--- a/src/runtime/internal/atomic/atomic_arm.go
+++ b/src/runtime/internal/atomic/atomic_arm.go
@@ -85,7 +85,7 @@ func Loadp(addr unsafe.Pointer) unsafe.Pointer {
}
//go:nosplit
-func Storep1(addr unsafe.Pointer, v unsafe.Pointer) {
+func StorepNoWB(addr unsafe.Pointer, v unsafe.Pointer) {
for {
old := *(*unsafe.Pointer)(addr)
if Casp1((*unsafe.Pointer)(addr), old, v) {
diff --git a/src/runtime/internal/atomic/atomic_arm64.go b/src/runtime/internal/atomic/atomic_arm64.go
index 6b32346656..dc82c3396d 100644
--- a/src/runtime/internal/atomic/atomic_arm64.go
+++ b/src/runtime/internal/atomic/atomic_arm64.go
@@ -77,4 +77,4 @@ func Store(ptr *uint32, val uint32)
func Store64(ptr *uint64, val uint64)
// NO go:noescape annotation; see atomic_pointer.go.
-func Storep1(ptr unsafe.Pointer, val unsafe.Pointer)
+func StorepNoWB(ptr unsafe.Pointer, val unsafe.Pointer)
diff --git a/src/runtime/internal/atomic/atomic_arm64.s b/src/runtime/internal/atomic/atomic_arm64.s
index 7b1b0efaf6..eb32f378aa 100644
--- a/src/runtime/internal/atomic/atomic_arm64.s
+++ b/src/runtime/internal/atomic/atomic_arm64.s
@@ -25,7 +25,7 @@ TEXT ·Loadp(SB),NOSPLIT,$-8-16
MOVD R0, ret+8(FP)
RET
-TEXT runtime∕internal∕atomic·Storep1(SB), NOSPLIT, $0-16
+TEXT runtime∕internal∕atomic·StorepNoWB(SB), NOSPLIT, $0-16
B runtime∕internal∕atomic·Store64(SB)
TEXT runtime∕internal∕atomic·Store(SB), NOSPLIT, $0-12
diff --git a/src/runtime/internal/atomic/atomic_mips64x.go b/src/runtime/internal/atomic/atomic_mips64x.go
index 8094db58a0..d06ea4809a 100644
--- a/src/runtime/internal/atomic/atomic_mips64x.go
+++ b/src/runtime/internal/atomic/atomic_mips64x.go
@@ -53,4 +53,4 @@ func Store(ptr *uint32, val uint32)
func Store64(ptr *uint64, val uint64)
// NO go:noescape annotation; see atomic_pointer.go.
-func Storep1(ptr unsafe.Pointer, val unsafe.Pointer)
+func StorepNoWB(ptr unsafe.Pointer, val unsafe.Pointer)
diff --git a/src/runtime/internal/atomic/atomic_ppc64x.go b/src/runtime/internal/atomic/atomic_ppc64x.go
index bf82b82643..72c98eb0c5 100644
--- a/src/runtime/internal/atomic/atomic_ppc64x.go
+++ b/src/runtime/internal/atomic/atomic_ppc64x.go
@@ -53,4 +53,4 @@ func Store(ptr *uint32, val uint32)
func Store64(ptr *uint64, val uint64)
// NO go:noescape annotation; see atomic_pointer.go.
-func Storep1(ptr unsafe.Pointer, val unsafe.Pointer)
+func StorepNoWB(ptr unsafe.Pointer, val unsafe.Pointer)
diff --git a/src/runtime/internal/atomic/atomic_s390x.go b/src/runtime/internal/atomic/atomic_s390x.go
new file mode 100644
index 0000000000..9343853485
--- /dev/null
+++ b/src/runtime/internal/atomic/atomic_s390x.go
@@ -0,0 +1,73 @@
+// Copyright 2016 The Go 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 atomic
+
+import "unsafe"
+
+//go:nosplit
+//go:noinline
+func Load(ptr *uint32) uint32 {
+ return *ptr
+}
+
+//go:nosplit
+//go:noinline
+func Loadp(ptr unsafe.Pointer) unsafe.Pointer {
+ return *(*unsafe.Pointer)(ptr)
+}
+
+//go:nosplit
+//go:noinline
+func Load64(ptr *uint64) uint64 {
+ return *ptr
+}
+
+//go:noinline
+//go:nosplit
+func Store(ptr *uint32, val uint32) {
+ *ptr = val
+}
+
+//go:noinline
+//go:nosplit
+func Store64(ptr *uint64, val uint64) {
+ *ptr = val
+}
+
+// NO go:noescape annotation; see atomic_pointer.go.
+//go:noinline
+//go:nosplit
+func StorepNoWB(ptr unsafe.Pointer, val unsafe.Pointer) {
+ *(*uintptr)(ptr) = uintptr(val)
+}
+
+//go:noescape
+func And8(ptr *uint8, val uint8)
+
+//go:noescape
+func Or8(ptr *uint8, val uint8)
+
+// NOTE: Do not add atomicxor8 (XOR is not idempotent).
+
+//go:noescape
+func Xadd(ptr *uint32, delta int32) uint32
+
+//go:noescape
+func Xadd64(ptr *uint64, delta int64) uint64
+
+//go:noescape
+func Xadduintptr(ptr *uintptr, delta uintptr) uintptr
+
+//go:noescape
+func Xchg(ptr *uint32, new uint32) uint32
+
+//go:noescape
+func Xchg64(ptr *uint64, new uint64) uint64
+
+//go:noescape
+func Xchguintptr(ptr *uintptr, new uintptr) uintptr
+
+//go:noescape
+func Cas64(ptr *uint64, old, new uint64) bool
diff --git a/src/runtime/internal/sys/arch.go b/src/runtime/internal/sys/arch.go
new file mode 100644
index 0000000000..c1757041d8
--- /dev/null
+++ b/src/runtime/internal/sys/arch.go
@@ -0,0 +1,17 @@
+// 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 sys
+
+type ArchFamilyType int
+
+const (
+ AMD64 ArchFamilyType = iota
+ ARM
+ ARM64
+ I386
+ MIPS64
+ PPC64
+ S390X
+)
diff --git a/src/runtime/internal/sys/arch_386.go b/src/runtime/internal/sys/arch_386.go
index 1f1c704f9a..48c42f7584 100644
--- a/src/runtime/internal/sys/arch_386.go
+++ b/src/runtime/internal/sys/arch_386.go
@@ -5,7 +5,7 @@
package sys
const (
- TheChar = '8'
+ ArchFamily = I386
BigEndian = 0
CacheLineSize = 64
PhysPageSize = GoosNacl*65536 + (1-GoosNacl)*4096 // 4k normally; 64k on NaCl
diff --git a/src/runtime/internal/sys/arch_amd64.go b/src/runtime/internal/sys/arch_amd64.go
index 80fff557f2..1bbdb99e07 100644
--- a/src/runtime/internal/sys/arch_amd64.go
+++ b/src/runtime/internal/sys/arch_amd64.go
@@ -5,7 +5,7 @@
package sys
const (
- TheChar = '6'
+ ArchFamily = AMD64
BigEndian = 0
CacheLineSize = 64
PhysPageSize = 4096
diff --git a/src/runtime/internal/sys/arch_amd64p32.go b/src/runtime/internal/sys/arch_amd64p32.go
index ca29f698a2..b7011a4ff2 100644
--- a/src/runtime/internal/sys/arch_amd64p32.go
+++ b/src/runtime/internal/sys/arch_amd64p32.go
@@ -5,7 +5,7 @@
package sys
const (
- TheChar = '6'
+ ArchFamily = AMD64
BigEndian = 0
CacheLineSize = 64
PhysPageSize = 65536*GoosNacl + 4096*(1-GoosNacl)
diff --git a/src/runtime/internal/sys/arch_arm.go b/src/runtime/internal/sys/arch_arm.go
index b185e8fb69..f90f52da7f 100644
--- a/src/runtime/internal/sys/arch_arm.go
+++ b/src/runtime/internal/sys/arch_arm.go
@@ -5,7 +5,7 @@
package sys
const (
- TheChar = '5'
+ ArchFamily = ARM
BigEndian = 0
CacheLineSize = 32
PhysPageSize = 65536*GoosNacl + 4096*(1-GoosNacl)
diff --git a/src/runtime/internal/sys/arch_arm64.go b/src/runtime/internal/sys/arch_arm64.go
index b63a7a6f9a..aaaa4b0947 100644
--- a/src/runtime/internal/sys/arch_arm64.go
+++ b/src/runtime/internal/sys/arch_arm64.go
@@ -5,7 +5,7 @@
package sys
const (
- TheChar = '7'
+ ArchFamily = ARM64
BigEndian = 0
CacheLineSize = 32
PhysPageSize = 65536
diff --git a/src/runtime/internal/sys/arch_mips64.go b/src/runtime/internal/sys/arch_mips64.go
index 5b933d4e1a..d5672599d2 100644
--- a/src/runtime/internal/sys/arch_mips64.go
+++ b/src/runtime/internal/sys/arch_mips64.go
@@ -5,7 +5,7 @@
package sys
const (
- TheChar = '0'
+ ArchFamily = MIPS64
BigEndian = 1
CacheLineSize = 32
PhysPageSize = 16384
diff --git a/src/runtime/internal/sys/arch_mips64le.go b/src/runtime/internal/sys/arch_mips64le.go
index ce2e98b19f..f8cdf2b2d2 100644
--- a/src/runtime/internal/sys/arch_mips64le.go
+++ b/src/runtime/internal/sys/arch_mips64le.go
@@ -5,7 +5,7 @@
package sys
const (
- TheChar = '0'
+ ArchFamily = MIPS64
BigEndian = 0
CacheLineSize = 32
PhysPageSize = 16384
diff --git a/src/runtime/internal/sys/arch_ppc64.go b/src/runtime/internal/sys/arch_ppc64.go
index 3aa07e1f56..cdec63ff71 100644
--- a/src/runtime/internal/sys/arch_ppc64.go
+++ b/src/runtime/internal/sys/arch_ppc64.go
@@ -5,7 +5,7 @@
package sys
const (
- TheChar = '9'
+ ArchFamily = PPC64
BigEndian = 1
CacheLineSize = 64
PhysPageSize = 65536
diff --git a/src/runtime/internal/sys/arch_ppc64le.go b/src/runtime/internal/sys/arch_ppc64le.go
index 0f02f0bf3c..4fd68f9ce3 100644
--- a/src/runtime/internal/sys/arch_ppc64le.go
+++ b/src/runtime/internal/sys/arch_ppc64le.go
@@ -5,7 +5,7 @@
package sys
const (
- TheChar = '9'
+ ArchFamily = PPC64
BigEndian = 0
CacheLineSize = 64
PhysPageSize = 65536
diff --git a/src/runtime/internal/sys/arch_s390x.go b/src/runtime/internal/sys/arch_s390x.go
index 8690571c81..ca1cb8646e 100644
--- a/src/runtime/internal/sys/arch_s390x.go
+++ b/src/runtime/internal/sys/arch_s390x.go
@@ -5,7 +5,7 @@
package sys
const (
- TheChar = 'z'
+ ArchFamily = S390X
BigEndian = 1
CacheLineSize = 256
PhysPageSize = 4096
diff --git a/src/runtime/internal/sys/gengoos.go b/src/runtime/internal/sys/gengoos.go
index e2bd87de4e..4c45c0af02 100644
--- a/src/runtime/internal/sys/gengoos.go
+++ b/src/runtime/internal/sys/gengoos.go
@@ -50,7 +50,7 @@ func main() {
fmt.Fprintf(&buf, "// +build !android\n\n") // must explicitly exclude android for linux
}
fmt.Fprintf(&buf, "package sys\n\n")
- fmt.Fprintf(&buf, "const TheGoos = `%s`\n\n", target)
+ fmt.Fprintf(&buf, "const GOOS = `%s`\n\n", target)
for _, goos := range gooses {
value := 0
if goos == target {
@@ -68,7 +68,7 @@ func main() {
var buf bytes.Buffer
fmt.Fprintf(&buf, "// generated by gengoos.go using 'go generate'\n\n")
fmt.Fprintf(&buf, "package sys\n\n")
- fmt.Fprintf(&buf, "const TheGoarch = `%s`\n\n", target)
+ fmt.Fprintf(&buf, "const GOARCH = `%s`\n\n", target)
for _, goarch := range goarches {
value := 0
if goarch == target {
diff --git a/src/runtime/internal/sys/intrinsics.go b/src/runtime/internal/sys/intrinsics.go
index 8feb754dbd..1054c6948f 100644
--- a/src/runtime/internal/sys/intrinsics.go
+++ b/src/runtime/internal/sys/intrinsics.go
@@ -4,88 +4,97 @@
package sys
+// Using techniques from http://supertech.csail.mit.edu/papers/debruijn.pdf
+
+const deBruijn64 = 0x0218a392cd3d5dbf
+
+var deBruijnIdx64 = [64]byte{
+ 0, 1, 2, 7, 3, 13, 8, 19,
+ 4, 25, 14, 28, 9, 34, 20, 40,
+ 5, 17, 26, 38, 15, 46, 29, 48,
+ 10, 31, 35, 54, 21, 50, 41, 57,
+ 63, 6, 12, 18, 24, 27, 33, 39,
+ 16, 37, 45, 47, 30, 53, 49, 56,
+ 62, 11, 23, 32, 36, 44, 52, 55,
+ 61, 22, 43, 51, 60, 42, 59, 58,
+}
+
+const deBruijn32 = 0x04653adf
+
+var deBruijnIdx32 = [32]byte{
+ 0, 1, 2, 6, 3, 11, 7, 16,
+ 4, 14, 12, 21, 8, 23, 17, 26,
+ 31, 5, 10, 15, 13, 20, 22, 25,
+ 30, 9, 19, 24, 29, 18, 28, 27,
+}
+
+const deBruijn16 = 0x09af
+
+var deBruijnIdx16 = [16]byte{
+ 0, 1, 2, 5, 3, 9, 6, 11,
+ 15, 4, 8, 10, 14, 7, 13, 12,
+}
+
+const deBruijn8 = 0x17
+
+var deBruijnIdx8 = [8]byte{
+ 0, 1, 2, 4, 7, 3, 6, 5,
+}
+
// Ctz64 counts trailing (low-order) zeroes,
// and if all are zero, then 64.
func Ctz64(x uint64) uint64 {
- if x&0xffffffff == 0 {
- return 32 + uint64(Ctz32(uint32(x>>32)))
- }
- return uint64(Ctz32(uint32(x)))
-
+ x &= -x // isolate low-order bit
+ y := x * deBruijn64 >> 58 // extract part of deBruijn sequence
+ y = uint64(deBruijnIdx64[y]) // convert to bit index
+ z := (x - 1) >> 57 & 64 // adjustment if zero
+ return y + z
}
// Ctz32 counts trailing (low-order) zeroes,
// and if all are zero, then 32.
func Ctz32(x uint32) uint32 {
- if x&0xffff == 0 {
- return 16 + uint32(Ctz16(uint16(x>>16)))
- }
- return uint32(Ctz16(uint16(x)))
+ x &= -x // isolate low-order bit
+ y := x * deBruijn32 >> 27 // extract part of deBruijn sequence
+ y = uint32(deBruijnIdx32[y]) // convert to bit index
+ z := (x - 1) >> 26 & 32 // adjustment if zero
+ return y + z
}
// Ctz16 counts trailing (low-order) zeroes,
// and if all are zero, then 16.
func Ctz16(x uint16) uint16 {
- if x&0xff == 0 {
- return 8 + uint16(Ctz8(uint8(x>>8)))
- }
- return uint16(Ctz8(uint8(x)))
+ x &= -x // isolate low-order bit
+ y := x * deBruijn16 >> 12 // extract part of deBruijn sequence
+ y = uint16(deBruijnIdx16[y]) // convert to bit index
+ z := (x - 1) >> 11 & 16 // adjustment if zero
+ return y + z
}
// Ctz8 counts trailing (low-order) zeroes,
// and if all are zero, then 8.
func Ctz8(x uint8) uint8 {
- return ctzVals[x]
+ x &= -x // isolate low-order bit
+ y := x * deBruijn8 >> 5 // extract part of deBruijn sequence
+ y = uint8(deBruijnIdx8[y]) // convert to bit index
+ z := (x - 1) >> 4 & 8 // adjustment if zero
+ return y + z
}
-var ctzVals = [256]uint8{
- 8, 0, 1, 0, 2, 0, 1, 0,
- 3, 0, 1, 0, 2, 0, 1, 0,
- 4, 0, 1, 0, 2, 0, 1, 0,
- 3, 0, 1, 0, 2, 0, 1, 0,
- 5, 0, 1, 0, 2, 0, 1, 0,
- 3, 0, 1, 0, 2, 0, 1, 0,
- 4, 0, 1, 0, 2, 0, 1, 0,
- 3, 0, 1, 0, 2, 0, 1, 0,
- 6, 0, 1, 0, 2, 0, 1, 0,
- 3, 0, 1, 0, 2, 0, 1, 0,
- 4, 0, 1, 0, 2, 0, 1, 0,
- 3, 0, 1, 0, 2, 0, 1, 0,
- 5, 0, 1, 0, 2, 0, 1, 0,
- 3, 0, 1, 0, 2, 0, 1, 0,
- 4, 0, 1, 0, 2, 0, 1, 0,
- 3, 0, 1, 0, 2, 0, 1, 0,
- 7, 0, 1, 0, 2, 0, 1, 0,
- 3, 0, 1, 0, 2, 0, 1, 0,
- 4, 0, 1, 0, 2, 0, 1, 0,
- 3, 0, 1, 0, 2, 0, 1, 0,
- 5, 0, 1, 0, 2, 0, 1, 0,
- 3, 0, 1, 0, 2, 0, 1, 0,
- 4, 0, 1, 0, 2, 0, 1, 0,
- 3, 0, 1, 0, 2, 0, 1, 0,
- 6, 0, 1, 0, 2, 0, 1, 0,
- 3, 0, 1, 0, 2, 0, 1, 0,
- 4, 0, 1, 0, 2, 0, 1, 0,
- 3, 0, 1, 0, 2, 0, 1, 0,
- 5, 0, 1, 0, 2, 0, 1, 0,
- 3, 0, 1, 0, 2, 0, 1, 0,
- 4, 0, 1, 0, 2, 0, 1, 0,
- 3, 0, 1, 0, 2, 0, 1, 0}
-
// Bswap64 returns its input with byte order reversed
// 0x0102030405060708 -> 0x0807060504030201
func Bswap64(x uint64) uint64 {
- c8 := uint64(0xff00ff00ff00ff00)
- a := (x & c8) >> 8
- b := (x &^ c8) << 8
+ c8 := uint64(0x00ff00ff00ff00ff)
+ a := x >> 8 & c8
+ b := (x & c8) << 8
x = a | b
- c16 := uint64(0xffff0000ffff0000)
- a = (x & c16) >> 16
- b = (x &^ c16) << 16
+ c16 := uint64(0x0000ffff0000ffff)
+ a = x >> 16 & c16
+ b = (x & c16) << 16
x = a | b
- c32 := uint64(0xffffffff00000000)
- a = (x & c32) >> 32
- b = (x &^ c32) << 32
+ c32 := uint64(0x00000000ffffffff)
+ a = x >> 32 & c32
+ b = (x & c32) << 32
x = a | b
return x
}
@@ -93,13 +102,13 @@ func Bswap64(x uint64) uint64 {
// Bswap32 returns its input with byte order reversed
// 0x01020304 -> 0x04030201
func Bswap32(x uint32) uint32 {
- c8 := uint32(0xff00ff00)
- a := (x & c8) >> 8
- b := (x &^ c8) << 8
+ c8 := uint32(0x00ff00ff)
+ a := x >> 8 & c8
+ b := (x & c8) << 8
x = a | b
- c16 := uint32(0xffff0000)
- a = (x & c16) >> 16
- b = (x &^ c16) << 16
+ c16 := uint32(0x0000ffff)
+ a = x >> 16 & c16
+ b = (x & c16) << 16
x = a | b
return x
}
diff --git a/src/runtime/internal/sys/intrinsics_test.go b/src/runtime/internal/sys/intrinsics_test.go
new file mode 100644
index 0000000000..097631bc1e
--- /dev/null
+++ b/src/runtime/internal/sys/intrinsics_test.go
@@ -0,0 +1,54 @@
+package sys_test
+
+import (
+ "runtime/internal/sys"
+ "testing"
+)
+
+func TestCtz64(t *testing.T) {
+ for i := uint(0); i <= 64; i++ {
+ x := uint64(5) << i
+ if got := sys.Ctz64(x); got != uint64(i) {
+ t.Errorf("Ctz64(%d)=%d, want %d", x, got, i)
+ }
+ }
+}
+func TestCtz32(t *testing.T) {
+ for i := uint(0); i <= 32; i++ {
+ x := uint32(5) << i
+ if got := sys.Ctz32(x); got != uint32(i) {
+ t.Errorf("Ctz32(%d)=%d, want %d", x, got, i)
+ }
+ }
+}
+func TestCtz16(t *testing.T) {
+ for i := uint(0); i <= 16; i++ {
+ x := uint16(5) << i
+ if got := sys.Ctz16(x); got != uint16(i) {
+ t.Errorf("Ctz16(%d)=%d, want %d", x, got, i)
+ }
+ }
+}
+func TestCtz8(t *testing.T) {
+ for i := uint(0); i <= 8; i++ {
+ x := uint8(5) << i
+ if got := sys.Ctz8(x); got != uint8(i) {
+ t.Errorf("Ctz8(%d)=%d, want %d", x, got, i)
+ }
+ }
+}
+
+func TestBswap64(t *testing.T) {
+ x := uint64(0x1122334455667788)
+ y := sys.Bswap64(x)
+ if y != 0x8877665544332211 {
+ t.Errorf("Bswap(%x)=%x, want 0x8877665544332211", x, y)
+ }
+}
+func TestBswap32(t *testing.T) {
+ x := uint32(0x11223344)
+ y := sys.Bswap32(x)
+ if y != 0x44332211 {
+ t.Errorf("Bswap(%x)=%x, want 0x44332211", x, y)
+ }
+}
diff --git a/src/runtime/internal/sys/zgoarch_386.go b/src/runtime/internal/sys/zgoarch_386.go
index 3ad244509d..3bcf83b8e3 100644
--- a/src/runtime/internal/sys/zgoarch_386.go
+++ b/src/runtime/internal/sys/zgoarch_386.go
@@ -2,7 +2,7 @@
package sys
-const TheGoarch = `386`
+const GOARCH = `386`
const Goarch386 = 1
const GoarchAmd64 = 0
diff --git a/src/runtime/internal/sys/zgoarch_amd64.go b/src/runtime/internal/sys/zgoarch_amd64.go
index 7c858e3f5d..699f191fba 100644
--- a/src/runtime/internal/sys/zgoarch_amd64.go
+++ b/src/runtime/internal/sys/zgoarch_amd64.go
@@ -2,7 +2,7 @@
package sys
-const TheGoarch = `amd64`
+const GOARCH = `amd64`
const Goarch386 = 0
const GoarchAmd64 = 1
diff --git a/src/runtime/internal/sys/zgoarch_amd64p32.go b/src/runtime/internal/sys/zgoarch_amd64p32.go
index 772031c090..cc2d658406 100644
--- a/src/runtime/internal/sys/zgoarch_amd64p32.go
+++ b/src/runtime/internal/sys/zgoarch_amd64p32.go
@@ -2,7 +2,7 @@
package sys
-const TheGoarch = `amd64p32`
+const GOARCH = `amd64p32`
const Goarch386 = 0
const GoarchAmd64 = 0
diff --git a/src/runtime/internal/sys/zgoarch_arm.go b/src/runtime/internal/sys/zgoarch_arm.go
index 276e8a869b..a5fd789f13 100644
--- a/src/runtime/internal/sys/zgoarch_arm.go
+++ b/src/runtime/internal/sys/zgoarch_arm.go
@@ -2,7 +2,7 @@
package sys
-const TheGoarch = `arm`
+const GOARCH = `arm`
const Goarch386 = 0
const GoarchAmd64 = 0
diff --git a/src/runtime/internal/sys/zgoarch_arm64.go b/src/runtime/internal/sys/zgoarch_arm64.go
index d124ec0343..084d2c7330 100644
--- a/src/runtime/internal/sys/zgoarch_arm64.go
+++ b/src/runtime/internal/sys/zgoarch_arm64.go
@@ -2,7 +2,7 @@
package sys
-const TheGoarch = `arm64`
+const GOARCH = `arm64`
const Goarch386 = 0
const GoarchAmd64 = 0
diff --git a/src/runtime/internal/sys/zgoarch_mips64.go b/src/runtime/internal/sys/zgoarch_mips64.go
index b4a97d6da9..2ad62bd68c 100644
--- a/src/runtime/internal/sys/zgoarch_mips64.go
+++ b/src/runtime/internal/sys/zgoarch_mips64.go
@@ -2,7 +2,7 @@
package sys
-const TheGoarch = `mips64`
+const GOARCH = `mips64`
const Goarch386 = 0
const GoarchAmd64 = 0
diff --git a/src/runtime/internal/sys/zgoarch_mips64le.go b/src/runtime/internal/sys/zgoarch_mips64le.go
index 3328a35bd2..047c8b425a 100644
--- a/src/runtime/internal/sys/zgoarch_mips64le.go
+++ b/src/runtime/internal/sys/zgoarch_mips64le.go
@@ -2,7 +2,7 @@
package sys
-const TheGoarch = `mips64le`
+const GOARCH = `mips64le`
const Goarch386 = 0
const GoarchAmd64 = 0
diff --git a/src/runtime/internal/sys/zgoarch_ppc64.go b/src/runtime/internal/sys/zgoarch_ppc64.go
index 06f78b2023..748b5b562c 100644
--- a/src/runtime/internal/sys/zgoarch_ppc64.go
+++ b/src/runtime/internal/sys/zgoarch_ppc64.go
@@ -2,7 +2,7 @@
package sys
-const TheGoarch = `ppc64`
+const GOARCH = `ppc64`
const Goarch386 = 0
const GoarchAmd64 = 0
diff --git a/src/runtime/internal/sys/zgoarch_ppc64le.go b/src/runtime/internal/sys/zgoarch_ppc64le.go
index 50b56dbe3f..d3dcba467d 100644
--- a/src/runtime/internal/sys/zgoarch_ppc64le.go
+++ b/src/runtime/internal/sys/zgoarch_ppc64le.go
@@ -2,7 +2,7 @@
package sys
-const TheGoarch = `ppc64le`
+const GOARCH = `ppc64le`
const Goarch386 = 0
const GoarchAmd64 = 0
diff --git a/src/runtime/internal/sys/zgoarch_s390x.go b/src/runtime/internal/sys/zgoarch_s390x.go
index ce85f20e0a..1ead5d573c 100644
--- a/src/runtime/internal/sys/zgoarch_s390x.go
+++ b/src/runtime/internal/sys/zgoarch_s390x.go
@@ -2,7 +2,7 @@
package sys
-const TheGoarch = `s390x`
+const GOARCH = `s390x`
const Goarch386 = 0
const GoarchAmd64 = 0
diff --git a/src/runtime/internal/sys/zgoos_android.go b/src/runtime/internal/sys/zgoos_android.go
index 03d91760ed..6503b15246 100644
--- a/src/runtime/internal/sys/zgoos_android.go
+++ b/src/runtime/internal/sys/zgoos_android.go
@@ -2,7 +2,7 @@
package sys
-const TheGoos = `android`
+const GOOS = `android`
const GoosAndroid = 1
const GoosDarwin = 0
diff --git a/src/runtime/internal/sys/zgoos_darwin.go b/src/runtime/internal/sys/zgoos_darwin.go
index eb2efeb7af..6a285984bd 100644
--- a/src/runtime/internal/sys/zgoos_darwin.go
+++ b/src/runtime/internal/sys/zgoos_darwin.go
@@ -2,7 +2,7 @@
package sys
-const TheGoos = `darwin`
+const GOOS = `darwin`
const GoosAndroid = 0
const GoosDarwin = 1
diff --git a/src/runtime/internal/sys/zgoos_dragonfly.go b/src/runtime/internal/sys/zgoos_dragonfly.go
index 403cf65311..886ac2698f 100644
--- a/src/runtime/internal/sys/zgoos_dragonfly.go
+++ b/src/runtime/internal/sys/zgoos_dragonfly.go
@@ -2,7 +2,7 @@
package sys
-const TheGoos = `dragonfly`
+const GOOS = `dragonfly`
const GoosAndroid = 0
const GoosDarwin = 0
diff --git a/src/runtime/internal/sys/zgoos_freebsd.go b/src/runtime/internal/sys/zgoos_freebsd.go
index 632d5db9db..0bf2403eab 100644
--- a/src/runtime/internal/sys/zgoos_freebsd.go
+++ b/src/runtime/internal/sys/zgoos_freebsd.go
@@ -2,7 +2,7 @@
package sys
-const TheGoos = `freebsd`
+const GOOS = `freebsd`
const GoosAndroid = 0
const GoosDarwin = 0
diff --git a/src/runtime/internal/sys/zgoos_linux.go b/src/runtime/internal/sys/zgoos_linux.go
index 2d43869a84..c8664db15d 100644
--- a/src/runtime/internal/sys/zgoos_linux.go
+++ b/src/runtime/internal/sys/zgoos_linux.go
@@ -4,7 +4,7 @@
package sys
-const TheGoos = `linux`
+const GOOS = `linux`
const GoosAndroid = 0
const GoosDarwin = 0
diff --git a/src/runtime/internal/sys/zgoos_nacl.go b/src/runtime/internal/sys/zgoos_nacl.go
index a56b6ef3c9..054122638a 100644
--- a/src/runtime/internal/sys/zgoos_nacl.go
+++ b/src/runtime/internal/sys/zgoos_nacl.go
@@ -2,7 +2,7 @@
package sys
-const TheGoos = `nacl`
+const GOOS = `nacl`
const GoosAndroid = 0
const GoosDarwin = 0
diff --git a/src/runtime/internal/sys/zgoos_netbsd.go b/src/runtime/internal/sys/zgoos_netbsd.go
index 46fd0a7cd5..5c509a1250 100644
--- a/src/runtime/internal/sys/zgoos_netbsd.go
+++ b/src/runtime/internal/sys/zgoos_netbsd.go
@@ -2,7 +2,7 @@
package sys
-const TheGoos = `netbsd`
+const GOOS = `netbsd`
const GoosAndroid = 0
const GoosDarwin = 0
diff --git a/src/runtime/internal/sys/zgoos_openbsd.go b/src/runtime/internal/sys/zgoos_openbsd.go
index 7ee650afbb..dc43157d49 100644
--- a/src/runtime/internal/sys/zgoos_openbsd.go
+++ b/src/runtime/internal/sys/zgoos_openbsd.go
@@ -2,7 +2,7 @@
package sys
-const TheGoos = `openbsd`
+const GOOS = `openbsd`
const GoosAndroid = 0
const GoosDarwin = 0
diff --git a/src/runtime/internal/sys/zgoos_plan9.go b/src/runtime/internal/sys/zgoos_plan9.go
index 162e7f6260..4b0934f77a 100644
--- a/src/runtime/internal/sys/zgoos_plan9.go
+++ b/src/runtime/internal/sys/zgoos_plan9.go
@@ -2,7 +2,7 @@
package sys
-const TheGoos = `plan9`
+const GOOS = `plan9`
const GoosAndroid = 0
const GoosDarwin = 0
diff --git a/src/runtime/internal/sys/zgoos_solaris.go b/src/runtime/internal/sys/zgoos_solaris.go
index b2a8f98504..42511a36ad 100644
--- a/src/runtime/internal/sys/zgoos_solaris.go
+++ b/src/runtime/internal/sys/zgoos_solaris.go
@@ -2,7 +2,7 @@
package sys
-const TheGoos = `solaris`
+const GOOS = `solaris`
const GoosAndroid = 0
const GoosDarwin = 0
diff --git a/src/runtime/internal/sys/zgoos_windows.go b/src/runtime/internal/sys/zgoos_windows.go
index 817ec79e4c..d77f62c396 100644
--- a/src/runtime/internal/sys/zgoos_windows.go
+++ b/src/runtime/internal/sys/zgoos_windows.go
@@ -2,7 +2,7 @@
package sys
-const TheGoos = `windows`
+const GOOS = `windows`
const GoosAndroid = 0
const GoosDarwin = 0
diff --git a/src/runtime/lfstack.go b/src/runtime/lfstack.go
index ea640eb12f..db54ecb05e 100644
--- a/src/runtime/lfstack.go
+++ b/src/runtime/lfstack.go
@@ -3,6 +3,9 @@
// license that can be found in the LICENSE file.
// Lock-free stack.
+// Initialize head to 0, compare with 0 to test for emptiness.
+// The stack does not keep pointers to nodes,
+// so they can be garbage collected if there are no other pointers to nodes.
// The following code runs only on g0 stack.
package runtime
@@ -15,7 +18,7 @@ import (
func lfstackpush(head *uint64, node *lfnode) {
node.pushcnt++
new := lfstackPack(node, node.pushcnt)
- if node1, _ := lfstackUnpack(new); node1 != node {
+ if node1 := lfstackUnpack(new); node1 != node {
print("runtime: lfstackpush invalid packing: node=", node, " cnt=", hex(node.pushcnt), " packed=", hex(new), " -> node=", node1, "\n")
throw("lfstackpush")
}
@@ -34,7 +37,7 @@ func lfstackpop(head *uint64) unsafe.Pointer {
if old == 0 {
return nil
}
- node, _ := lfstackUnpack(old)
+ node := lfstackUnpack(old)
next := atomic.Load64(&node.next)
if atomic.Cas64(head, old, next) {
return unsafe.Pointer(node)
diff --git a/src/runtime/lfstack_32bit.go b/src/runtime/lfstack_32bit.go
index 36811c1e47..2f59e0212e 100644
--- a/src/runtime/lfstack_32bit.go
+++ b/src/runtime/lfstack_32bit.go
@@ -14,8 +14,6 @@ func lfstackPack(node *lfnode, cnt uintptr) uint64 {
return uint64(uintptr(unsafe.Pointer(node)))<<32 | uint64(cnt)
}
-func lfstackUnpack(val uint64) (node *lfnode, cnt uintptr) {
- node = (*lfnode)(unsafe.Pointer(uintptr(val >> 32)))
- cnt = uintptr(val)
- return
+func lfstackUnpack(val uint64) *lfnode {
+ return (*lfnode)(unsafe.Pointer(uintptr(val >> 32)))
}
diff --git a/src/runtime/lfstack_64bit.go b/src/runtime/lfstack_64bit.go
new file mode 100644
index 0000000000..5367f08c56
--- /dev/null
+++ b/src/runtime/lfstack_64bit.go
@@ -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.
+
+// +build amd64 arm64 mips64 mips64le ppc64 ppc64le s390x
+
+package runtime
+
+import "unsafe"
+
+const (
+ // addrBits is the number of bits needed to represent a virtual address.
+ //
+ // In Linux the user address space for each architecture is limited as
+ // follows (taken from the processor.h file for the architecture):
+ //
+ // Architecture Name Maximum Value (exclusive)
+ // ---------------------------------------------------------------------
+ // arm64 TASK_SIZE_64 Depends on configuration.
+ // ppc64{,le} TASK_SIZE_USER64 0x400000000000UL (46 bit addresses)
+ // mips64{,le} TASK_SIZE64 0x010000000000UL (40 bit addresses)
+ // s390x TASK_SIZE 0x020000000000UL (41 bit addresses)
+ //
+ // These values may increase over time.
+ //
+ // On AMD64, virtual addresses are 48-bit numbers sign extended to 64.
+ // We shift the address left 16 to eliminate the sign extended part and make
+ // room in the bottom for the count.
+ addrBits = 48
+
+ // In addition to the 16 bits taken from the top, we can take 3 from the
+ // bottom, because node must be pointer-aligned, giving a total of 19 bits
+ // of count.
+ cntBits = 64 - addrBits + 3
+)
+
+func lfstackPack(node *lfnode, cnt uintptr) uint64 {
+ return uint64(uintptr(unsafe.Pointer(node)))<<(64-addrBits) | uint64(cnt&(1<<cntBits-1))
+}
+
+func lfstackUnpack(val uint64) *lfnode {
+ if GOARCH == "amd64" {
+ // amd64 systems can place the stack above the VA hole, so we need to sign extend
+ // val before unpacking.
+ return (*lfnode)(unsafe.Pointer(uintptr(int64(val) >> cntBits << 3)))
+ }
+ return (*lfnode)(unsafe.Pointer(uintptr(val >> cntBits << 3)))
+}
diff --git a/src/runtime/lfstack_amd64.go b/src/runtime/lfstack_amd64.go
deleted file mode 100644
index 0a71455c6b..0000000000
--- a/src/runtime/lfstack_amd64.go
+++ /dev/null
@@ -1,24 +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 runtime
-
-import "unsafe"
-
-// On AMD64, virtual addresses are 48-bit numbers sign extended to 64.
-// We shift the address left 16 to eliminate the sign extended part and make
-// room in the bottom for the count.
-// In addition to the 16 bits taken from the top, we can take 3 from the
-// bottom, because node must be pointer-aligned, giving a total of 19 bits
-// of count.
-
-func lfstackPack(node *lfnode, cnt uintptr) uint64 {
- return uint64(uintptr(unsafe.Pointer(node)))<<16 | uint64(cnt&(1<<19-1))
-}
-
-func lfstackUnpack(val uint64) (node *lfnode, cnt uintptr) {
- node = (*lfnode)(unsafe.Pointer(uintptr(int64(val) >> 19 << 3)))
- cnt = uintptr(val & (1<<19 - 1))
- return
-}
diff --git a/src/runtime/lfstack_darwin_arm64.go b/src/runtime/lfstack_darwin_arm64.go
deleted file mode 100644
index f48d76382b..0000000000
--- a/src/runtime/lfstack_darwin_arm64.go
+++ /dev/null
@@ -1,25 +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 runtime
-
-import "unsafe"
-
-// In addition to the 16 bits taken from the top, we can take 3 from the
-// bottom, because node must be pointer-aligned, giving a total of 19 bits
-// of count.
-const (
- addrBits = 48
- cntBits = 64 - addrBits + 3
-)
-
-func lfstackPack(node *lfnode, cnt uintptr) uint64 {
- return uint64(uintptr(unsafe.Pointer(node)))<<(64-addrBits) | uint64(cnt&(1<<cntBits-1))
-}
-
-func lfstackUnpack(val uint64) (node *lfnode, cnt uintptr) {
- node = (*lfnode)(unsafe.Pointer(uintptr(val >> cntBits << 3)))
- cnt = uintptr(val & (1<<cntBits - 1))
- return
-}
diff --git a/src/runtime/lfstack_linux_arm64.go b/src/runtime/lfstack_linux_arm64.go
deleted file mode 100644
index f48d76382b..0000000000
--- a/src/runtime/lfstack_linux_arm64.go
+++ /dev/null
@@ -1,25 +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 runtime
-
-import "unsafe"
-
-// In addition to the 16 bits taken from the top, we can take 3 from the
-// bottom, because node must be pointer-aligned, giving a total of 19 bits
-// of count.
-const (
- addrBits = 48
- cntBits = 64 - addrBits + 3
-)
-
-func lfstackPack(node *lfnode, cnt uintptr) uint64 {
- return uint64(uintptr(unsafe.Pointer(node)))<<(64-addrBits) | uint64(cnt&(1<<cntBits-1))
-}
-
-func lfstackUnpack(val uint64) (node *lfnode, cnt uintptr) {
- node = (*lfnode)(unsafe.Pointer(uintptr(val >> cntBits << 3)))
- cnt = uintptr(val & (1<<cntBits - 1))
- return
-}
diff --git a/src/runtime/lfstack_linux_mips64x.go b/src/runtime/lfstack_linux_mips64x.go
deleted file mode 100644
index 7ff95f77ae..0000000000
--- a/src/runtime/lfstack_linux_mips64x.go
+++ /dev/null
@@ -1,32 +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.
-
-// +build mips64 mips64le
-// +build linux
-
-package runtime
-
-import "unsafe"
-
-// On mips64, Linux limits the user address space to 40 bits (see
-// TASK_SIZE64 in the Linux kernel). This has grown over time,
-// so here we allow 48 bit addresses.
-//
-// In addition to the 16 bits taken from the top, we can take 3 from the
-// bottom, because node must be pointer-aligned, giving a total of 19 bits
-// of count.
-const (
- addrBits = 48
- cntBits = 64 - addrBits + 3
-)
-
-func lfstackPack(node *lfnode, cnt uintptr) uint64 {
- return uint64(uintptr(unsafe.Pointer(node)))<<(64-addrBits) | uint64(cnt&(1<<cntBits-1))
-}
-
-func lfstackUnpack(val uint64) (node *lfnode, cnt uintptr) {
- node = (*lfnode)(unsafe.Pointer(uintptr(val >> cntBits << 3)))
- cnt = uintptr(val & (1<<cntBits - 1))
- return
-}
diff --git a/src/runtime/lfstack_linux_ppc64x.go b/src/runtime/lfstack_linux_ppc64x.go
deleted file mode 100644
index 83b7cf4f58..0000000000
--- a/src/runtime/lfstack_linux_ppc64x.go
+++ /dev/null
@@ -1,32 +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.
-
-// +build ppc64 ppc64le
-// +build linux
-
-package runtime
-
-import "unsafe"
-
-// On ppc64, Linux limits the user address space to 46 bits (see
-// TASK_SIZE_USER64 in the Linux kernel). This has grown over time,
-// so here we allow 48 bit addresses.
-//
-// In addition to the 16 bits taken from the top, we can take 3 from the
-// bottom, because node must be pointer-aligned, giving a total of 19 bits
-// of count.
-const (
- addrBits = 48
- cntBits = 64 - addrBits + 3
-)
-
-func lfstackPack(node *lfnode, cnt uintptr) uint64 {
- return uint64(uintptr(unsafe.Pointer(node)))<<(64-addrBits) | uint64(cnt&(1<<cntBits-1))
-}
-
-func lfstackUnpack(val uint64) (node *lfnode, cnt uintptr) {
- node = (*lfnode)(unsafe.Pointer(uintptr(val >> cntBits << 3)))
- cnt = uintptr(val & (1<<cntBits - 1))
- return
-}
diff --git a/src/runtime/lock_futex.go b/src/runtime/lock_futex.go
index d28fd92720..073136abd0 100644
--- a/src/runtime/lock_futex.go
+++ b/src/runtime/lock_futex.go
@@ -13,13 +13,13 @@ import (
// This implementation depends on OS-specific implementations of
//
-// runtime·futexsleep(uint32 *addr, uint32 val, int64 ns)
+// futexsleep(addr *uint32, val uint32, ns int64)
// Atomically,
-// if(*addr == val) sleep
+// if *addr == val { sleep }
// Might be woken up spuriously; that's allowed.
// Don't sleep longer than ns; ns < 0 means forever.
//
-// runtime·futexwakeup(uint32 *addr, uint32 cnt)
+// futexwakeup(addr *uint32, cnt uint32)
// If any procs are sleeping on addr, wake up at most cnt.
const (
diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go
index 31335dae80..6fe4656603 100644
--- a/src/runtime/malloc.go
+++ b/src/runtime/malloc.go
@@ -87,9 +87,6 @@ import (
const (
debugMalloc = false
- flagNoScan = _FlagNoScan
- flagNoZero = _FlagNoZero
-
maxTinySize = _TinySize
tinySizeClass = _TinySizeClass
maxSmallSize = _MaxSmallSize
@@ -490,12 +487,6 @@ func (h *mheap) sysAlloc(n uintptr) unsafe.Pointer {
// base address for all 0-byte allocations
var zerobase uintptr
-const (
- // flags to malloc
- _FlagNoScan = 1 << 0 // GC doesn't have to scan object
- _FlagNoZero = 1 << 1 // don't zero memory
-)
-
// nextFreeFast returns the next free object if one is quickly available.
// Otherwise it returns 0.
func (c *mcache) nextFreeFast(sizeclass int8) gclinkptr {
@@ -564,7 +555,7 @@ func (c *mcache) nextFree(sizeclass int8) (v gclinkptr, shouldhelpgc bool) {
// Allocate an object of size bytes.
// Small objects are allocated from the per-P cache's free lists.
// Large objects (> 32 kB) are allocated straight from the heap.
-func mallocgc(size uintptr, typ *_type, flags uint32) unsafe.Pointer {
+func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
if gcphase == _GCmarktermination {
throw("mallocgc called with gcphase == _GCmarktermination")
}
@@ -573,10 +564,6 @@ func mallocgc(size uintptr, typ *_type, flags uint32) unsafe.Pointer {
return unsafe.Pointer(&zerobase)
}
- if flags&flagNoScan == 0 && typ == nil {
- throw("malloc missing type")
- }
-
if debug.sbrk != 0 {
align := uintptr(16)
if typ != nil {
@@ -620,14 +607,15 @@ func mallocgc(size uintptr, typ *_type, flags uint32) unsafe.Pointer {
dataSize := size
c := gomcache()
var x unsafe.Pointer
+ noscan := typ == nil || typ.kind&kindNoPointers != 0
if size <= maxSmallSize {
- if flags&flagNoScan != 0 && size < maxTinySize {
+ if noscan && size < maxTinySize {
// Tiny allocator.
//
// Tiny allocator combines several tiny allocation requests
// into a single memory block. The resulting memory block
// is freed when all subobjects are unreachable. The subobjects
- // must be FlagNoScan (don't have pointers), this ensures that
+ // must be noscan (don't have pointers), this ensures that
// the amount of potentially wasted memory is bounded.
//
// Size of the memory block used for combining (maxTinySize) is tunable.
@@ -699,7 +687,7 @@ func mallocgc(size uintptr, typ *_type, flags uint32) unsafe.Pointer {
v, shouldhelpgc = c.nextFree(sizeclass)
}
x = unsafe.Pointer(v)
- if flags&flagNoZero == 0 {
+ if needzero {
memclr(unsafe.Pointer(v), size)
// TODO:(rlh) Only clear if object is not known to be zeroed.
}
@@ -708,14 +696,15 @@ func mallocgc(size uintptr, typ *_type, flags uint32) unsafe.Pointer {
var s *mspan
shouldhelpgc = true
systemstack(func() {
- s = largeAlloc(size, flags)
+ s = largeAlloc(size, needzero)
})
s.freeindex = 1
x = unsafe.Pointer(s.base())
size = s.elemsize
}
- if flags&flagNoScan != 0 {
+ var scanSize uintptr
+ if noscan {
heapBitsSetTypeNoScan(uintptr(x), size)
} else {
// If allocating a defer+arg block, now that we've picked a malloc size
@@ -733,11 +722,12 @@ func mallocgc(size uintptr, typ *_type, flags uint32) unsafe.Pointer {
// pointers, GC has to scan to the last
// element.
if typ.ptrdata != 0 {
- c.local_scan += dataSize - typ.size + typ.ptrdata
+ scanSize = dataSize - typ.size + typ.ptrdata
}
} else {
- c.local_scan += typ.ptrdata
+ scanSize = typ.ptrdata
}
+ c.local_scan += scanSize
// Ensure that the stores above that initialize x to
// type-safe memory and set the heap bits occur before
@@ -748,14 +738,12 @@ func mallocgc(size uintptr, typ *_type, flags uint32) unsafe.Pointer {
publicationBarrier()
}
- // GCmarkterminate allocates black
+ // Allocate black during GC.
// All slots hold nil so no scanning is needed.
// This may be racing with GC so do it atomically if there can be
// a race marking the bit.
- if gcphase == _GCmarktermination || gcBlackenPromptly {
- systemstack(func() {
- gcmarknewobject_m(uintptr(x), size)
- })
+ if gcphase != _GCoff {
+ gcmarknewobject(uintptr(x), size, scanSize)
}
// The object x is about to be reused but tracefree and msanfree
@@ -813,7 +801,7 @@ func mallocgc(size uintptr, typ *_type, flags uint32) unsafe.Pointer {
return x
}
-func largeAlloc(size uintptr, flag uint32) *mspan {
+func largeAlloc(size uintptr, needzero bool) *mspan {
// print("largeAlloc size=", size, "\n")
if size+_PageSize < size {
@@ -829,7 +817,7 @@ func largeAlloc(size uintptr, flag uint32) *mspan {
// pays the debt down to npage pages.
deductSweepCredit(npages*_PageSize, npages)
- s := mheap_.alloc(npages, 0, true, flag&_FlagNoZero == 0)
+ s := mheap_.alloc(npages, 0, true, needzero)
if s == nil {
throw("out of memory")
}
@@ -840,11 +828,7 @@ func largeAlloc(size uintptr, flag uint32) *mspan {
// implementation of new builtin
func newobject(typ *_type) unsafe.Pointer {
- flags := uint32(0)
- if typ.kind&kindNoPointers != 0 {
- flags |= flagNoScan
- }
- return mallocgc(typ.size, typ, flags)
+ return mallocgc(typ.size, typ, true)
}
//go:linkname reflect_unsafe_New reflect.unsafe_New
@@ -852,29 +836,19 @@ func reflect_unsafe_New(typ *_type) unsafe.Pointer {
return newobject(typ)
}
-// implementation of make builtin for slices
-func newarray(typ *_type, n uintptr) unsafe.Pointer {
- flags := uint32(0)
- if typ.kind&kindNoPointers != 0 {
- flags |= flagNoScan
+// newarray allocates an array of n elements of type typ.
+func newarray(typ *_type, n int) unsafe.Pointer {
+ if n < 0 || uintptr(n) > maxSliceCap(typ.size) {
+ panic(plainError("runtime: allocation size out of range"))
}
- if int(n) < 0 || (typ.size > 0 && n > _MaxMem/typ.size) {
- panic("runtime: allocation size out of range")
- }
- return mallocgc(typ.size*n, typ, flags)
+ return mallocgc(typ.size*uintptr(n), typ, true)
}
//go:linkname reflect_unsafe_NewArray reflect.unsafe_NewArray
-func reflect_unsafe_NewArray(typ *_type, n uintptr) unsafe.Pointer {
+func reflect_unsafe_NewArray(typ *_type, n int) unsafe.Pointer {
return newarray(typ, n)
}
-// rawmem returns a chunk of pointerless memory. It is
-// not zeroed.
-func rawmem(size uintptr) unsafe.Pointer {
- return mallocgc(size, nil, flagNoScan|flagNoZero)
-}
-
func profilealloc(mp *m, x unsafe.Pointer, size uintptr) {
mp.mcache.next_sample = nextSample()
mProf_Malloc(x, size)
diff --git a/src/runtime/map_test.go b/src/runtime/map_test.go
index 9d2894cb6f..496f8e8868 100644
--- a/src/runtime/map_test.go
+++ b/src/runtime/map_test.go
@@ -317,6 +317,22 @@ func TestBigItems(t *testing.T) {
}
}
+func TestMapHugeZero(t *testing.T) {
+ type T [4000]byte
+ m := map[int]T{}
+ x := m[0]
+ if x != (T{}) {
+ t.Errorf("map value not zero")
+ }
+ y, ok := m[0]
+ if ok {
+ t.Errorf("map value should be missing")
+ }
+ if y != (T{}) {
+ t.Errorf("map value not zero")
+ }
+}
+
type empty struct {
}
diff --git a/src/runtime/mbarrier.go b/src/runtime/mbarrier.go
index f03bf18ebc..637d9b886a 100644
--- a/src/runtime/mbarrier.go
+++ b/src/runtime/mbarrier.go
@@ -87,6 +87,17 @@ import (
// frames that have potentially been active since the concurrent scan,
// so it depends on write barriers to track changes to pointers in
// stack frames that have not been active.
+//
+//
+// Global writes:
+//
+// The Go garbage collector requires write barriers when heap pointers
+// are stored in globals. Many garbage collectors ignore writes to
+// globals and instead pick up global -> heap pointers during
+// termination. This increases pause time, so we instead rely on write
+// barriers for writes to globals so that we don't have to rescan
+// global during mark termination.
+//
//go:nowritebarrierrec
func gcmarkwb_m(slot *uintptr, ptr uintptr) {
if writeBarrier.needed {
@@ -185,7 +196,7 @@ func reflect_typedmemmovepartial(typ *_type, dst, src unsafe.Pointer, off, size
if writeBarrier.cgo {
cgoCheckMemmove(typ, dst, src, off, size)
}
- if !writeBarrier.needed || typ.kind&kindNoPointers != 0 || size < sys.PtrSize || !inheap(uintptr(dst)) {
+ if !writeBarrier.needed || typ.kind&kindNoPointers != 0 || size < sys.PtrSize {
return
}
@@ -201,11 +212,11 @@ func reflect_typedmemmovepartial(typ *_type, dst, src unsafe.Pointer, off, size
// values have just been copied to frame, starting at retoffset
// and continuing to framesize. The entire frame (not just the return
// values) is described by typ. Because the copy has already
-// happened, we call writebarrierptr_nostore, and we must be careful
-// not to be preempted before the write barriers have been run.
+// happened, we call writebarrierptr_nostore, and this is nosplit so
+// the copy and write barrier appear atomic to GC.
//go:nosplit
func callwritebarrier(typ *_type, frame unsafe.Pointer, framesize, retoffset uintptr) {
- if !writeBarrier.needed || typ == nil || typ.kind&kindNoPointers != 0 || framesize-retoffset < sys.PtrSize || !inheap(uintptr(frame)) {
+ if !writeBarrier.needed || typ == nil || typ.kind&kindNoPointers != 0 || framesize-retoffset < sys.PtrSize {
return
}
heapBitsBulkBarrier(uintptr(add(frame, retoffset)), framesize-retoffset)
diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go
index b342de600e..af89577703 100644
--- a/src/runtime/mbitmap.go
+++ b/src/runtime/mbitmap.go
@@ -88,6 +88,7 @@ const (
// addb returns the byte pointer p+n.
//go:nowritebarrier
+//go:nosplit
func addb(p *byte, n uintptr) *byte {
// Note: wrote out full expression instead of calling add(p, n)
// to reduce the number of temporaries generated by the
@@ -99,6 +100,7 @@ func addb(p *byte, n uintptr) *byte {
// subtractb is typically used when traversing the pointer tables referred to by hbits
// which are arranged in reverse order.
//go:nowritebarrier
+//go:nosplit
func subtractb(p *byte, n uintptr) *byte {
// Note: wrote out full expression instead of calling add(p, -n)
// to reduce the number of temporaries generated by the
@@ -108,6 +110,7 @@ func subtractb(p *byte, n uintptr) *byte {
// add1 returns the byte pointer p+1.
//go:nowritebarrier
+//go:nosplit
func add1(p *byte) *byte {
// Note: wrote out full expression instead of calling addb(p, 1)
// to reduce the number of temporaries generated by the
@@ -596,10 +599,10 @@ func (h heapBits) setCheckmarked(size uintptr) {
// heapBitsBulkBarrier executes writebarrierptr_nostore
// for every pointer slot in the memory range [p, p+size),
-// using the heap bitmap to locate those pointer slots.
+// using the heap, data, or BSS bitmap to locate those pointer slots.
// This executes the write barriers necessary after a memmove.
// Both p and size must be pointer-aligned.
-// The range [p, p+size) must lie within a single allocation.
+// The range [p, p+size) must lie within a single object.
//
// Callers should call heapBitsBulkBarrier immediately after
// calling memmove(p, src, size). This function is marked nosplit
@@ -643,6 +646,22 @@ func heapBitsBulkBarrier(p, size uintptr) {
systemstack(func() {
gcUnwindBarriers(gp, p)
})
+ return
+ }
+
+ // If p is a global, use the data or BSS bitmaps to
+ // execute write barriers.
+ for datap := &firstmoduledata; datap != nil; datap = datap.next {
+ if datap.data <= p && p < datap.edata {
+ bulkBarrierBitmap(p, size, p-datap.data, datap.gcdatamask.bytedata)
+ return
+ }
+ }
+ for datap := &firstmoduledata; datap != nil; datap = datap.next {
+ if datap.bss <= p && p < datap.ebss {
+ bulkBarrierBitmap(p, size, p-datap.bss, datap.gcbssmask.bytedata)
+ return
+ }
}
return
}
@@ -657,6 +676,36 @@ func heapBitsBulkBarrier(p, size uintptr) {
}
}
+// bulkBarrierBitmap executes write barriers for [p, p+size) using a
+// 1-bit pointer bitmap. p is assumed to start maskOffset bytes into
+// the data covered by the bitmap in bits.
+//
+// This is used by heapBitsBulkBarrier for writes to data and BSS.
+//
+//go:nosplit
+func bulkBarrierBitmap(p, size, maskOffset uintptr, bits *uint8) {
+ word := maskOffset / sys.PtrSize
+ bits = addb(bits, word/8)
+ mask := uint8(1) << (word % 8)
+
+ for i := uintptr(0); i < size; i += sys.PtrSize {
+ if mask == 0 {
+ bits = addb(bits, 1)
+ if *bits == 0 {
+ // Skip 8 words.
+ i += 7 * sys.PtrSize
+ continue
+ }
+ mask = 1
+ }
+ if *bits&mask != 0 {
+ x := (*uintptr)(unsafe.Pointer(p + i))
+ writebarrierptr_nostore(x, *x)
+ }
+ mask <<= 1
+ }
+}
+
// typeBitsBulkBarrier executes writebarrierptr_nostore
// for every pointer slot in the memory range [p, p+size),
// using the type bitmap to locate those pointer slots.
@@ -676,11 +725,11 @@ func typeBitsBulkBarrier(typ *_type, p, size uintptr) {
throw("runtime: typeBitsBulkBarrier without type")
}
if typ.size != size {
- println("runtime: typeBitsBulkBarrier with type ", typ._string, " of size ", typ.size, " but memory size", size)
+ println("runtime: typeBitsBulkBarrier with type ", typ.string(), " of size ", typ.size, " but memory size", size)
throw("runtime: invalid typeBitsBulkBarrier")
}
if typ.kind&kindGCProg != 0 {
- println("runtime: typeBitsBulkBarrier with type ", typ._string, " with GC prog")
+ println("runtime: typeBitsBulkBarrier with type ", typ.string(), " with GC prog")
throw("runtime: invalid typeBitsBulkBarrier")
}
if !writeBarrier.needed {
@@ -1128,7 +1177,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
}
if nw == 0 {
// No pointers! Caller was supposed to check.
- println("runtime: invalid type ", typ._string)
+ println("runtime: invalid type ", typ.string())
throw("heapBitsSetType: called with non-pointer type")
return
}
@@ -1314,7 +1363,7 @@ Phase4:
if doubleCheck {
end := heapBitsForAddr(x + size)
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)
+ 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")
h0 := heapBitsForAddr(x)
@@ -1350,7 +1399,7 @@ Phase4:
}
}
if have != want {
- println("mismatch writing bits for", typ._string, "x", dataSize/typ.size)
+ 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")
diff --git a/src/runtime/mem_linux.go b/src/runtime/mem_linux.go
index 1ee13bd7e6..61fdcee543 100644
--- a/src/runtime/mem_linux.go
+++ b/src/runtime/mem_linux.go
@@ -132,6 +132,13 @@ func sysUnused(v unsafe.Pointer, n uintptr) {
}
}
+ if uintptr(v)&(sys.PhysPageSize-1) != 0 || n&(sys.PhysPageSize-1) != 0 {
+ // madvise will round this to any physical page
+ // *covered* by this range, so an unaligned madvise
+ // will release more memory than intended.
+ throw("unaligned sysUnused")
+ }
+
madvise(v, n, _MADV_DONTNEED)
}
diff --git a/src/runtime/memclr_s390x.s b/src/runtime/memclr_s390x.s
new file mode 100644
index 0000000000..86eafec0a9
--- /dev/null
+++ b/src/runtime/memclr_s390x.s
@@ -0,0 +1,122 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+// void runtime·memclr(void*, uintptr)
+TEXT runtime·memclr(SB),NOSPLIT|NOFRAME,$0-16
+ MOVD ptr+0(FP), R4
+ MOVD n+8(FP), R5
+
+start:
+ CMPBLE R5, $3, clear0to3
+ CMPBLE R5, $7, clear4to7
+ CMPBLE R5, $11, clear8to11
+ CMPBLE R5, $15, clear12to15
+ CMP R5, $32
+ BGE clearmt32
+ MOVD R0, 0(R4)
+ MOVD R0, 8(R4)
+ ADD $16, R4
+ SUB $16, R5
+ BR start
+
+clear0to3:
+ CMPBEQ R5, $0, done
+ CMPBNE R5, $1, clear2
+ MOVB R0, 0(R4)
+ RET
+clear2:
+ CMPBNE R5, $2, clear3
+ MOVH R0, 0(R4)
+ RET
+clear3:
+ MOVH R0, 0(R4)
+ MOVB R0, 2(R4)
+ RET
+
+clear4to7:
+ CMPBNE R5, $4, clear5
+ MOVW R0, 0(R4)
+ RET
+clear5:
+ CMPBNE R5, $5, clear6
+ MOVW R0, 0(R4)
+ MOVB R0, 4(R4)
+ RET
+clear6:
+ CMPBNE R5, $6, clear7
+ MOVW R0, 0(R4)
+ MOVH R0, 4(R4)
+ RET
+clear7:
+ MOVW R0, 0(R4)
+ MOVH R0, 4(R4)
+ MOVB R0, 6(R4)
+ RET
+
+clear8to11:
+ CMPBNE R5, $8, clear9
+ MOVD R0, 0(R4)
+ RET
+clear9:
+ CMPBNE R5, $9, clear10
+ MOVD R0, 0(R4)
+ MOVB R0, 8(R4)
+ RET
+clear10:
+ CMPBNE R5, $10, clear11
+ MOVD R0, 0(R4)
+ MOVH R0, 8(R4)
+ RET
+clear11:
+ MOVD R0, 0(R4)
+ MOVH R0, 8(R4)
+ MOVB R0, 10(R4)
+ RET
+
+clear12to15:
+ CMPBNE R5, $12, clear13
+ MOVD R0, 0(R4)
+ MOVW R0, 8(R4)
+ RET
+clear13:
+ CMPBNE R5, $13, clear14
+ MOVD R0, 0(R4)
+ MOVW R0, 8(R4)
+ MOVB R0, 12(R4)
+ RET
+clear14:
+ CMPBNE R5, $14, clear15
+ MOVD R0, 0(R4)
+ MOVW R0, 8(R4)
+ MOVH R0, 12(R4)
+ RET
+clear15:
+ MOVD R0, 0(R4)
+ MOVW R0, 8(R4)
+ MOVH R0, 12(R4)
+ MOVB R0, 14(R4)
+ RET
+
+clearmt32:
+ CMP R5, $256
+ BLT clearlt256
+ XC $256, 0(R4), 0(R4)
+ ADD $256, R4
+ ADD $-256, R5
+ BR clearmt32
+clearlt256:
+ CMPBEQ R5, $0, done
+ ADD $-1, R5
+ EXRL $runtime·memclr_s390x_exrl_xc(SB), R5
+done:
+ RET
+
+// DO NOT CALL - target for exrl (execute relative long) instruction.
+TEXT runtime·memclr_s390x_exrl_xc(SB),NOSPLIT|NOFRAME,$0-0
+ XC $1, 0(R4), 0(R4)
+ MOVD R0, 0(R0)
+ RET
+
diff --git a/src/runtime/memmove_ppc64x.s b/src/runtime/memmove_ppc64x.s
index ea73b455b4..26dabd9e69 100644
--- a/src/runtime/memmove_ppc64x.s
+++ b/src/runtime/memmove_ppc64x.s
@@ -11,78 +11,109 @@ TEXT runtime·memmove(SB), NOSPLIT|NOFRAME, $0-24
MOVD to+0(FP), R3
MOVD from+8(FP), R4
MOVD n+16(FP), R5
- CMP R5, $0
- BNE check
- RET
+ // Determine if there are doublewords to
+ // copy so a more efficient move can be done
check:
- ANDCC $7, R5, R7 // R7 is the number of bytes to copy and CR0[EQ] is set if there are none.
- SRAD $3, R5, R6 // R6 is the number of words to copy
- CMP R6, $0, CR1 // CR1[EQ] is set if there are no words to copy.
+ ANDCC $7, R5, R7 // R7: bytes to copy
+ SRAD $3, R5, R6 // R6: double words to copy
+ CMP R6, $0, CR1 // CR1[EQ] set if no double words to copy
+
+ // Determine overlap by subtracting dest - src and comparing against the
+ // length. The catches the cases where src and dest are in different types
+ // of storage such as stack and static to avoid doing backward move when not
+ // necessary.
- CMP R3, R4, CR2
- BC 12, 9, backward // I think you should be able to write this as "BGT CR2, backward"
+ SUB R4, R3, R8 // dest - src
+ CMPU R8, R5, CR2 // < len?
+ BC 12, 8, backward // BLT CR2 backward
- // Copying forward proceeds by copying R6 words then copying R7 bytes.
- // R3 and R4 are advanced as we copy. Because PPC64 lacks post-increment
- // load/store, R3 and R4 point before the bytes that are to be copied.
+ // Copying forward if no overlap.
BC 12, 6, noforwardlarge // "BEQ CR1, noforwardlarge"
+ MOVD R6,CTR // R6 = number of double words
+ SRADCC $2,R6,R8 // 32 byte chunks?
+ BNE forward32setup //
- MOVD R6, CTR
+ // Move double words
- SUB $8, R3
- SUB $8, R4
+forward8:
+ MOVD 0(R4), R8 // double word
+ ADD $8,R4
+ MOVD R8, 0(R3) //
+ ADD $8,R3
+ BC 16, 0, forward8
+ BR noforwardlarge // handle remainder
-forwardlargeloop:
- MOVDU 8(R4), R8
- MOVDU R8, 8(R3)
- BC 16, 0, forwardlargeloop // "BDNZ"
+ // Prepare for moves of 32 bytes at a time.
- ADD $8, R3
- ADD $8, R4
+forward32setup:
+ DCBTST (R3) // prepare data cache
+ DCBT (R4)
+ MOVD R8, CTR // double work count
+
+forward32:
+ MOVD 0(R4), R8 // load 4 double words
+ MOVD 8(R4), R9
+ MOVD 16(R4), R14
+ MOVD 24(R4), R15
+ ADD $32,R4
+ MOVD R8, 0(R3) // store those 4
+ MOVD R9, 8(R3)
+ MOVD R14,16(R3)
+ MOVD R15,24(R3)
+ ADD $32,R3 // bump up for next set
+ BC 16, 0, forward32 // continue
+ RLDCLCC $61,R5,$3,R6 // remaining doublewords
+ BEQ noforwardlarge
+ MOVD R6,CTR // set up the CTR
+ BR forward8
noforwardlarge:
- BNE forwardtail // Tests the bit set by ANDCC above
- RET
+ CMP R7,$0 // any remaining bytes
+ BC 4, 1, LR
forwardtail:
- SUB $1, R3
- SUB $1, R4
- MOVD R7, CTR
+ MOVD R7, CTR // move tail bytes
forwardtailloop:
- MOVBZU 1(R4), R8
- MOVBZU R8, 1(R3)
+ MOVBZ 0(R4), R8 // move single bytes
+ ADD $1,R4
+ MOVBZ R8, 0(R3)
+ ADD $1,R3
BC 16, 0, forwardtailloop
RET
backward:
- // Copying backwards proceeds by copying R7 bytes then copying R6 words.
+ // Copying backwards proceeds by copying R7 bytes then copying R6 double words.
// R3 and R4 are advanced to the end of the destination/source buffers
// respectively and moved back as we copy.
- ADD R5, R4, R4
- ADD R3, R5, R3
+ ADD R5, R4, R4 // end of source
+ ADD R3, R5, R3 // end of dest
- BEQ nobackwardtail
+ BEQ nobackwardtail // earlier condition
- MOVD R7, CTR
+ MOVD R7, CTR // bytes to move
backwardtailloop:
- MOVBZU -1(R4), R8
- MOVBZU R8, -1(R3)
+ MOVBZ -1(R4), R8 // point to last byte
+ SUB $1,R4
+ MOVBZ R8, -1(R3)
+ SUB $1,R3
BC 16, 0, backwardtailloop
nobackwardtail:
- BC 4, 6, backwardlarge // "BNE CR1"
- RET
+ CMP R6,$0
+ BC 4, 5, LR
backwardlarge:
MOVD R6, CTR
backwardlargeloop:
- MOVDU -8(R4), R8
- MOVDU R8, -8(R3)
- BC 16, 0, backwardlargeloop // "BDNZ"
+ MOVD -8(R4), R8
+ SUB $8,R4
+ MOVD R8, -8(R3)
+ SUB $8,R3
+ BC 16, 0, backwardlargeloop //
RET
diff --git a/src/runtime/memmove_s390x.s b/src/runtime/memmove_s390x.s
new file mode 100644
index 0000000000..238f30891d
--- /dev/null
+++ b/src/runtime/memmove_s390x.s
@@ -0,0 +1,189 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+// void runtime·memmove(void*, void*, uintptr)
+TEXT runtime·memmove(SB),NOSPLIT|NOFRAME,$0-24
+ MOVD to+0(FP), R6
+ MOVD from+8(FP), R4
+ MOVD n+16(FP), R5
+
+ CMPBEQ R6, R4, done
+
+start:
+ CMPBLE R5, $3, move0to3
+ CMPBLE R5, $7, move4to7
+ CMPBLE R5, $11, move8to11
+ CMPBLE R5, $15, move12to15
+ CMPBNE R5, $16, movemt16
+ MOVD 0(R4), R7
+ MOVD 8(R4), R8
+ MOVD R7, 0(R6)
+ MOVD R8, 8(R6)
+ RET
+
+movemt16:
+ CMPBGT R4, R6, forwards
+ ADD R5, R4, R7
+ CMPBLE R7, R6, forwards
+ ADD R5, R6, R8
+backwards:
+ MOVD -8(R7), R3
+ MOVD R3, -8(R8)
+ MOVD -16(R7), R3
+ MOVD R3, -16(R8)
+ ADD $-16, R5
+ ADD $-16, R7
+ ADD $-16, R8
+ CMP R5, $16
+ BGE backwards
+ BR start
+
+forwards:
+ CMPBGT R5, $64, forwards_fast
+ MOVD 0(R4), R3
+ MOVD R3, 0(R6)
+ MOVD 8(R4), R3
+ MOVD R3, 8(R6)
+ ADD $16, R4
+ ADD $16, R6
+ ADD $-16, R5
+ CMP R5, $16
+ BGE forwards
+ BR start
+
+forwards_fast:
+ CMP R5, $256
+ BLE forwards_small
+ MVC $256, 0(R4), 0(R6)
+ ADD $256, R4
+ ADD $256, R6
+ ADD $-256, R5
+ BR forwards_fast
+
+forwards_small:
+ CMPBEQ R5, $0, done
+ ADD $-1, R5
+ EXRL $runtime·memmove_s390x_exrl_mvc(SB), R5
+ RET
+
+move0to3:
+ CMPBEQ R5, $0, done
+move1:
+ CMPBNE R5, $1, move2
+ MOVB 0(R4), R3
+ MOVB R3, 0(R6)
+ RET
+move2:
+ CMPBNE R5, $2, move3
+ MOVH 0(R4), R3
+ MOVH R3, 0(R6)
+ RET
+move3:
+ MOVH 0(R4), R3
+ MOVB 2(R4), R7
+ MOVH R3, 0(R6)
+ MOVB R7, 2(R6)
+ RET
+
+move4to7:
+ CMPBNE R5, $4, move5
+ MOVW 0(R4), R3
+ MOVW R3, 0(R6)
+ RET
+move5:
+ CMPBNE R5, $5, move6
+ MOVW 0(R4), R3
+ MOVB 4(R4), R7
+ MOVW R3, 0(R6)
+ MOVB R7, 4(R6)
+ RET
+move6:
+ CMPBNE R5, $6, move7
+ MOVW 0(R4), R3
+ MOVH 4(R4), R7
+ MOVW R3, 0(R6)
+ MOVH R7, 4(R6)
+ RET
+move7:
+ MOVW 0(R4), R3
+ MOVH 4(R4), R7
+ MOVB 6(R4), R8
+ MOVW R3, 0(R6)
+ MOVH R7, 4(R6)
+ MOVB R8, 6(R6)
+ RET
+
+move8to11:
+ CMPBNE R5, $8, move9
+ MOVD 0(R4), R3
+ MOVD R3, 0(R6)
+ RET
+move9:
+ CMPBNE R5, $9, move10
+ MOVD 0(R4), R3
+ MOVB 8(R4), R7
+ MOVD R3, 0(R6)
+ MOVB R7, 8(R6)
+ RET
+move10:
+ CMPBNE R5, $10, move11
+ MOVD 0(R4), R3
+ MOVH 8(R4), R7
+ MOVD R3, 0(R6)
+ MOVH R7, 8(R6)
+ RET
+move11:
+ MOVD 0(R4), R3
+ MOVH 8(R4), R7
+ MOVB 10(R4), R8
+ MOVD R3, 0(R6)
+ MOVH R7, 8(R6)
+ MOVB R8, 10(R6)
+ RET
+
+move12to15:
+ CMPBNE R5, $12, move13
+ MOVD 0(R4), R3
+ MOVW 8(R4), R7
+ MOVD R3, 0(R6)
+ MOVW R7, 8(R6)
+ RET
+move13:
+ CMPBNE R5, $13, move14
+ MOVD 0(R4), R3
+ MOVW 8(R4), R7
+ MOVB 12(R4), R8
+ MOVD R3, 0(R6)
+ MOVW R7, 8(R6)
+ MOVB R8, 12(R6)
+ RET
+move14:
+ CMPBNE R5, $14, move15
+ MOVD 0(R4), R3
+ MOVW 8(R4), R7
+ MOVH 12(R4), R8
+ MOVD R3, 0(R6)
+ MOVW R7, 8(R6)
+ MOVH R8, 12(R6)
+ RET
+move15:
+ MOVD 0(R4), R3
+ MOVW 8(R4), R7
+ MOVH 12(R4), R8
+ MOVB 14(R4), R10
+ MOVD R3, 0(R6)
+ MOVW R7, 8(R6)
+ MOVH R8, 12(R6)
+ MOVB R10, 14(R6)
+done:
+ RET
+
+// DO NOT CALL - target for exrl (execute relative long) instruction.
+TEXT runtime·memmove_s390x_exrl_mvc(SB),NOSPLIT|NOFRAME,$0-0
+ MVC $1, 0(R4), 0(R6)
+ MOVD R0, 0(R0)
+ RET
+
diff --git a/src/runtime/mfinal.go b/src/runtime/mfinal.go
index b862f019b6..e81650d842 100644
--- a/src/runtime/mfinal.go
+++ b/src/runtime/mfinal.go
@@ -172,7 +172,7 @@ func runfinq() {
// all not yet finalized objects are stored in finq.
// If we do not mark it as FlagNoScan,
// the last finalized object is not collected.
- frame = mallocgc(framesz, nil, flagNoScan)
+ frame = mallocgc(framesz, nil, true)
framecap = framesz
}
@@ -274,7 +274,7 @@ func SetFinalizer(obj interface{}, finalizer interface{}) {
throw("runtime.SetFinalizer: first argument is nil")
}
if etyp.kind&kindMask != kindPtr {
- throw("runtime.SetFinalizer: first argument is " + etyp._string + ", not pointer")
+ throw("runtime.SetFinalizer: first argument is " + etyp.string() + ", not pointer")
}
ot := (*ptrtype)(unsafe.Pointer(etyp))
if ot.elem == nil {
@@ -328,14 +328,14 @@ func SetFinalizer(obj interface{}, finalizer interface{}) {
}
if ftyp.kind&kindMask != kindFunc {
- throw("runtime.SetFinalizer: second argument is " + ftyp._string + ", not a function")
+ throw("runtime.SetFinalizer: second argument is " + ftyp.string() + ", not a function")
}
ft := (*functype)(unsafe.Pointer(ftyp))
if ft.dotdotdot() {
- throw("runtime.SetFinalizer: cannot pass " + etyp._string + " to finalizer " + ftyp._string + " because dotdotdot")
+ throw("runtime.SetFinalizer: cannot pass " + etyp.string() + " to finalizer " + ftyp.string() + " because dotdotdot")
}
if ft.dotdotdot() || ft.inCount != 1 {
- throw("runtime.SetFinalizer: cannot pass " + etyp._string + " to finalizer " + ftyp._string)
+ throw("runtime.SetFinalizer: cannot pass " + etyp.string() + " to finalizer " + ftyp.string())
}
fint := ft.in()[0]
switch {
@@ -358,7 +358,7 @@ func SetFinalizer(obj interface{}, finalizer interface{}) {
goto okarg
}
}
- throw("runtime.SetFinalizer: cannot pass " + etyp._string + " to finalizer " + ftyp._string)
+ throw("runtime.SetFinalizer: cannot pass " + etyp.string() + " to finalizer " + ftyp.string())
okarg:
// compute size needed for return parameters
nret := uintptr(0)
diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go
index 1c184db10b..ae8338ac10 100644
--- a/src/runtime/mgc.go
+++ b/src/runtime/mgc.go
@@ -24,6 +24,10 @@
// Hudson, R., and Moss, J.E.B. Copying Garbage Collection without stopping the world.
// Concurrency and Computation: Practice and Experience 15(3-5), 2003.
//
+// TODO(austin): The rest of this comment is woefully out of date and
+// needs to be rewritten. There is no distinct scan phase any more and
+// we allocate black during GC.
+//
// 0. Set phase = GCscan from GCoff.
// 1. Wait for all P's to acknowledge phase change.
// At this point all goroutines have passed through a GC safepoint and
@@ -244,7 +248,7 @@ var gcBlackenPromptly bool
const (
_GCoff = iota // GC not running; sweeping in background, write barrier disabled
- _GCmark // GC marking roots and workbufs, write barrier ENABLED
+ _GCmark // GC marking roots and workbufs: allocate black, write barrier ENABLED
_GCmarktermination // GC mark termination: allocate black, P's help GC, write barrier ENABLED
)
@@ -304,7 +308,8 @@ type gcControllerState struct {
// scanWork is the total scan work performed this cycle. This
// is updated atomically during the cycle. Updates occur in
// bounded batches, since it is both written and read
- // throughout the cycle.
+ // throughout the cycle. At the end of the cycle, this is how
+ // much of the retained heap is scannable.
//
// Currently this is the bytes of heap scanned. For most uses,
// this is an opaque unit of work, but for estimation the
@@ -466,14 +471,18 @@ func (c *gcControllerState) startCycle() {
// It should only be called when gcBlackenEnabled != 0 (because this
// is when assists are enabled and the necessary statistics are
// available).
+//
+// TODO: Consider removing the periodic controller update altogether.
+// Since we switched to allocating black, in theory we shouldn't have
+// to change the assist ratio. However, this is still a useful hook
+// that we've found many uses for when experimenting.
func (c *gcControllerState) revise() {
// Compute the expected scan work remaining.
//
- // Note that the scannable heap size is likely to increase
- // during the GC cycle. This is why it's important to revise
- // the assist ratio throughout the cycle: if the scannable
- // heap size increases, the assist ratio based on the initial
- // scannable heap size may target too little scan work.
+ // Note that we currently count allocations during GC as both
+ // scannable heap (heap_scan) and scan work completed
+ // (scanWork), so this difference won't be changed by
+ // allocations during GC.
//
// This particular estimate is a strict upper bound on the
// possible remaining scan work for the current heap.
@@ -753,7 +762,7 @@ var work struct {
alldone note
// Number of roots of various root types. Set by gcMarkRootPrepare.
- nDataRoots, nBSSRoots, nSpanRoots, nStackRoots int
+ nDataRoots, nBSSRoots, nSpanRoots, nStackRoots, nRescanRoots int
// markrootDone indicates that roots have been marked at least
// once during the current GC cycle. This is checked by root
@@ -821,6 +830,14 @@ var work struct {
head, tail guintptr
}
+ // rescan is a list of G's that need to be rescanned during
+ // mark termination. A G adds itself to this list when it
+ // first invalidates its stack scan.
+ rescan struct {
+ lock mutex
+ list []guintptr
+ }
+
// Timing/utilization stats for this cycle.
stwprocs, maxprocs int32
tSweepTerm, tMark, tMarkTerm, tEnd int64 // nanotime() of phase start
@@ -1069,13 +1086,6 @@ top:
// cached workbufs.
atomic.Xadd(&work.nwait, -1)
- // Rescan global data and BSS. There may still work
- // workers running at this point, so bump "jobs" down
- // before "next" so they won't try running root jobs
- // until we set next.
- atomic.Store(&work.markrootJobs, uint32(fixedRootCount+work.nDataRoots+work.nBSSRoots))
- atomic.Store(&work.markrootNext, fixedRootCount)
-
// GC is set up for mark 2. Let Gs blocked on the
// transition lock go while we flush caches.
semrelease(&work.markDoneSema)
@@ -1257,6 +1267,13 @@ func gcMarkTermination() {
// Free stack spans. This must be done between GC cycles.
systemstack(freeStackSpans)
+ // Best-effort remove stack barriers so they don't get in the
+ // way of things like GDB and perf.
+ lock(&allglock)
+ myallgs := allgs
+ unlock(&allglock)
+ gcTryRemoveAllStackBarriers(myallgs)
+
// Print gctrace before dropping worldsema. As soon as we drop
// worldsema another cycle could start and smash the stats
// we're trying to print.
@@ -1578,9 +1595,13 @@ func gcMark(start_time int64) {
work.markrootDone = true
for i := 0; i < int(gomaxprocs); i++ {
- if !allp[i].gcw.empty() {
+ gcw := &allp[i].gcw
+ if !gcw.empty() {
throw("P has cached GC work at end of mark termination")
}
+ if gcw.scanWork != 0 || gcw.bytesMarked != 0 {
+ throw("P has unflushed stats at end of mark termination")
+ }
}
if trace.enabled {
@@ -1589,27 +1610,8 @@ func gcMark(start_time int64) {
cachestats()
- // Compute the reachable heap size at the beginning of the
- // cycle. This is approximately the marked heap size at the
- // end (which we know) minus the amount of marked heap that
- // was allocated after marking began (which we don't know, but
- // is approximately the amount of heap that was allocated
- // since marking began).
- allocatedDuringCycle := memstats.heap_live - work.initialHeapLive
- if memstats.heap_live < work.initialHeapLive {
- // This can happen if mCentral_UncacheSpan tightens
- // the heap_live approximation.
- allocatedDuringCycle = 0
- }
- if work.bytesMarked >= allocatedDuringCycle {
- memstats.heap_reachable = work.bytesMarked - allocatedDuringCycle
- } else {
- // This can happen if most of the allocation during
- // the cycle never became reachable from the heap.
- // Just set the reachable heap approximation to 0 and
- // let the heapminimum kick in below.
- memstats.heap_reachable = 0
- }
+ // Update the reachable heap stat.
+ memstats.heap_reachable = work.bytesMarked
// Trigger the next GC cycle when the allocated heap has grown
// by triggerRatio over the reachable heap size. Assume that
@@ -1735,14 +1737,22 @@ func gcCopySpans() {
func gcResetMarkState() {
// This may be called during a concurrent phase, so make sure
// allgs doesn't change.
+ if !(gcphase == _GCoff || gcphase == _GCmarktermination) {
+ // Accessing gcRescan is unsafe.
+ throw("bad GC phase")
+ }
lock(&allglock)
for _, gp := range allgs {
gp.gcscandone = false // set to true in gcphasework
gp.gcscanvalid = false // stack has not been scanned
+ gp.gcRescan = -1
gp.gcAssistBytes = 0
}
unlock(&allglock)
+ // Clear rescan list.
+ work.rescan.list = work.rescan.list[:0]
+
work.bytesMarked = 0
work.initialHeapLive = memstats.heap_live
work.markrootDone = false
diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go
index d05ad6549f..3704164527 100644
--- a/src/runtime/mgcmark.go
+++ b/src/runtime/mgcmark.go
@@ -15,6 +15,7 @@ import (
const (
fixedRootFinalizers = iota
fixedRootFlushCaches
+ fixedRootFreeGStacks
fixedRootCount
// rootBlockBytes is the number of bytes to scan per data or
@@ -31,6 +32,8 @@ const (
//
// The caller must have call gcCopySpans().
//
+// The world must be stopped.
+//
//go:nowritebarrier
func gcMarkRootPrepare() {
// Compute how many data and BSS root blocks there are.
@@ -39,36 +42,58 @@ func gcMarkRootPrepare() {
}
work.nDataRoots = 0
- for datap := &firstmoduledata; datap != nil; datap = datap.next {
- nDataRoots := nBlocks(datap.edata - datap.data)
- if nDataRoots > work.nDataRoots {
- work.nDataRoots = nDataRoots
+ work.nBSSRoots = 0
+
+ // Only scan globals once per cycle; preferably concurrently.
+ if !work.markrootDone {
+ for datap := &firstmoduledata; datap != nil; datap = datap.next {
+ nDataRoots := nBlocks(datap.edata - datap.data)
+ if nDataRoots > work.nDataRoots {
+ work.nDataRoots = nDataRoots
+ }
}
- }
- work.nBSSRoots = 0
- for datap := &firstmoduledata; datap != nil; datap = datap.next {
- nBSSRoots := nBlocks(datap.ebss - datap.bss)
- if nBSSRoots > work.nBSSRoots {
- work.nBSSRoots = nBSSRoots
+ for datap := &firstmoduledata; datap != nil; datap = datap.next {
+ nBSSRoots := nBlocks(datap.ebss - datap.bss)
+ if nBSSRoots > work.nBSSRoots {
+ work.nBSSRoots = nBSSRoots
+ }
}
}
- // Compute number of span roots.
- work.nSpanRoots = (len(work.spans) + rootBlockSpans - 1) / rootBlockSpans
+ if !work.markrootDone {
+ // On the first markroot, we need to scan span roots.
+ // In concurrent GC, this happens during concurrent
+ // mark and we depend on addfinalizer to ensure the
+ // above invariants for objects that get finalizers
+ // after concurrent mark. In STW GC, this will happen
+ // during mark termination.
+ work.nSpanRoots = (len(work.spans) + rootBlockSpans - 1) / rootBlockSpans
+
+ // On the first markroot, we need to scan all Gs. Gs
+ // may be created after this point, but it's okay that
+ // we ignore them because they begin life without any
+ // roots, so there's nothing to scan, and any roots
+ // they create during the concurrent phase will be
+ // scanned during mark termination. During mark
+ // termination, allglen isn't changing, so we'll scan
+ // all Gs.
+ work.nStackRoots = int(atomic.Loaduintptr(&allglen))
+ work.nRescanRoots = 0
+ } else {
+ // We've already scanned span roots and kept the scan
+ // up-to-date during concurrent mark.
+ work.nSpanRoots = 0
- // Snapshot of allglen. During concurrent scan, we just need
- // to be consistent about how many markroot jobs we create and
- // how many Gs we check. Gs may be created after this point,
- // but it's okay that we ignore them because they begin life
- // without any roots, so there's nothing to scan, and any
- // roots they create during the concurrent phase will be
- // scanned during mark termination. During mark termination,
- // allglen isn't changing, so we'll scan all Gs.
- work.nStackRoots = int(atomic.Loaduintptr(&allglen))
+ // On the second pass of markroot, we're just scanning
+ // dirty stacks. It's safe to access rescan since the
+ // world is stopped.
+ work.nStackRoots = 0
+ work.nRescanRoots = len(work.rescan.list)
+ }
work.markrootNext = 0
- work.markrootJobs = uint32(fixedRootCount + work.nDataRoots + work.nBSSRoots + work.nSpanRoots + work.nStackRoots)
+ work.markrootJobs = uint32(fixedRootCount + work.nDataRoots + work.nBSSRoots + work.nSpanRoots + work.nStackRoots + work.nRescanRoots)
}
// gcMarkRootCheck checks that all roots have been scanned. It is
@@ -80,11 +105,24 @@ func gcMarkRootCheck() {
}
lock(&allglock)
- // Check that gc work is done.
- for i := 0; i < work.nStackRoots; i++ {
- gp := allgs[i]
- if !gp.gcscandone {
- throw("scan missed a g")
+ // Check that stacks have been scanned.
+ if gcphase == _GCmarktermination {
+ for i := 0; i < len(allgs); i++ {
+ gp := allgs[i]
+ if !(gp.gcscandone && gp.gcscanvalid) && readgstatus(gp) != _Gdead {
+ println("gp", gp, "goid", gp.goid,
+ "status", readgstatus(gp),
+ "gcscandone", gp.gcscandone,
+ "gcscanvalid", gp.gcscanvalid)
+ throw("scan missed a g")
+ }
+ }
+ } else {
+ for i := 0; i < work.nStackRoots; i++ {
+ gp := allgs[i]
+ if !gp.gcscandone {
+ throw("scan missed a g")
+ }
}
}
unlock(&allglock)
@@ -97,12 +135,18 @@ var oneptrmask = [...]uint8{1}
//
// Preemption must be disabled (because this uses a gcWork).
//
+// nowritebarrier is only advisory here.
+//
//go:nowritebarrier
func markroot(gcw *gcWork, i uint32) {
+ // TODO(austin): This is a bit ridiculous. Compute and store
+ // the bases in gcMarkRootPrepare instead of the counts.
baseData := uint32(fixedRootCount)
baseBSS := baseData + uint32(work.nDataRoots)
baseSpans := baseBSS + uint32(work.nBSSRoots)
baseStacks := baseSpans + uint32(work.nSpanRoots)
+ baseRescan := baseStacks + uint32(work.nStackRoots)
+ end := baseRescan + uint32(work.nRescanRoots)
// Note: if you add a case here, please also update heapdump.go:dumproots.
switch {
@@ -126,16 +170,27 @@ func markroot(gcw *gcWork, i uint32) {
flushallmcaches()
}
+ case i == fixedRootFreeGStacks:
+ // Only do this once per GC cycle; preferably
+ // concurrently.
+ if !work.markrootDone {
+ markrootFreeGStacks()
+ }
+
case baseSpans <= i && i < baseStacks:
// mark MSpan.specials
markrootSpans(gcw, int(i-baseSpans))
default:
// the rest is scanning goroutine stacks
- if uintptr(i-baseStacks) >= allglen {
+ var gp *g
+ if baseStacks <= i && i < baseRescan {
+ gp = allgs[i-baseStacks]
+ } else if baseRescan <= i && i < end {
+ gp = work.rescan.list[i-baseRescan].ptr()
+ } else {
throw("markroot: bad index")
}
- gp := allgs[i-baseStacks]
// remember when we've first observed the G blocked
// needed only to output in traceback
@@ -144,20 +199,14 @@ func markroot(gcw *gcWork, i uint32) {
gp.waitsince = work.tstart
}
- if gcphase == _GCmarktermination && status == _Gdead {
- // Free gp's stack if necessary. Only do this
- // during mark termination because otherwise
- // _Gdead may be transient.
- shrinkstack(gp)
- }
-
- if gcphase != _GCmarktermination && gp.startpc == gcBgMarkWorkerPC {
+ if gcphase != _GCmarktermination && gp.startpc == gcBgMarkWorkerPC && readgstatus(gp) != _Gdead {
// GC background workers may be
// non-preemptible, so we may deadlock if we
// try to scan them during a concurrent phase.
// They also have tiny stacks, so just ignore
// them until mark termination.
gp.gcscandone = true
+ queueRescan(gp)
break
}
@@ -215,6 +264,36 @@ func markrootBlock(b0, n0 uintptr, ptrmask0 *uint8, gcw *gcWork, shard int) {
scanblock(b, n, ptrmask, gcw)
}
+// markrootFreeGStacks frees stacks of dead Gs.
+//
+// This does not free stacks of dead Gs cached on Ps, but having a few
+// cached stacks around isn't a problem.
+//
+//TODO go:nowritebarrier
+func markrootFreeGStacks() {
+ // Take list of dead Gs with stacks.
+ lock(&sched.gflock)
+ list := sched.gfreeStack
+ sched.gfreeStack = nil
+ unlock(&sched.gflock)
+ if list == nil {
+ return
+ }
+
+ // Free stacks.
+ tail := list
+ for gp := list; gp != nil; gp = gp.schedlink.ptr() {
+ shrinkstack(gp)
+ tail = gp
+ }
+
+ // Put Gs back on the free list.
+ lock(&sched.gflock)
+ tail.schedlink.set(sched.gfreeNoStack)
+ sched.gfreeNoStack = list
+ unlock(&sched.gflock)
+}
+
// markrootSpans marks roots for one shard of work.spans.
//
//go:nowritebarrier
@@ -232,14 +311,8 @@ func markrootSpans(gcw *gcWork, shard int) {
// TODO(austin): There are several ideas for making this more
// efficient in issue #11485.
- // We process objects with finalizers only during the first
- // markroot pass. In concurrent GC, this happens during
- // concurrent mark and we depend on addfinalizer to ensure the
- // above invariants for objects that get finalizers after
- // concurrent mark. In STW GC, this will happen during mark
- // termination.
if work.markrootDone {
- return
+ throw("markrootSpans during second markroot")
}
sg := mheap_.sweepgen
@@ -566,9 +639,6 @@ func gcFlushBgCredit(scanWork int64) {
//go:nowritebarrier
func scanstack(gp *g) {
if gp.gcscanvalid {
- if gcphase == _GCmarktermination {
- gcRemoveStackBarriers(gp)
- }
return
}
@@ -611,6 +681,7 @@ func scanstack(gp *g) {
} else {
sp = gp.sched.sp
}
+ gcLockStackBarriers(gp) // Not necessary during mark term, but harmless.
switch gcphase {
case _GCmark:
// Install stack barriers during stack scan.
@@ -621,16 +692,18 @@ func scanstack(gp *g) {
nextBarrier = ^uintptr(0)
}
- if gp.stkbarPos != 0 || len(gp.stkbar) != 0 {
- // If this happens, it's probably because we
- // scanned a stack twice in the same phase.
- print("stkbarPos=", gp.stkbarPos, " len(stkbar)=", len(gp.stkbar), " goid=", gp.goid, " gcphase=", gcphase, "\n")
- throw("g already has stack barriers")
- }
-
- gcLockStackBarriers(gp)
+ // Remove any existing stack barriers before we
+ // install new ones.
+ gcRemoveStackBarriers(gp)
case _GCmarktermination:
+ if !work.markrootDone {
+ // This is a STW GC. There may be stale stack
+ // barriers from an earlier cycle since we
+ // never passed through mark phase.
+ gcRemoveStackBarriers(gp)
+ }
+
if int(gp.stkbarPos) == len(gp.stkbar) {
// gp hit all of the stack barriers (or there
// were none). Re-scan the whole stack.
@@ -647,8 +720,6 @@ func scanstack(gp *g) {
}
}
- gcRemoveStackBarriers(gp)
-
default:
throw("scanstack in wrong phase")
}
@@ -686,8 +757,14 @@ func scanstack(gp *g) {
if gcphase == _GCmarktermination {
gcw.dispose()
}
+ gcUnlockStackBarriers(gp)
if gcphase == _GCmark {
- gcUnlockStackBarriers(gp)
+ // gp may have added itself to the rescan list between
+ // when GC started and now. It's clean now, so remove
+ // it. This isn't safe during mark termination because
+ // mark termination is consuming this list, but it's
+ // also not necessary.
+ dequeueRescan(gp)
}
gp.gcscanvalid = true
}
@@ -719,8 +796,8 @@ func scanframeworker(frame *stkframe, cache *pcvalueCache, gcw *gcWork) {
// Scan local variables if stack frame has been allocated.
size := frame.varp - frame.sp
var minsize uintptr
- switch sys.TheChar {
- case '7':
+ switch sys.ArchFamily {
+ case sys.ARM64:
minsize = sys.SpAlign
default:
minsize = sys.MinFrameSize
@@ -765,6 +842,60 @@ func scanframeworker(frame *stkframe, cache *pcvalueCache, gcw *gcWork) {
}
}
+// queueRescan adds gp to the stack rescan list and clears
+// gp.gcscanvalid. The caller must own gp and ensure that gp isn't
+// already on the rescan list.
+func queueRescan(gp *g) {
+ if gcphase == _GCoff {
+ gp.gcscanvalid = false
+ return
+ }
+ if gp.gcRescan != -1 {
+ throw("g already on rescan list")
+ }
+
+ lock(&work.rescan.lock)
+ gp.gcscanvalid = false
+
+ // Recheck gcphase under the lock in case there was a phase change.
+ if gcphase == _GCoff {
+ unlock(&work.rescan.lock)
+ return
+ }
+ if len(work.rescan.list) == cap(work.rescan.list) {
+ throw("rescan list overflow")
+ }
+ n := len(work.rescan.list)
+ gp.gcRescan = int32(n)
+ work.rescan.list = work.rescan.list[:n+1]
+ work.rescan.list[n].set(gp)
+ unlock(&work.rescan.lock)
+}
+
+// dequeueRescan removes gp from the stack rescan list, if gp is on
+// the rescan list. The caller must own gp.
+func dequeueRescan(gp *g) {
+ if gp.gcRescan == -1 {
+ return
+ }
+ if gcphase == _GCoff {
+ gp.gcRescan = -1
+ return
+ }
+
+ lock(&work.rescan.lock)
+ if work.rescan.list[gp.gcRescan].ptr() != gp {
+ throw("bad dequeueRescan")
+ }
+ // Careful: gp may itself be the last G on the list.
+ last := work.rescan.list[len(work.rescan.list)-1]
+ work.rescan.list[gp.gcRescan] = last
+ last.ptr().gcRescan = gp.gcRescan
+ gp.gcRescan = -1
+ work.rescan.list = work.rescan.list[:len(work.rescan.list)-1]
+ unlock(&work.rescan.lock)
+}
+
type gcDrainFlags int
const (
@@ -1140,14 +1271,21 @@ func gcDumpObject(label string, obj, off uintptr) {
}
}
-// If gcBlackenPromptly is true we are in the second mark phase phase so we allocate black.
+// gcmarknewobject marks a newly allocated object black. obj must
+// not contain any non-nil pointers.
+//
+// This is nosplit so it can manipulate a gcWork without preemption.
+//
//go:nowritebarrier
-func gcmarknewobject_m(obj, size uintptr) {
+//go:nosplit
+func gcmarknewobject(obj, size, scanSize uintptr) {
if useCheckmark && !gcBlackenPromptly { // The world should be stopped so this should not happen.
throw("gcmarknewobject called while doing checkmark")
}
markBitsForAddr(obj).setMarked()
- atomic.Xadd64(&work.bytesMarked, int64(size))
+ gcw := &getg().m.p.ptr().gcw
+ gcw.bytesMarked += uint64(size)
+ gcw.scanWork += int64(scanSize)
}
// Checkmarking
diff --git a/src/runtime/mheap.go b/src/runtime/mheap.go
index 9f07dfbb99..1333dd696b 100644
--- a/src/runtime/mheap.go
+++ b/src/runtime/mheap.go
@@ -870,15 +870,6 @@ func (h *mheap) busyList(npages uintptr) *mSpanList {
}
func scavengelist(list *mSpanList, now, limit uint64) uintptr {
- if sys.PhysPageSize > _PageSize {
- // golang.org/issue/9993
- // If the physical page size of the machine is larger than
- // our logical heap page size the kernel may round up the
- // amount to be freed to its page size and corrupt the heap
- // pages surrounding the unused block.
- return 0
- }
-
if list.isEmpty() {
return 0
}
@@ -886,11 +877,30 @@ func scavengelist(list *mSpanList, now, limit uint64) uintptr {
var sumreleased uintptr
for s := list.first; s != nil; s = s.next {
if (now-uint64(s.unusedsince)) > limit && s.npreleased != s.npages {
- released := (s.npages - s.npreleased) << _PageShift
+ start := uintptr(s.start) << _PageShift
+ end := start + s.npages<<_PageShift
+ if sys.PhysPageSize > _PageSize {
+ // We can only release pages in
+ // PhysPageSize blocks, so round start
+ // and end in. (Otherwise, madvise
+ // will round them *out* and release
+ // more memory than we want.)
+ start = (start + sys.PhysPageSize - 1) &^ (sys.PhysPageSize - 1)
+ end &^= sys.PhysPageSize - 1
+ if start == end {
+ continue
+ }
+ }
+ len := end - start
+
+ released := len - (s.npreleased << _PageShift)
+ if sys.PhysPageSize > _PageSize && released == 0 {
+ continue
+ }
memstats.heap_released += uint64(released)
sumreleased += released
- s.npreleased = s.npages
- sysUnused(unsafe.Pointer(s.start<<_PageShift), s.npages<<_PageShift)
+ s.npreleased = len >> _PageShift
+ sysUnused(unsafe.Pointer(start), len)
}
}
return sumreleased
diff --git a/src/runtime/mmap.go b/src/runtime/mmap.go
index 6363a90242..53617e41e4 100644
--- a/src/runtime/mmap.go
+++ b/src/runtime/mmap.go
@@ -13,4 +13,7 @@ package runtime
import "unsafe"
// mmap calls the mmap system call. It is implemented in assembly.
+// We only pass the lower 32 bits of file offset to the
+// assembly routine; the higher bits (if required), should be provided
+// by the assembly routine as 0.
func mmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) unsafe.Pointer
diff --git a/src/runtime/mprof.go b/src/runtime/mprof.go
index f3b9b4bc78..c3e4e2cb87 100644
--- a/src/runtime/mprof.go
+++ b/src/runtime/mprof.go
@@ -624,7 +624,7 @@ func tracealloc(p unsafe.Pointer, size uintptr, typ *_type) {
if typ == nil {
print("tracealloc(", p, ", ", hex(size), ")\n")
} else {
- print("tracealloc(", p, ", ", hex(size), ", ", typ._string, ")\n")
+ print("tracealloc(", p, ", ", hex(size), ", ", typ.string(), ")\n")
}
if gp.m.curg == nil || gp == gp.m.curg {
goroutineheader(gp)
diff --git a/src/runtime/mstkbar.go b/src/runtime/mstkbar.go
index 016625ae92..1bf9d573b7 100644
--- a/src/runtime/mstkbar.go
+++ b/src/runtime/mstkbar.go
@@ -214,14 +214,15 @@ func gcInstallStackBarrier(gp *g, frame *stkframe) bool {
}
// gcRemoveStackBarriers removes all stack barriers installed in gp's stack.
+//
+// gp's stack barriers must be locked.
+//
//go:nowritebarrier
func gcRemoveStackBarriers(gp *g) {
if debugStackBarrier && gp.stkbarPos != 0 {
print("hit ", gp.stkbarPos, " stack barriers, goid=", gp.goid, "\n")
}
- gcLockStackBarriers(gp)
-
// Remove stack barriers that we didn't hit.
for _, stkbar := range gp.stkbar[gp.stkbarPos:] {
gcRemoveStackBarrier(gp, stkbar)
@@ -231,8 +232,6 @@ func gcRemoveStackBarriers(gp *g) {
// adjust them.
gp.stkbarPos = 0
gp.stkbar = gp.stkbar[:0]
-
- gcUnlockStackBarriers(gp)
}
// gcRemoveStackBarrier removes a single stack barrier. It is the
@@ -258,6 +257,31 @@ func gcRemoveStackBarrier(gp *g, stkbar stkbar) {
*lrPtr = sys.Uintreg(stkbar.savedLRVal)
}
+// gcTryRemoveAllStackBarriers tries to remove stack barriers from all
+// Gs in gps. It is best-effort and efficient. If it can't remove
+// barriers from a G immediately, it will simply skip it.
+func gcTryRemoveAllStackBarriers(gps []*g) {
+ for _, gp := range gps {
+ retry:
+ for {
+ switch s := readgstatus(gp); s {
+ default:
+ break retry
+
+ case _Grunnable, _Gsyscall, _Gwaiting:
+ if !castogscanstatus(gp, s, s|_Gscan) {
+ continue
+ }
+ gcLockStackBarriers(gp)
+ gcRemoveStackBarriers(gp)
+ gcUnlockStackBarriers(gp)
+ restartg(gp)
+ break retry
+ }
+ }
+ }
+}
+
// gcPrintStkbars prints the stack barriers of gp for debugging. It
// places a "@@@" marker at gp.stkbarPos. If marker >= 0, it will also
// place a "==>" marker before the marker'th entry.
diff --git a/src/runtime/noasm.go b/src/runtime/noasm.go
index 351e325f4f..0a8f9e6f52 100644
--- a/src/runtime/noasm.go
+++ b/src/runtime/noasm.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Routines that are implemented in assembly in asm_{amd64,386,arm,arm64,ppc64x}.s
+// Routines that are implemented in assembly in asm_{amd64,386,arm,arm64,ppc64x,s390x}.s
// +build mips64 mips64le
diff --git a/src/runtime/os1_darwin.go b/src/runtime/os1_darwin.go
deleted file mode 100644
index 01dc90f97c..0000000000
--- a/src/runtime/os1_darwin.go
+++ /dev/null
@@ -1,538 +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 runtime
-
-import "unsafe"
-
-//extern SigTabTT runtime·sigtab[];
-
-type sigset uint32
-
-var sigset_all = ^sigset(0)
-
-func unimplemented(name string) {
- println(name, "not implemented")
- *(*int)(unsafe.Pointer(uintptr(1231))) = 1231
-}
-
-//go:nosplit
-func semawakeup(mp *m) {
- mach_semrelease(mp.waitsema)
-}
-
-//go:nosplit
-func semacreate(mp *m) {
- if mp.waitsema != 0 {
- return
- }
- systemstack(func() {
- mp.waitsema = mach_semcreate()
- })
-}
-
-// BSD interface for threading.
-func osinit() {
- // bsdthread_register delayed until end of goenvs so that we
- // can look at the environment first.
-
- ncpu = getncpu()
-}
-
-func getncpu() int32 {
- // Use sysctl to fetch hw.ncpu.
- mib := [2]uint32{6, 3}
- out := uint32(0)
- nout := unsafe.Sizeof(out)
- ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
- if ret >= 0 && int32(out) > 0 {
- return int32(out)
- }
- return 1
-}
-
-var urandom_dev = []byte("/dev/urandom\x00")
-
-//go:nosplit
-func getRandomData(r []byte) {
- fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
- n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
- closefd(fd)
- extendRandom(r, int(n))
-}
-
-func goenvs() {
- goenvs_unix()
-
- // Register our thread-creation callback (see sys_darwin_{amd64,386}.s)
- // but only if we're not using cgo. If we are using cgo we need
- // to let the C pthread library install its own thread-creation callback.
- if !iscgo {
- if bsdthread_register() != 0 {
- if gogetenv("DYLD_INSERT_LIBRARIES") != "" {
- throw("runtime: bsdthread_register error (unset DYLD_INSERT_LIBRARIES)")
- }
- throw("runtime: bsdthread_register error")
- }
- }
-}
-
-// May run with m.p==nil, so write barriers are not allowed.
-//go:nowritebarrier
-func newosproc(mp *m, stk unsafe.Pointer) {
- if false {
- print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " id=", mp.id, " ostk=", &mp, "\n")
- }
-
- var oset sigset
- sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
- errno := bsdthread_create(stk, unsafe.Pointer(mp), funcPC(mstart))
- sigprocmask(_SIG_SETMASK, &oset, nil)
-
- if errno < 0 {
- print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", -errno, ")\n")
- throw("runtime.newosproc")
- }
-}
-
-// newosproc0 is a version of newosproc that can be called before the runtime
-// is initialized.
-//
-// As Go uses bsdthread_register when running without cgo, this function is
-// not safe to use after initialization as it does not pass an M as fnarg.
-//
-//go:nosplit
-func newosproc0(stacksize uintptr, fn unsafe.Pointer, fnarg uintptr) {
- stack := sysAlloc(stacksize, &memstats.stacks_sys)
- if stack == nil {
- write(2, unsafe.Pointer(&failallocatestack[0]), int32(len(failallocatestack)))
- exit(1)
- }
- stk := unsafe.Pointer(uintptr(stack) + stacksize)
-
- var oset sigset
- sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
- errno := bsdthread_create(stk, fn, fnarg)
- sigprocmask(_SIG_SETMASK, &oset, nil)
-
- if errno < 0 {
- write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
- exit(1)
- }
-}
-
-var failallocatestack = []byte("runtime: failed to allocate stack for the new OS thread\n")
-var failthreadcreate = []byte("runtime: failed to create new OS thread\n")
-
-// Called to do synchronous initialization of Go code built with
-// -buildmode=c-archive or -buildmode=c-shared.
-// None of the Go runtime is initialized.
-//go:nosplit
-//go:nowritebarrierrec
-func libpreinit() {
- initsig(true)
-}
-
-// Called to initialize a new m (including the bootstrap m).
-// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
-func mpreinit(mp *m) {
- mp.gsignal = malg(32 * 1024) // OS X wants >= 8K
- mp.gsignal.m = mp
-}
-
-//go:nosplit
-func msigsave(mp *m) {
- sigprocmask(_SIG_SETMASK, nil, &mp.sigmask)
-}
-
-//go:nosplit
-func msigrestore(sigmask sigset) {
- sigprocmask(_SIG_SETMASK, &sigmask, nil)
-}
-
-//go:nosplit
-func sigblock() {
- sigprocmask(_SIG_SETMASK, &sigset_all, nil)
-}
-
-// Called to initialize a new m (including the bootstrap m).
-// Called on the new thread, cannot allocate memory.
-func minit() {
- // Initialize signal handling.
- _g_ := getg()
-
- // The alternate signal stack is buggy on arm and arm64.
- // The signal handler handles it directly.
- // The sigaltstack assembly function does nothing.
- if GOARCH != "arm" && GOARCH != "arm64" {
- var st stackt
- sigaltstack(nil, &st)
- if st.ss_flags&_SS_DISABLE != 0 {
- signalstack(&_g_.m.gsignal.stack)
- _g_.m.newSigstack = true
- } else {
- // Use existing signal stack.
- stsp := uintptr(unsafe.Pointer(st.ss_sp))
- _g_.m.gsignal.stack.lo = stsp
- _g_.m.gsignal.stack.hi = stsp + st.ss_size
- _g_.m.gsignal.stackguard0 = stsp + _StackGuard
- _g_.m.gsignal.stackguard1 = stsp + _StackGuard
- _g_.m.gsignal.stackAlloc = st.ss_size
- _g_.m.newSigstack = false
- }
- }
-
- // restore signal mask from m.sigmask and unblock essential signals
- nmask := _g_.m.sigmask
- for i := range sigtable {
- if sigtable[i].flags&_SigUnblock != 0 {
- nmask &^= 1 << (uint32(i) - 1)
- }
- }
- sigprocmask(_SIG_SETMASK, &nmask, nil)
-}
-
-// Called from dropm to undo the effect of an minit.
-//go:nosplit
-func unminit() {
- if getg().m.newSigstack {
- signalstack(nil)
- }
-}
-
-// Mach IPC, to get at semaphores
-// Definitions are in /usr/include/mach on a Mac.
-
-func macherror(r int32, fn string) {
- print("mach error ", fn, ": ", r, "\n")
- throw("mach error")
-}
-
-const _DebugMach = false
-
-var zerondr machndr
-
-func mach_msgh_bits(a, b uint32) uint32 {
- return a | b<<8
-}
-
-func mach_msg(h *machheader, op int32, send_size, rcv_size, rcv_name, timeout, notify uint32) int32 {
- // TODO: Loop on interrupt.
- return mach_msg_trap(unsafe.Pointer(h), op, send_size, rcv_size, rcv_name, timeout, notify)
-}
-
-// Mach RPC (MIG)
-const (
- _MinMachMsg = 48
- _MachReply = 100
-)
-
-type codemsg struct {
- h machheader
- ndr machndr
- code int32
-}
-
-func machcall(h *machheader, maxsize int32, rxsize int32) int32 {
- _g_ := getg()
- port := _g_.m.machport
- if port == 0 {
- port = mach_reply_port()
- _g_.m.machport = port
- }
-
- h.msgh_bits |= mach_msgh_bits(_MACH_MSG_TYPE_COPY_SEND, _MACH_MSG_TYPE_MAKE_SEND_ONCE)
- h.msgh_local_port = port
- h.msgh_reserved = 0
- id := h.msgh_id
-
- if _DebugMach {
- p := (*[10000]unsafe.Pointer)(unsafe.Pointer(h))
- print("send:\t")
- var i uint32
- for i = 0; i < h.msgh_size/uint32(unsafe.Sizeof(p[0])); i++ {
- print(" ", p[i])
- if i%8 == 7 {
- print("\n\t")
- }
- }
- if i%8 != 0 {
- print("\n")
- }
- }
- ret := mach_msg(h, _MACH_SEND_MSG|_MACH_RCV_MSG, h.msgh_size, uint32(maxsize), port, 0, 0)
- if ret != 0 {
- if _DebugMach {
- print("mach_msg error ", ret, "\n")
- }
- return ret
- }
- if _DebugMach {
- p := (*[10000]unsafe.Pointer)(unsafe.Pointer(h))
- var i uint32
- for i = 0; i < h.msgh_size/uint32(unsafe.Sizeof(p[0])); i++ {
- print(" ", p[i])
- if i%8 == 7 {
- print("\n\t")
- }
- }
- if i%8 != 0 {
- print("\n")
- }
- }
- if h.msgh_id != id+_MachReply {
- if _DebugMach {
- print("mach_msg _MachReply id mismatch ", h.msgh_id, " != ", id+_MachReply, "\n")
- }
- return -303 // MIG_REPLY_MISMATCH
- }
- // Look for a response giving the return value.
- // Any call can send this back with an error,
- // and some calls only have return values so they
- // send it back on success too. I don't quite see how
- // you know it's one of these and not the full response
- // format, so just look if the message is right.
- c := (*codemsg)(unsafe.Pointer(h))
- if uintptr(h.msgh_size) == unsafe.Sizeof(*c) && h.msgh_bits&_MACH_MSGH_BITS_COMPLEX == 0 {
- if _DebugMach {
- print("mig result ", c.code, "\n")
- }
- return c.code
- }
- if h.msgh_size != uint32(rxsize) {
- if _DebugMach {
- print("mach_msg _MachReply size mismatch ", h.msgh_size, " != ", rxsize, "\n")
- }
- return -307 // MIG_ARRAY_TOO_LARGE
- }
- return 0
-}
-
-// Semaphores!
-
-const (
- tmach_semcreate = 3418
- rmach_semcreate = tmach_semcreate + _MachReply
-
- tmach_semdestroy = 3419
- rmach_semdestroy = tmach_semdestroy + _MachReply
-
- _KERN_ABORTED = 14
- _KERN_OPERATION_TIMED_OUT = 49
-)
-
-type tmach_semcreatemsg struct {
- h machheader
- ndr machndr
- policy int32
- value int32
-}
-
-type rmach_semcreatemsg struct {
- h machheader
- body machbody
- semaphore machport
-}
-
-type tmach_semdestroymsg struct {
- h machheader
- body machbody
- semaphore machport
-}
-
-func mach_semcreate() uint32 {
- var m [256]uint8
- tx := (*tmach_semcreatemsg)(unsafe.Pointer(&m))
- rx := (*rmach_semcreatemsg)(unsafe.Pointer(&m))
-
- tx.h.msgh_bits = 0
- tx.h.msgh_size = uint32(unsafe.Sizeof(*tx))
- tx.h.msgh_remote_port = mach_task_self()
- tx.h.msgh_id = tmach_semcreate
- tx.ndr = zerondr
-
- tx.policy = 0 // 0 = SYNC_POLICY_FIFO
- tx.value = 0
-
- for {
- r := machcall(&tx.h, int32(unsafe.Sizeof(m)), int32(unsafe.Sizeof(*rx)))
- if r == 0 {
- break
- }
- if r == _KERN_ABORTED { // interrupted
- continue
- }
- macherror(r, "semaphore_create")
- }
- if rx.body.msgh_descriptor_count != 1 {
- unimplemented("mach_semcreate desc count")
- }
- return rx.semaphore.name
-}
-
-func mach_semdestroy(sem uint32) {
- var m [256]uint8
- tx := (*tmach_semdestroymsg)(unsafe.Pointer(&m))
-
- tx.h.msgh_bits = _MACH_MSGH_BITS_COMPLEX
- tx.h.msgh_size = uint32(unsafe.Sizeof(*tx))
- tx.h.msgh_remote_port = mach_task_self()
- tx.h.msgh_id = tmach_semdestroy
- tx.body.msgh_descriptor_count = 1
- tx.semaphore.name = sem
- tx.semaphore.disposition = _MACH_MSG_TYPE_MOVE_SEND
- tx.semaphore._type = 0
-
- for {
- r := machcall(&tx.h, int32(unsafe.Sizeof(m)), 0)
- if r == 0 {
- break
- }
- if r == _KERN_ABORTED { // interrupted
- continue
- }
- macherror(r, "semaphore_destroy")
- }
-}
-
-// The other calls have simple system call traps in sys_darwin_{amd64,386}.s
-
-func mach_semaphore_wait(sema uint32) int32
-func mach_semaphore_timedwait(sema, sec, nsec uint32) int32
-func mach_semaphore_signal(sema uint32) int32
-func mach_semaphore_signal_all(sema uint32) int32
-
-func semasleep1(ns int64) int32 {
- _g_ := getg()
-
- if ns >= 0 {
- var nsecs int32
- secs := timediv(ns, 1000000000, &nsecs)
- r := mach_semaphore_timedwait(_g_.m.waitsema, uint32(secs), uint32(nsecs))
- if r == _KERN_ABORTED || r == _KERN_OPERATION_TIMED_OUT {
- return -1
- }
- if r != 0 {
- macherror(r, "semaphore_wait")
- }
- return 0
- }
-
- for {
- r := mach_semaphore_wait(_g_.m.waitsema)
- if r == 0 {
- break
- }
- if r == _KERN_ABORTED { // interrupted
- continue
- }
- macherror(r, "semaphore_wait")
- }
- return 0
-}
-
-//go:nosplit
-func semasleep(ns int64) int32 {
- var r int32
- systemstack(func() {
- r = semasleep1(ns)
- })
- return r
-}
-
-//go:nosplit
-func mach_semrelease(sem uint32) {
- for {
- r := mach_semaphore_signal(sem)
- if r == 0 {
- break
- }
- if r == _KERN_ABORTED { // interrupted
- continue
- }
-
- // mach_semrelease must be completely nosplit,
- // because it is called from Go code.
- // If we're going to die, start that process on the system stack
- // to avoid a Go stack split.
- systemstack(func() { macherror(r, "semaphore_signal") })
- }
-}
-
-//go:nosplit
-func osyield() {
- usleep(1)
-}
-
-func memlimit() uintptr {
- // NOTE(rsc): Could use getrlimit here,
- // like on FreeBSD or Linux, but Darwin doesn't enforce
- // ulimit -v, so it's unclear why we'd try to stay within
- // the limit.
- return 0
-}
-
-//go:nosplit
-//go:nowritebarrierrec
-func setsig(i int32, fn uintptr, restart bool) {
- var sa sigactiont
- sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK
- if restart {
- sa.sa_flags |= _SA_RESTART
- }
- sa.sa_mask = ^uint32(0)
- sa.sa_tramp = unsafe.Pointer(funcPC(sigtramp)) // runtime·sigtramp's job is to call into real handler
- *(*uintptr)(unsafe.Pointer(&sa.__sigaction_u)) = fn
- sigaction(uint32(i), &sa, nil)
-}
-
-//go:nosplit
-//go:nowritebarrierrec
-func setsigstack(i int32) {
- var osa usigactiont
- sigaction(uint32(i), nil, &osa)
- handler := *(*uintptr)(unsafe.Pointer(&osa.__sigaction_u))
- if handler == 0 || handler == _SIG_DFL || handler == _SIG_IGN || osa.sa_flags&_SA_ONSTACK != 0 {
- return
- }
- var sa sigactiont
- *(*uintptr)(unsafe.Pointer(&sa.__sigaction_u)) = handler
- sa.sa_tramp = unsafe.Pointer(funcPC(sigtramp))
- sa.sa_mask = osa.sa_mask
- sa.sa_flags = osa.sa_flags | _SA_ONSTACK
- sigaction(uint32(i), &sa, nil)
-}
-
-//go:nosplit
-//go:nowritebarrierrec
-func getsig(i int32) uintptr {
- var sa usigactiont
- sigaction(uint32(i), nil, &sa)
- return *(*uintptr)(unsafe.Pointer(&sa.__sigaction_u))
-}
-
-//go:nosplit
-func signalstack(s *stack) {
- var st stackt
- if s == nil {
- st.ss_flags = _SS_DISABLE
- } else {
- st.ss_sp = (*byte)(unsafe.Pointer(s.lo))
- st.ss_size = s.hi - s.lo
- st.ss_flags = 0
- }
- sigaltstack(&st, nil)
-}
-
-//go:nosplit
-//go:nowritebarrierrec
-func updatesigmask(m sigmask) {
- s := sigset(m[0])
- sigprocmask(_SIG_SETMASK, &s, nil)
-}
-
-func unblocksig(sig int32) {
- mask := sigset(1) << (uint32(sig) - 1)
- sigprocmask(_SIG_UNBLOCK, &mask, nil)
-}
diff --git a/src/runtime/os1_dragonfly.go b/src/runtime/os1_dragonfly.go
deleted file mode 100644
index d7044ae4b0..0000000000
--- a/src/runtime/os1_dragonfly.go
+++ /dev/null
@@ -1,270 +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 runtime
-
-import "unsafe"
-
-// From DragonFly's <sys/sysctl.h>
-const (
- _CTL_HW = 6
- _HW_NCPU = 3
-)
-
-var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}}
-
-func getncpu() int32 {
- mib := [2]uint32{_CTL_HW, _HW_NCPU}
- out := uint32(0)
- nout := unsafe.Sizeof(out)
- ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
- if ret >= 0 {
- return int32(out)
- }
- return 1
-}
-
-//go:nosplit
-func futexsleep(addr *uint32, val uint32, ns int64) {
- systemstack(func() {
- futexsleep1(addr, val, ns)
- })
-}
-
-func futexsleep1(addr *uint32, val uint32, ns int64) {
- var timeout int32
- if ns >= 0 {
- // The timeout is specified in microseconds - ensure that we
- // do not end up dividing to zero, which would put us to sleep
- // indefinitely...
- timeout = timediv(ns, 1000, nil)
- if timeout == 0 {
- timeout = 1
- }
- }
-
- // sys_umtx_sleep will return EWOULDBLOCK (EAGAIN) when the timeout
- // expires or EBUSY if the mutex value does not match.
- ret := sys_umtx_sleep(addr, int32(val), timeout)
- if ret >= 0 || ret == -_EINTR || ret == -_EAGAIN || ret == -_EBUSY {
- return
- }
-
- print("umtx_sleep addr=", addr, " val=", val, " ret=", ret, "\n")
- *(*int32)(unsafe.Pointer(uintptr(0x1005))) = 0x1005
-}
-
-//go:nosplit
-func futexwakeup(addr *uint32, cnt uint32) {
- ret := sys_umtx_wakeup(addr, int32(cnt))
- if ret >= 0 {
- return
- }
-
- systemstack(func() {
- print("umtx_wake_addr=", addr, " ret=", ret, "\n")
- *(*int32)(unsafe.Pointer(uintptr(0x1006))) = 0x1006
- })
-}
-
-func lwp_start(uintptr)
-
-// May run with m.p==nil, so write barriers are not allowed.
-//go:nowritebarrier
-func newosproc(mp *m, stk unsafe.Pointer) {
- if false {
- print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " lwp_start=", funcPC(lwp_start), " id=", mp.id, " ostk=", &mp, "\n")
- }
-
- var oset sigset
- sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
-
- params := lwpparams{
- start_func: funcPC(lwp_start),
- arg: unsafe.Pointer(mp),
- stack: uintptr(stk),
- tid1: unsafe.Pointer(&mp.procid),
- tid2: nil,
- }
-
- lwp_create(&params)
- sigprocmask(_SIG_SETMASK, &oset, nil)
-}
-
-func osinit() {
- ncpu = getncpu()
-}
-
-var urandom_dev = []byte("/dev/urandom\x00")
-
-//go:nosplit
-func getRandomData(r []byte) {
- fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
- n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
- closefd(fd)
- extendRandom(r, int(n))
-}
-
-func goenvs() {
- goenvs_unix()
-}
-
-// Called to initialize a new m (including the bootstrap m).
-// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
-func mpreinit(mp *m) {
- mp.gsignal = malg(32 * 1024)
- mp.gsignal.m = mp
-}
-
-//go:nosplit
-func msigsave(mp *m) {
- sigprocmask(_SIG_SETMASK, nil, &mp.sigmask)
-}
-
-//go:nosplit
-func msigrestore(sigmask sigset) {
- sigprocmask(_SIG_SETMASK, &sigmask, nil)
-}
-
-//go:nosplit
-func sigblock() {
- sigprocmask(_SIG_SETMASK, &sigset_all, nil)
-}
-
-// Called to initialize a new m (including the bootstrap m).
-// Called on the new thread, cannot allocate memory.
-func minit() {
- _g_ := getg()
-
- // m.procid is a uint64, but lwp_start writes an int32. Fix it up.
- _g_.m.procid = uint64(*(*int32)(unsafe.Pointer(&_g_.m.procid)))
-
- // Initialize signal handling.
-
- // On DragonFly a thread created by pthread_create inherits
- // the signal stack of the creating thread. We always create
- // a new signal stack here, to avoid having two Go threads
- // using the same signal stack. This breaks the case of a
- // thread created in C that calls sigaltstack and then calls a
- // Go function, because we will lose track of the C code's
- // sigaltstack, but it's the best we can do.
- signalstack(&_g_.m.gsignal.stack)
- _g_.m.newSigstack = true
-
- // restore signal mask from m.sigmask and unblock essential signals
- nmask := _g_.m.sigmask
- for i := range sigtable {
- if sigtable[i].flags&_SigUnblock != 0 {
- nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
- }
- }
- sigprocmask(_SIG_SETMASK, &nmask, nil)
-}
-
-// Called from dropm to undo the effect of an minit.
-//go:nosplit
-func unminit() {
- if getg().m.newSigstack {
- signalstack(nil)
- }
-}
-
-func memlimit() uintptr {
- /*
- TODO: Convert to Go when something actually uses the result.
-
- Rlimit rl;
- extern byte runtime·text[], runtime·end[];
- uintptr used;
-
- if(runtime·getrlimit(RLIMIT_AS, &rl) != 0)
- return 0;
- if(rl.rlim_cur >= 0x7fffffff)
- return 0;
-
- // Estimate our VM footprint excluding the heap.
- // Not an exact science: use size of binary plus
- // some room for thread stacks.
- used = runtime·end - runtime·text + (64<<20);
- if(used >= rl.rlim_cur)
- return 0;
-
- // If there's not at least 16 MB left, we're probably
- // not going to be able to do much. Treat as no limit.
- rl.rlim_cur -= used;
- if(rl.rlim_cur < (16<<20))
- return 0;
-
- return rl.rlim_cur - used;
- */
- return 0
-}
-
-func sigtramp()
-
-type sigactiont struct {
- sa_sigaction uintptr
- sa_flags int32
- sa_mask sigset
-}
-
-//go:nosplit
-//go:nowritebarrierrec
-func setsig(i int32, fn uintptr, restart bool) {
- var sa sigactiont
- sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK
- if restart {
- sa.sa_flags |= _SA_RESTART
- }
- sa.sa_mask = sigset_all
- if fn == funcPC(sighandler) {
- fn = funcPC(sigtramp)
- }
- sa.sa_sigaction = fn
- sigaction(i, &sa, nil)
-}
-
-//go:nosplit
-//go:nowritebarrierrec
-func setsigstack(i int32) {
- throw("setsigstack")
-}
-
-//go:nosplit
-//go:nowritebarrierrec
-func getsig(i int32) uintptr {
- var sa sigactiont
- sigaction(i, nil, &sa)
- if sa.sa_sigaction == funcPC(sigtramp) {
- return funcPC(sighandler)
- }
- return sa.sa_sigaction
-}
-
-//go:nosplit
-func signalstack(s *stack) {
- var st sigaltstackt
- if s == nil {
- st.ss_flags = _SS_DISABLE
- } else {
- st.ss_sp = s.lo
- st.ss_size = s.hi - s.lo
- st.ss_flags = 0
- }
- sigaltstack(&st, nil)
-}
-
-//go:nosplit
-//go:nowritebarrierrec
-func updatesigmask(m sigmask) {
- var mask sigset
- copy(mask.__bits[:], m[:])
- sigprocmask(_SIG_SETMASK, &mask, nil)
-}
-
-func unblocksig(sig int32) {
- var mask sigset
- mask.__bits[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31)
- sigprocmask(_SIG_UNBLOCK, &mask, nil)
-}
diff --git a/src/runtime/os1_linux.go b/src/runtime/os1_linux.go
deleted file mode 100644
index 726dd649fe..0000000000
--- a/src/runtime/os1_linux.go
+++ /dev/null
@@ -1,393 +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 runtime
-
-import (
- "runtime/internal/sys"
- "unsafe"
-)
-
-// Linux futex.
-//
-// futexsleep(uint32 *addr, uint32 val)
-// futexwakeup(uint32 *addr)
-//
-// Futexsleep atomically checks if *addr == val and if so, sleeps on addr.
-// Futexwakeup wakes up threads sleeping on addr.
-// Futexsleep is allowed to wake up spuriously.
-
-const (
- _FUTEX_WAIT = 0
- _FUTEX_WAKE = 1
-)
-
-// Atomically,
-// if(*addr == val) sleep
-// Might be woken up spuriously; that's allowed.
-// Don't sleep longer than ns; ns < 0 means forever.
-//go:nosplit
-func futexsleep(addr *uint32, val uint32, ns int64) {
- var ts timespec
-
- // Some Linux kernels have a bug where futex of
- // FUTEX_WAIT returns an internal error code
- // as an errno. Libpthread ignores the return value
- // here, and so can we: as it says a few lines up,
- // spurious wakeups are allowed.
- if ns < 0 {
- futex(unsafe.Pointer(addr), _FUTEX_WAIT, val, nil, nil, 0)
- return
- }
-
- // It's difficult to live within the no-split stack limits here.
- // On ARM and 386, a 64-bit divide invokes a general software routine
- // that needs more stack than we can afford. So we use timediv instead.
- // But on real 64-bit systems, where words are larger but the stack limit
- // is not, even timediv is too heavy, and we really need to use just an
- // ordinary machine instruction.
- if sys.PtrSize == 8 {
- ts.set_sec(ns / 1000000000)
- ts.set_nsec(int32(ns % 1000000000))
- } else {
- ts.tv_nsec = 0
- ts.set_sec(int64(timediv(ns, 1000000000, (*int32)(unsafe.Pointer(&ts.tv_nsec)))))
- }
- futex(unsafe.Pointer(addr), _FUTEX_WAIT, val, unsafe.Pointer(&ts), nil, 0)
-}
-
-// If any procs are sleeping on addr, wake up at most cnt.
-//go:nosplit
-func futexwakeup(addr *uint32, cnt uint32) {
- ret := futex(unsafe.Pointer(addr), _FUTEX_WAKE, cnt, nil, nil, 0)
- if ret >= 0 {
- return
- }
-
- // I don't know that futex wakeup can return
- // EAGAIN or EINTR, but if it does, it would be
- // safe to loop and call futex again.
- systemstack(func() {
- print("futexwakeup addr=", addr, " returned ", ret, "\n")
- })
-
- *(*int32)(unsafe.Pointer(uintptr(0x1006))) = 0x1006
-}
-
-func getproccount() int32 {
- // This buffer is huge (8 kB) but we are on the system stack
- // and there should be plenty of space (64 kB).
- // Also this is a leaf, so we're not holding up the memory for long.
- // See golang.org/issue/11823.
- // The suggested behavior here is to keep trying with ever-larger
- // buffers, but we don't have a dynamic memory allocator at the
- // moment, so that's a bit tricky and seems like overkill.
- const maxCPUs = 64 * 1024
- var buf [maxCPUs / (sys.PtrSize * 8)]uintptr
- r := sched_getaffinity(0, unsafe.Sizeof(buf), &buf[0])
- n := int32(0)
- for _, v := range buf[:r/sys.PtrSize] {
- for v != 0 {
- n += int32(v & 1)
- v >>= 1
- }
- }
- if n == 0 {
- n = 1
- }
- return n
-}
-
-// Clone, the Linux rfork.
-const (
- _CLONE_VM = 0x100
- _CLONE_FS = 0x200
- _CLONE_FILES = 0x400
- _CLONE_SIGHAND = 0x800
- _CLONE_PTRACE = 0x2000
- _CLONE_VFORK = 0x4000
- _CLONE_PARENT = 0x8000
- _CLONE_THREAD = 0x10000
- _CLONE_NEWNS = 0x20000
- _CLONE_SYSVSEM = 0x40000
- _CLONE_SETTLS = 0x80000
- _CLONE_PARENT_SETTID = 0x100000
- _CLONE_CHILD_CLEARTID = 0x200000
- _CLONE_UNTRACED = 0x800000
- _CLONE_CHILD_SETTID = 0x1000000
- _CLONE_STOPPED = 0x2000000
- _CLONE_NEWUTS = 0x4000000
- _CLONE_NEWIPC = 0x8000000
-
- cloneFlags = _CLONE_VM | /* share memory */
- _CLONE_FS | /* share cwd, etc */
- _CLONE_FILES | /* share fd table */
- _CLONE_SIGHAND | /* share sig handler table */
- _CLONE_THREAD /* revisit - okay for now */
-)
-
-// May run with m.p==nil, so write barriers are not allowed.
-//go:nowritebarrier
-func newosproc(mp *m, stk unsafe.Pointer) {
- /*
- * note: strace gets confused if we use CLONE_PTRACE here.
- */
- if false {
- print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " clone=", funcPC(clone), " id=", mp.id, " ostk=", &mp, "\n")
- }
-
- // Disable signals during clone, so that the new thread starts
- // with signals disabled. It will enable them in minit.
- var oset sigset
- rtsigprocmask(_SIG_SETMASK, &sigset_all, &oset, int32(unsafe.Sizeof(oset)))
- ret := clone(cloneFlags, stk, unsafe.Pointer(mp), unsafe.Pointer(mp.g0), unsafe.Pointer(funcPC(mstart)))
- rtsigprocmask(_SIG_SETMASK, &oset, nil, int32(unsafe.Sizeof(oset)))
-
- if ret < 0 {
- print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", -ret, ")\n")
- throw("newosproc")
- }
-}
-
-// Version of newosproc that doesn't require a valid G.
-//go:nosplit
-func newosproc0(stacksize uintptr, fn unsafe.Pointer) {
- stack := sysAlloc(stacksize, &memstats.stacks_sys)
- if stack == nil {
- write(2, unsafe.Pointer(&failallocatestack[0]), int32(len(failallocatestack)))
- exit(1)
- }
- ret := clone(cloneFlags, unsafe.Pointer(uintptr(stack)+stacksize), nil, nil, fn)
- if ret < 0 {
- write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
- exit(1)
- }
-}
-
-var failallocatestack = []byte("runtime: failed to allocate stack for the new OS thread\n")
-var failthreadcreate = []byte("runtime: failed to create new OS thread\n")
-
-func osinit() {
- ncpu = getproccount()
-}
-
-var urandom_dev = []byte("/dev/urandom\x00")
-
-func getRandomData(r []byte) {
- if startupRandomData != nil {
- n := copy(r, startupRandomData)
- extendRandom(r, n)
- return
- }
- fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
- n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
- closefd(fd)
- extendRandom(r, int(n))
-}
-
-func goenvs() {
- goenvs_unix()
-}
-
-// Called to do synchronous initialization of Go code built with
-// -buildmode=c-archive or -buildmode=c-shared.
-// None of the Go runtime is initialized.
-//go:nosplit
-//go:nowritebarrierrec
-func libpreinit() {
- initsig(true)
-}
-
-// Called to initialize a new m (including the bootstrap m).
-// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
-func mpreinit(mp *m) {
- mp.gsignal = malg(32 * 1024) // Linux wants >= 2K
- mp.gsignal.m = mp
-}
-
-//go:nosplit
-func msigsave(mp *m) {
- smask := &mp.sigmask
- rtsigprocmask(_SIG_SETMASK, nil, smask, int32(unsafe.Sizeof(*smask)))
-}
-
-//go:nosplit
-func msigrestore(sigmask sigset) {
- rtsigprocmask(_SIG_SETMASK, &sigmask, nil, int32(unsafe.Sizeof(sigmask)))
-}
-
-//go:nosplit
-func sigblock() {
- rtsigprocmask(_SIG_SETMASK, &sigset_all, nil, int32(unsafe.Sizeof(sigset_all)))
-}
-
-func gettid() uint32
-
-// Called to initialize a new m (including the bootstrap m).
-// Called on the new thread, cannot allocate memory.
-func minit() {
- // Initialize signal handling.
- _g_ := getg()
-
- var st sigaltstackt
- sigaltstack(nil, &st)
- if st.ss_flags&_SS_DISABLE != 0 {
- signalstack(&_g_.m.gsignal.stack)
- _g_.m.newSigstack = true
- } else {
- // Use existing signal stack.
- stsp := uintptr(unsafe.Pointer(st.ss_sp))
- _g_.m.gsignal.stack.lo = stsp
- _g_.m.gsignal.stack.hi = stsp + st.ss_size
- _g_.m.gsignal.stackguard0 = stsp + _StackGuard
- _g_.m.gsignal.stackguard1 = stsp + _StackGuard
- _g_.m.gsignal.stackAlloc = st.ss_size
- _g_.m.newSigstack = false
- }
-
- // for debuggers, in case cgo created the thread
- _g_.m.procid = uint64(gettid())
-
- // restore signal mask from m.sigmask and unblock essential signals
- nmask := _g_.m.sigmask
- for i := range sigtable {
- if sigtable[i].flags&_SigUnblock != 0 {
- sigdelset(&nmask, i)
- }
- }
- rtsigprocmask(_SIG_SETMASK, &nmask, nil, int32(unsafe.Sizeof(nmask)))
-}
-
-// Called from dropm to undo the effect of an minit.
-//go:nosplit
-func unminit() {
- if getg().m.newSigstack {
- signalstack(nil)
- }
-}
-
-func memlimit() uintptr {
- /*
- TODO: Convert to Go when something actually uses the result.
-
- Rlimit rl;
- extern byte runtime·text[], runtime·end[];
- uintptr used;
-
- if(runtime·getrlimit(RLIMIT_AS, &rl) != 0)
- return 0;
- if(rl.rlim_cur >= 0x7fffffff)
- return 0;
-
- // Estimate our VM footprint excluding the heap.
- // Not an exact science: use size of binary plus
- // some room for thread stacks.
- used = runtime·end - runtime·text + (64<<20);
- if(used >= rl.rlim_cur)
- return 0;
-
- // If there's not at least 16 MB left, we're probably
- // not going to be able to do much. Treat as no limit.
- rl.rlim_cur -= used;
- if(rl.rlim_cur < (16<<20))
- return 0;
-
- return rl.rlim_cur - used;
- */
-
- return 0
-}
-
-//#ifdef GOARCH_386
-//#define sa_handler k_sa_handler
-//#endif
-
-func sigreturn()
-func sigtramp()
-func cgoSigtramp()
-
-//go:nosplit
-//go:nowritebarrierrec
-func setsig(i int32, fn uintptr, restart bool) {
- var sa sigactiont
- memclr(unsafe.Pointer(&sa), unsafe.Sizeof(sa))
- sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTORER
- if restart {
- sa.sa_flags |= _SA_RESTART
- }
- sigfillset(&sa.sa_mask)
- // Although Linux manpage says "sa_restorer element is obsolete and
- // should not be used". x86_64 kernel requires it. Only use it on
- // x86.
- if GOARCH == "386" || GOARCH == "amd64" {
- sa.sa_restorer = funcPC(sigreturn)
- }
- if fn == funcPC(sighandler) {
- if iscgo {
- fn = funcPC(cgoSigtramp)
- } else {
- fn = funcPC(sigtramp)
- }
- }
- sa.sa_handler = fn
- rt_sigaction(uintptr(i), &sa, nil, unsafe.Sizeof(sa.sa_mask))
-}
-
-//go:nosplit
-//go:nowritebarrierrec
-func setsigstack(i int32) {
- var sa sigactiont
- if rt_sigaction(uintptr(i), nil, &sa, unsafe.Sizeof(sa.sa_mask)) != 0 {
- throw("rt_sigaction failure")
- }
- if sa.sa_handler == 0 || sa.sa_handler == _SIG_DFL || sa.sa_handler == _SIG_IGN || sa.sa_flags&_SA_ONSTACK != 0 {
- return
- }
- sa.sa_flags |= _SA_ONSTACK
- if rt_sigaction(uintptr(i), &sa, nil, unsafe.Sizeof(sa.sa_mask)) != 0 {
- throw("rt_sigaction failure")
- }
-}
-
-//go:nosplit
-//go:nowritebarrierrec
-func getsig(i int32) uintptr {
- var sa sigactiont
-
- memclr(unsafe.Pointer(&sa), unsafe.Sizeof(sa))
- if rt_sigaction(uintptr(i), nil, &sa, unsafe.Sizeof(sa.sa_mask)) != 0 {
- throw("rt_sigaction read failure")
- }
- if sa.sa_handler == funcPC(sigtramp) || sa.sa_handler == funcPC(cgoSigtramp) {
- return funcPC(sighandler)
- }
- return sa.sa_handler
-}
-
-//go:nosplit
-func signalstack(s *stack) {
- var st sigaltstackt
- if s == nil {
- st.ss_flags = _SS_DISABLE
- } else {
- st.ss_sp = (*byte)(unsafe.Pointer(s.lo))
- st.ss_size = s.hi - s.lo
- st.ss_flags = 0
- }
- sigaltstack(&st, nil)
-}
-
-//go:nosplit
-//go:nowritebarrierrec
-func updatesigmask(m sigmask) {
- var mask sigset
- sigcopyset(&mask, m)
- rtsigprocmask(_SIG_SETMASK, &mask, nil, int32(unsafe.Sizeof(mask)))
-}
-
-func unblocksig(sig int32) {
- var mask sigset
- sigaddset(&mask, int(sig))
- rtsigprocmask(_SIG_UNBLOCK, &mask, nil, int32(unsafe.Sizeof(mask)))
-}
diff --git a/src/runtime/os1_linux_generic.go b/src/runtime/os1_linux_generic.go
deleted file mode 100644
index 2c8b743aeb..0000000000
--- a/src/runtime/os1_linux_generic.go
+++ /dev/null
@@ -1,27 +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 !mips64
-// +build !mips64le
-// +build linux
-
-package runtime
-
-var sigset_all = sigset{^uint32(0), ^uint32(0)}
-
-func sigaddset(mask *sigset, i int) {
- (*mask)[(i-1)/32] |= 1 << ((uint32(i) - 1) & 31)
-}
-
-func sigdelset(mask *sigset, i int) {
- (*mask)[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
-}
-
-func sigfillset(mask *uint64) {
- *mask = ^uint64(0)
-}
-
-func sigcopyset(mask *sigset, m sigmask) {
- copy((*mask)[:], m[:])
-}
diff --git a/src/runtime/os1_linux_mips64x.go b/src/runtime/os1_linux_mips64x.go
deleted file mode 100644
index 701e979102..0000000000
--- a/src/runtime/os1_linux_mips64x.go
+++ /dev/null
@@ -1,26 +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.
-
-// +build mips64 mips64le
-// +build linux
-
-package runtime
-
-var sigset_all = sigset{^uint64(0), ^uint64(0)}
-
-func sigaddset(mask *sigset, i int) {
- (*mask)[(i-1)/64] |= 1 << ((uint32(i) - 1) & 63)
-}
-
-func sigdelset(mask *sigset, i int) {
- (*mask)[(i-1)/64] &^= 1 << ((uint32(i) - 1) & 63)
-}
-
-func sigfillset(mask *[2]uint64) {
- (*mask)[0], (*mask)[1] = ^uint64(0), ^uint64(0)
-}
-
-func sigcopyset(mask *sigset, m sigmask) {
- (*mask)[0] = uint64(m[0]) | uint64(m[1])<<32
-}
diff --git a/src/runtime/os1_netbsd.go b/src/runtime/os1_netbsd.go
deleted file mode 100644
index 3c3b64186d..0000000000
--- a/src/runtime/os1_netbsd.go
+++ /dev/null
@@ -1,275 +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 runtime
-
-import (
- "runtime/internal/atomic"
- "unsafe"
-)
-
-const (
- _ESRCH = 3
- _ETIMEDOUT = 60
-
- // From NetBSD's <sys/time.h>
- _CLOCK_REALTIME = 0
- _CLOCK_VIRTUAL = 1
- _CLOCK_PROF = 2
- _CLOCK_MONOTONIC = 3
-)
-
-var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}}
-
-// From NetBSD's <sys/sysctl.h>
-const (
- _CTL_HW = 6
- _HW_NCPU = 3
-)
-
-func getncpu() int32 {
- mib := [2]uint32{_CTL_HW, _HW_NCPU}
- out := uint32(0)
- nout := unsafe.Sizeof(out)
- ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
- if ret >= 0 {
- return int32(out)
- }
- return 1
-}
-
-//go:nosplit
-func semacreate(mp *m) {
-}
-
-//go:nosplit
-func semasleep(ns int64) int32 {
- _g_ := getg()
-
- // Compute sleep deadline.
- var tsp *timespec
- if ns >= 0 {
- var ts timespec
- var nsec int32
- ns += nanotime()
- ts.set_sec(timediv(ns, 1000000000, &nsec))
- ts.set_nsec(nsec)
- tsp = &ts
- }
-
- for {
- v := atomic.Load(&_g_.m.waitsemacount)
- if v > 0 {
- if atomic.Cas(&_g_.m.waitsemacount, v, v-1) {
- return 0 // semaphore acquired
- }
- continue
- }
-
- // Sleep until unparked by semawakeup or timeout.
- ret := lwp_park(tsp, 0, unsafe.Pointer(&_g_.m.waitsemacount), nil)
- if ret == _ETIMEDOUT {
- return -1
- }
- }
-}
-
-//go:nosplit
-func semawakeup(mp *m) {
- atomic.Xadd(&mp.waitsemacount, 1)
- // From NetBSD's _lwp_unpark(2) manual:
- // "If the target LWP is not currently waiting, it will return
- // immediately upon the next call to _lwp_park()."
- ret := lwp_unpark(int32(mp.procid), unsafe.Pointer(&mp.waitsemacount))
- if ret != 0 && ret != _ESRCH {
- // semawakeup can be called on signal stack.
- systemstack(func() {
- print("thrwakeup addr=", &mp.waitsemacount, " sem=", mp.waitsemacount, " ret=", ret, "\n")
- })
- }
-}
-
-// May run with m.p==nil, so write barriers are not allowed.
-//go:nowritebarrier
-func newosproc(mp *m, stk unsafe.Pointer) {
- if false {
- print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " id=", mp.id, " ostk=", &mp, "\n")
- }
-
- var uc ucontextt
- getcontext(unsafe.Pointer(&uc))
-
- uc.uc_flags = _UC_SIGMASK | _UC_CPU
- uc.uc_link = nil
- uc.uc_sigmask = sigset_all
-
- lwp_mcontext_init(&uc.uc_mcontext, stk, mp, mp.g0, funcPC(netbsdMstart))
-
- ret := lwp_create(unsafe.Pointer(&uc), 0, unsafe.Pointer(&mp.procid))
- if ret < 0 {
- print("runtime: failed to create new OS thread (have ", mcount()-1, " already; errno=", -ret, ")\n")
- throw("runtime.newosproc")
- }
-}
-
-// netbsdMStart is the function call that starts executing a newly
-// created thread. On NetBSD, a new thread inherits the signal stack
-// of the creating thread. That confuses minit, so we remove that
-// signal stack here before calling the regular mstart. It's a bit
-// baroque to remove a signal stack here only to add one in minit, but
-// it's a simple change that keeps NetBSD working like other OS's.
-// At this point all signals are blocked, so there is no race.
-//go:nosplit
-func netbsdMstart() {
- signalstack(nil)
- mstart()
-}
-
-func osinit() {
- ncpu = getncpu()
-}
-
-var urandom_dev = []byte("/dev/urandom\x00")
-
-//go:nosplit
-func getRandomData(r []byte) {
- fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
- n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
- closefd(fd)
- extendRandom(r, int(n))
-}
-
-func goenvs() {
- goenvs_unix()
-}
-
-// Called to initialize a new m (including the bootstrap m).
-// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
-func mpreinit(mp *m) {
- mp.gsignal = malg(32 * 1024)
- mp.gsignal.m = mp
-}
-
-//go:nosplit
-func msigsave(mp *m) {
- sigprocmask(_SIG_SETMASK, nil, &mp.sigmask)
-}
-
-//go:nosplit
-func msigrestore(sigmask sigset) {
- sigprocmask(_SIG_SETMASK, &sigmask, nil)
-}
-
-//go:nosplit
-func sigblock() {
- sigprocmask(_SIG_SETMASK, &sigset_all, nil)
-}
-
-// Called to initialize a new m (including the bootstrap m).
-// Called on the new thread, cannot allocate memory.
-func minit() {
- _g_ := getg()
- _g_.m.procid = uint64(lwp_self())
-
- // Initialize signal handling.
-
- // On NetBSD a thread created by pthread_create inherits the
- // signal stack of the creating thread. We always create a
- // new signal stack here, to avoid having two Go threads using
- // the same signal stack. This breaks the case of a thread
- // created in C that calls sigaltstack and then calls a Go
- // function, because we will lose track of the C code's
- // sigaltstack, but it's the best we can do.
- signalstack(&_g_.m.gsignal.stack)
- _g_.m.newSigstack = true
-
- // restore signal mask from m.sigmask and unblock essential signals
- nmask := _g_.m.sigmask
- for i := range sigtable {
- if sigtable[i].flags&_SigUnblock != 0 {
- nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
- }
- }
- sigprocmask(_SIG_SETMASK, &nmask, nil)
-}
-
-// Called from dropm to undo the effect of an minit.
-//go:nosplit
-func unminit() {
- if getg().m.newSigstack {
- signalstack(nil)
- }
-}
-
-func memlimit() uintptr {
- return 0
-}
-
-func sigtramp()
-
-type sigactiont struct {
- sa_sigaction uintptr
- sa_mask sigset
- sa_flags int32
-}
-
-//go:nosplit
-//go:nowritebarrierrec
-func setsig(i int32, fn uintptr, restart bool) {
- var sa sigactiont
- sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK
- if restart {
- sa.sa_flags |= _SA_RESTART
- }
- sa.sa_mask = sigset_all
- if fn == funcPC(sighandler) {
- fn = funcPC(sigtramp)
- }
- sa.sa_sigaction = fn
- sigaction(i, &sa, nil)
-}
-
-//go:nosplit
-//go:nowritebarrierrec
-func setsigstack(i int32) {
- throw("setsigstack")
-}
-
-//go:nosplit
-//go:nowritebarrierrec
-func getsig(i int32) uintptr {
- var sa sigactiont
- sigaction(i, nil, &sa)
- if sa.sa_sigaction == funcPC(sigtramp) {
- return funcPC(sighandler)
- }
- return sa.sa_sigaction
-}
-
-//go:nosplit
-func signalstack(s *stack) {
- var st sigaltstackt
- if s == nil {
- st.ss_flags = _SS_DISABLE
- } else {
- st.ss_sp = s.lo
- st.ss_size = s.hi - s.lo
- st.ss_flags = 0
- }
- sigaltstack(&st, nil)
-}
-
-//go:nosplit
-//go:nowritebarrierrec
-func updatesigmask(m sigmask) {
- var mask sigset
- copy(mask.__bits[:], m[:])
- sigprocmask(_SIG_SETMASK, &mask, nil)
-}
-
-func unblocksig(sig int32) {
- var mask sigset
- mask.__bits[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31)
- sigprocmask(_SIG_UNBLOCK, &mask, nil)
-}
diff --git a/src/runtime/os1_plan9.go b/src/runtime/os1_plan9.go
index 2c257442ba..eb7a0c6481 100644
--- a/src/runtime/os1_plan9.go
+++ b/src/runtime/os1_plan9.go
@@ -17,10 +17,10 @@ func mpreinit(mp *m) {
// Initialize stack and goroutine for note handling.
mp.gsignal = malg(32 * 1024)
mp.gsignal.m = mp
- mp.notesig = (*int8)(mallocgc(_ERRMAX, nil, _FlagNoScan))
+ mp.notesig = (*int8)(mallocgc(_ERRMAX, nil, true))
// Initialize stack for handling strings from the
// errstr system call, as used in package syscall.
- mp.errstr = (*byte)(mallocgc(_ERRMAX, nil, _FlagNoScan))
+ mp.errstr = (*byte)(mallocgc(_ERRMAX, nil, true))
}
func msigsave(mp *m) {
diff --git a/src/runtime/os1_windows.go b/src/runtime/os1_windows.go
deleted file mode 100644
index 315dd9816a..0000000000
--- a/src/runtime/os1_windows.go
+++ /dev/null
@@ -1,703 +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 runtime
-
-import (
- "runtime/internal/atomic"
- "unsafe"
-)
-
-//go:cgo_import_dynamic runtime._AddVectoredExceptionHandler AddVectoredExceptionHandler%2 "kernel32.dll"
-//go:cgo_import_dynamic runtime._CloseHandle CloseHandle%1 "kernel32.dll"
-//go:cgo_import_dynamic runtime._CreateEventA CreateEventA%4 "kernel32.dll"
-//go:cgo_import_dynamic runtime._CreateIoCompletionPort CreateIoCompletionPort%4 "kernel32.dll"
-//go:cgo_import_dynamic runtime._CreateThread CreateThread%6 "kernel32.dll"
-//go:cgo_import_dynamic runtime._CreateWaitableTimerA CreateWaitableTimerA%3 "kernel32.dll"
-//go:cgo_import_dynamic runtime._CryptAcquireContextW CryptAcquireContextW%5 "advapi32.dll"
-//go:cgo_import_dynamic runtime._CryptGenRandom CryptGenRandom%3 "advapi32.dll"
-//go:cgo_import_dynamic runtime._CryptReleaseContext CryptReleaseContext%2 "advapi32.dll"
-//go:cgo_import_dynamic runtime._DuplicateHandle DuplicateHandle%7 "kernel32.dll"
-//go:cgo_import_dynamic runtime._ExitProcess ExitProcess%1 "kernel32.dll"
-//go:cgo_import_dynamic runtime._FreeEnvironmentStringsW FreeEnvironmentStringsW%1 "kernel32.dll"
-//go:cgo_import_dynamic runtime._GetConsoleMode GetConsoleMode%2 "kernel32.dll"
-//go:cgo_import_dynamic runtime._GetEnvironmentStringsW GetEnvironmentStringsW%0 "kernel32.dll"
-//go:cgo_import_dynamic runtime._GetProcAddress GetProcAddress%2 "kernel32.dll"
-//go:cgo_import_dynamic runtime._GetProcessAffinityMask GetProcessAffinityMask%3 "kernel32.dll"
-//go:cgo_import_dynamic runtime._GetQueuedCompletionStatus GetQueuedCompletionStatus%5 "kernel32.dll"
-//go:cgo_import_dynamic runtime._GetStdHandle GetStdHandle%1 "kernel32.dll"
-//go:cgo_import_dynamic runtime._GetSystemInfo GetSystemInfo%1 "kernel32.dll"
-//go:cgo_import_dynamic runtime._GetThreadContext GetThreadContext%2 "kernel32.dll"
-//go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 "kernel32.dll"
-//go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA%1 "kernel32.dll"
-//go:cgo_import_dynamic runtime._NtWaitForSingleObject NtWaitForSingleObject%3 "ntdll.dll"
-//go:cgo_import_dynamic runtime._ResumeThread ResumeThread%1 "kernel32.dll"
-//go:cgo_import_dynamic runtime._SetConsoleCtrlHandler SetConsoleCtrlHandler%2 "kernel32.dll"
-//go:cgo_import_dynamic runtime._SetErrorMode SetErrorMode%1 "kernel32.dll"
-//go:cgo_import_dynamic runtime._SetEvent SetEvent%1 "kernel32.dll"
-//go:cgo_import_dynamic runtime._SetProcessPriorityBoost SetProcessPriorityBoost%2 "kernel32.dll"
-//go:cgo_import_dynamic runtime._SetThreadPriority SetThreadPriority%2 "kernel32.dll"
-//go:cgo_import_dynamic runtime._SetUnhandledExceptionFilter SetUnhandledExceptionFilter%1 "kernel32.dll"
-//go:cgo_import_dynamic runtime._SetWaitableTimer SetWaitableTimer%6 "kernel32.dll"
-//go:cgo_import_dynamic runtime._SuspendThread SuspendThread%1 "kernel32.dll"
-//go:cgo_import_dynamic runtime._SwitchToThread SwitchToThread%0 "kernel32.dll"
-//go:cgo_import_dynamic runtime._VirtualAlloc VirtualAlloc%4 "kernel32.dll"
-//go:cgo_import_dynamic runtime._VirtualFree VirtualFree%3 "kernel32.dll"
-//go:cgo_import_dynamic runtime._WSAGetOverlappedResult WSAGetOverlappedResult%5 "ws2_32.dll"
-//go:cgo_import_dynamic runtime._WaitForSingleObject WaitForSingleObject%2 "kernel32.dll"
-//go:cgo_import_dynamic runtime._WriteConsoleW WriteConsoleW%5 "kernel32.dll"
-//go:cgo_import_dynamic runtime._WriteFile WriteFile%5 "kernel32.dll"
-
-var (
- // Following syscalls are available on every Windows PC.
- // All these variables are set by the Windows executable
- // loader before the Go program starts.
- _AddVectoredExceptionHandler,
- _CloseHandle,
- _CreateEventA,
- _CreateIoCompletionPort,
- _CreateThread,
- _CreateWaitableTimerA,
- _CryptAcquireContextW,
- _CryptGenRandom,
- _CryptReleaseContext,
- _DuplicateHandle,
- _ExitProcess,
- _FreeEnvironmentStringsW,
- _GetConsoleMode,
- _GetEnvironmentStringsW,
- _GetProcAddress,
- _GetProcessAffinityMask,
- _GetQueuedCompletionStatus,
- _GetStdHandle,
- _GetSystemInfo,
- _GetThreadContext,
- _LoadLibraryW,
- _LoadLibraryA,
- _NtWaitForSingleObject,
- _ResumeThread,
- _SetConsoleCtrlHandler,
- _SetErrorMode,
- _SetEvent,
- _SetProcessPriorityBoost,
- _SetThreadPriority,
- _SetUnhandledExceptionFilter,
- _SetWaitableTimer,
- _SuspendThread,
- _SwitchToThread,
- _VirtualAlloc,
- _VirtualFree,
- _WSAGetOverlappedResult,
- _WaitForSingleObject,
- _WriteConsoleW,
- _WriteFile stdFunction
-
- // Following syscalls are only available on some Windows PCs.
- // We will load syscalls, if available, before using them.
- _AddDllDirectory,
- _AddVectoredContinueHandler,
- _GetQueuedCompletionStatusEx,
- _LoadLibraryExW,
- _ stdFunction
-)
-
-type sigset struct{}
-
-// Call a Windows function with stdcall conventions,
-// and switch to os stack during the call.
-func asmstdcall(fn unsafe.Pointer)
-
-var asmstdcallAddr unsafe.Pointer
-
-func windowsFindfunc(name []byte, lib uintptr) stdFunction {
- f := stdcall2(_GetProcAddress, lib, uintptr(unsafe.Pointer(&name[0])))
- return stdFunction(unsafe.Pointer(f))
-}
-
-func loadOptionalSyscalls() {
- var (
- kernel32dll = []byte("kernel32.dll\000")
- addVectoredContinueHandler = []byte("AddVectoredContinueHandler\000")
- getQueuedCompletionStatusEx = []byte("GetQueuedCompletionStatusEx\000")
- addDllDirectory = []byte("AddDllDirectory\000")
- loadLibraryExW = []byte("LoadLibraryExW\000")
- )
-
- k32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&kernel32dll[0])))
- if k32 == 0 {
- throw("kernel32.dll not found")
- }
- _AddDllDirectory = windowsFindfunc(addDllDirectory, k32)
- _AddVectoredContinueHandler = windowsFindfunc(addVectoredContinueHandler, k32)
- _GetQueuedCompletionStatusEx = windowsFindfunc(getQueuedCompletionStatusEx, k32)
- _LoadLibraryExW = windowsFindfunc(loadLibraryExW, k32)
-}
-
-//go:nosplit
-func getLoadLibrary() uintptr {
- return uintptr(unsafe.Pointer(_LoadLibraryW))
-}
-
-//go:nosplit
-func getLoadLibraryEx() uintptr {
- return uintptr(unsafe.Pointer(_LoadLibraryExW))
-}
-
-//go:nosplit
-func getGetProcAddress() uintptr {
- return uintptr(unsafe.Pointer(_GetProcAddress))
-}
-
-func getproccount() int32 {
- var mask, sysmask uintptr
- ret := stdcall3(_GetProcessAffinityMask, currentProcess, uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask)))
- if ret != 0 {
- n := 0
- maskbits := int(unsafe.Sizeof(mask) * 8)
- for i := 0; i < maskbits; i++ {
- if mask&(1<<uint(i)) != 0 {
- n++
- }
- }
- if n != 0 {
- return int32(n)
- }
- }
- // use GetSystemInfo if GetProcessAffinityMask fails
- var info systeminfo
- stdcall1(_GetSystemInfo, uintptr(unsafe.Pointer(&info)))
- return int32(info.dwnumberofprocessors)
-}
-
-const (
- currentProcess = ^uintptr(0) // -1 = current process
- currentThread = ^uintptr(1) // -2 = current thread
-)
-
-// in sys_windows_386.s and sys_windows_amd64.s
-func externalthreadhandler()
-
-// When loading DLLs, we prefer to use LoadLibraryEx with
-// LOAD_LIBRARY_SEARCH_* flags, if available. LoadLibraryEx is not
-// available on old Windows, though, and the LOAD_LIBRARY_SEARCH_*
-// flags are not available on some versions of Windows without a
-// security patch.
-//
-// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says:
-// "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows
-// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on
-// systems that have KB2533623 installed. To determine whether the
-// flags are available, use GetProcAddress to get the address of the
-// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories
-// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_*
-// flags can be used with LoadLibraryEx."
-var useLoadLibraryEx bool
-
-func osinit() {
- asmstdcallAddr = unsafe.Pointer(funcPC(asmstdcall))
- usleep2Addr = unsafe.Pointer(funcPC(usleep2))
- switchtothreadAddr = unsafe.Pointer(funcPC(switchtothread))
-
- setBadSignalMsg()
-
- loadOptionalSyscalls()
-
- useLoadLibraryEx = (_LoadLibraryExW != nil && _AddDllDirectory != nil)
-
- disableWER()
-
- externalthreadhandlerp = funcPC(externalthreadhandler)
-
- initExceptionHandler()
-
- stdcall2(_SetConsoleCtrlHandler, funcPC(ctrlhandler), 1)
-
- ncpu = getproccount()
-
- // Windows dynamic priority boosting assumes that a process has different types
- // of dedicated threads -- GUI, IO, computational, etc. Go processes use
- // equivalent threads that all do a mix of GUI, IO, computations, etc.
- // In such context dynamic priority boosting does nothing but harm, so we turn it off.
- stdcall2(_SetProcessPriorityBoost, currentProcess, 1)
-}
-
-//go:nosplit
-func getRandomData(r []byte) {
- const (
- prov_rsa_full = 1
- crypt_verifycontext = 0xF0000000
- )
- var handle uintptr
- n := 0
- if stdcall5(_CryptAcquireContextW, uintptr(unsafe.Pointer(&handle)), 0, 0, prov_rsa_full, crypt_verifycontext) != 0 {
- if stdcall3(_CryptGenRandom, handle, uintptr(len(r)), uintptr(unsafe.Pointer(&r[0]))) != 0 {
- n = len(r)
- }
- stdcall2(_CryptReleaseContext, handle, 0)
- }
- extendRandom(r, n)
-}
-
-func goenvs() {
- // strings is a pointer to environment variable pairs in the form:
- // "envA=valA\x00envB=valB\x00\x00" (in UTF-16)
- // Two consecutive zero bytes end the list.
- strings := unsafe.Pointer(stdcall0(_GetEnvironmentStringsW))
- p := (*[1 << 24]uint16)(strings)[:]
-
- n := 0
- for from, i := 0, 0; true; i++ {
- if p[i] == 0 {
- // empty string marks the end
- if i == from {
- break
- }
- from = i + 1
- n++
- }
- }
- envs = make([]string, n)
-
- for i := range envs {
- envs[i] = gostringw(&p[0])
- for p[0] != 0 {
- p = p[1:]
- }
- p = p[1:] // skip nil byte
- }
-
- stdcall1(_FreeEnvironmentStringsW, uintptr(strings))
-}
-
-//go:nosplit
-func exit(code int32) {
- stdcall1(_ExitProcess, uintptr(code))
-}
-
-//go:nosplit
-func write(fd uintptr, buf unsafe.Pointer, n int32) int32 {
- const (
- _STD_OUTPUT_HANDLE = ^uintptr(10) // -11
- _STD_ERROR_HANDLE = ^uintptr(11) // -12
- )
- var handle uintptr
- switch fd {
- case 1:
- handle = stdcall1(_GetStdHandle, _STD_OUTPUT_HANDLE)
- case 2:
- handle = stdcall1(_GetStdHandle, _STD_ERROR_HANDLE)
- default:
- // assume fd is real windows handle.
- handle = fd
- }
- isASCII := true
- b := (*[1 << 30]byte)(buf)[:n]
- for _, x := range b {
- if x >= 0x80 {
- isASCII = false
- break
- }
- }
-
- if !isASCII {
- var m uint32
- isConsole := stdcall2(_GetConsoleMode, handle, uintptr(unsafe.Pointer(&m))) != 0
- // If this is a console output, various non-unicode code pages can be in use.
- // Use the dedicated WriteConsole call to ensure unicode is printed correctly.
- if isConsole {
- return int32(writeConsole(handle, buf, n))
- }
- }
- var written uint32
- stdcall5(_WriteFile, handle, uintptr(buf), uintptr(n), uintptr(unsafe.Pointer(&written)), 0)
- return int32(written)
-}
-
-var (
- utf16ConsoleBack [1000]uint16
- utf16ConsoleBackLock mutex
-)
-
-// writeConsole writes bufLen bytes from buf to the console File.
-// It returns the number of bytes written.
-func writeConsole(handle uintptr, buf unsafe.Pointer, bufLen int32) int {
- const surr2 = (surrogateMin + surrogateMax + 1) / 2
-
- // Do not use defer for unlock. May cause issues when printing a panic.
- lock(&utf16ConsoleBackLock)
-
- b := (*[1 << 30]byte)(buf)[:bufLen]
- s := *(*string)(unsafe.Pointer(&b))
-
- utf16tmp := utf16ConsoleBack[:]
-
- total := len(s)
- w := 0
- for len(s) > 0 {
- if w >= len(utf16tmp)-2 {
- writeConsoleUTF16(handle, utf16tmp[:w])
- w = 0
- }
- r, n := charntorune(s)
- s = s[n:]
- if r < 0x10000 {
- utf16tmp[w] = uint16(r)
- w++
- } else {
- r -= 0x10000
- utf16tmp[w] = surrogateMin + uint16(r>>10)&0x3ff
- utf16tmp[w+1] = surr2 + uint16(r)&0x3ff
- w += 2
- }
- }
- writeConsoleUTF16(handle, utf16tmp[:w])
- unlock(&utf16ConsoleBackLock)
- return total
-}
-
-// writeConsoleUTF16 is the dedicated windows calls that correctly prints
-// to the console regardless of the current code page. Input is utf-16 code points.
-// The handle must be a console handle.
-func writeConsoleUTF16(handle uintptr, b []uint16) {
- l := uint32(len(b))
- if l == 0 {
- return
- }
- var written uint32
- stdcall5(_WriteConsoleW,
- handle,
- uintptr(unsafe.Pointer(&b[0])),
- uintptr(l),
- uintptr(unsafe.Pointer(&written)),
- 0,
- )
- return
-}
-
-//go:nosplit
-func semasleep(ns int64) int32 {
- // store ms in ns to save stack space
- if ns < 0 {
- ns = _INFINITE
- } else {
- ns = int64(timediv(ns, 1000000, nil))
- if ns == 0 {
- ns = 1
- }
- }
- if stdcall2(_WaitForSingleObject, getg().m.waitsema, uintptr(ns)) != 0 {
- return -1 // timeout
- }
- return 0
-}
-
-//go:nosplit
-func semawakeup(mp *m) {
- stdcall1(_SetEvent, mp.waitsema)
-}
-
-//go:nosplit
-func semacreate(mp *m) {
- if mp.waitsema != 0 {
- return
- }
- mp.waitsema = stdcall4(_CreateEventA, 0, 0, 0, 0)
-}
-
-// May run with m.p==nil, so write barriers are not allowed. This
-// function is called by newosproc0, so it is also required to
-// operate without stack guards.
-//go:nowritebarrierc
-//go:nosplit
-func newosproc(mp *m, stk unsafe.Pointer) {
- const _STACK_SIZE_PARAM_IS_A_RESERVATION = 0x00010000
- thandle := stdcall6(_CreateThread, 0, 0x20000,
- funcPC(tstart_stdcall), uintptr(unsafe.Pointer(mp)),
- _STACK_SIZE_PARAM_IS_A_RESERVATION, 0)
- if thandle == 0 {
- print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", getlasterror(), ")\n")
- throw("runtime.newosproc")
- }
-}
-
-// Used by the C library build mode. On Linux this function would allocate a
-// stack, but that's not necessary for Windows. No stack guards are present
-// and the GC has not been initialized, so write barriers will fail.
-//go:nowritebarrierc
-//go:nosplit
-func newosproc0(mp *m, stk unsafe.Pointer) {
- newosproc(mp, stk)
-}
-
-// Called to initialize a new m (including the bootstrap m).
-// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
-func mpreinit(mp *m) {
-}
-
-//go:nosplit
-func msigsave(mp *m) {
-}
-
-//go:nosplit
-func msigrestore(sigmask sigset) {
-}
-
-//go:nosplit
-func sigblock() {
-}
-
-// Called to initialize a new m (including the bootstrap m).
-// Called on the new thread, cannot allocate memory.
-func minit() {
- var thandle uintptr
- stdcall7(_DuplicateHandle, currentProcess, currentThread, currentProcess, uintptr(unsafe.Pointer(&thandle)), 0, 0, _DUPLICATE_SAME_ACCESS)
- atomic.Storeuintptr(&getg().m.thread, thandle)
-}
-
-// Called from dropm to undo the effect of an minit.
-//go:nosplit
-func unminit() {
- tp := &getg().m.thread
- stdcall1(_CloseHandle, *tp)
- *tp = 0
-}
-
-// Described in http://www.dcl.hpi.uni-potsdam.de/research/WRK/2007/08/getting-os-information-the-kuser_shared_data-structure/
-type _KSYSTEM_TIME struct {
- LowPart uint32
- High1Time int32
- High2Time int32
-}
-
-const (
- _INTERRUPT_TIME = 0x7ffe0008
- _SYSTEM_TIME = 0x7ffe0014
-)
-
-//go:nosplit
-func systime(addr uintptr) int64 {
- timeaddr := (*_KSYSTEM_TIME)(unsafe.Pointer(addr))
-
- var t _KSYSTEM_TIME
- for i := 1; i < 10000; i++ {
- // these fields must be read in that order (see URL above)
- t.High1Time = timeaddr.High1Time
- t.LowPart = timeaddr.LowPart
- t.High2Time = timeaddr.High2Time
- if t.High1Time == t.High2Time {
- return int64(t.High1Time)<<32 | int64(t.LowPart)
- }
- if (i % 100) == 0 {
- osyield()
- }
- }
- systemstack(func() {
- throw("interrupt/system time is changing too fast")
- })
- return 0
-}
-
-//go:nosplit
-func unixnano() int64 {
- return (systime(_SYSTEM_TIME) - 116444736000000000) * 100
-}
-
-//go:nosplit
-func nanotime() int64 {
- return systime(_INTERRUPT_TIME) * 100
-}
-
-// Calling stdcall on os stack.
-// May run during STW, so write barriers are not allowed.
-//go:nowritebarrier
-//go:nosplit
-func stdcall(fn stdFunction) uintptr {
- gp := getg()
- mp := gp.m
- mp.libcall.fn = uintptr(unsafe.Pointer(fn))
-
- if mp.profilehz != 0 {
- // leave pc/sp for cpu profiler
- mp.libcallg.set(gp)
- mp.libcallpc = getcallerpc(unsafe.Pointer(&fn))
- // sp must be the last, because once async cpu profiler finds
- // all three values to be non-zero, it will use them
- mp.libcallsp = getcallersp(unsafe.Pointer(&fn))
- }
- asmcgocall(asmstdcallAddr, unsafe.Pointer(&mp.libcall))
- mp.libcallsp = 0
- return mp.libcall.r1
-}
-
-//go:nosplit
-func stdcall0(fn stdFunction) uintptr {
- mp := getg().m
- mp.libcall.n = 0
- mp.libcall.args = uintptr(noescape(unsafe.Pointer(&fn))) // it's unused but must be non-nil, otherwise crashes
- return stdcall(fn)
-}
-
-//go:nosplit
-func stdcall1(fn stdFunction, a0 uintptr) uintptr {
- mp := getg().m
- mp.libcall.n = 1
- mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0)))
- return stdcall(fn)
-}
-
-//go:nosplit
-func stdcall2(fn stdFunction, a0, a1 uintptr) uintptr {
- mp := getg().m
- mp.libcall.n = 2
- mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0)))
- return stdcall(fn)
-}
-
-//go:nosplit
-func stdcall3(fn stdFunction, a0, a1, a2 uintptr) uintptr {
- mp := getg().m
- mp.libcall.n = 3
- mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0)))
- return stdcall(fn)
-}
-
-//go:nosplit
-func stdcall4(fn stdFunction, a0, a1, a2, a3 uintptr) uintptr {
- mp := getg().m
- mp.libcall.n = 4
- mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0)))
- return stdcall(fn)
-}
-
-//go:nosplit
-func stdcall5(fn stdFunction, a0, a1, a2, a3, a4 uintptr) uintptr {
- mp := getg().m
- mp.libcall.n = 5
- mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0)))
- return stdcall(fn)
-}
-
-//go:nosplit
-func stdcall6(fn stdFunction, a0, a1, a2, a3, a4, a5 uintptr) uintptr {
- mp := getg().m
- mp.libcall.n = 6
- mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0)))
- return stdcall(fn)
-}
-
-//go:nosplit
-func stdcall7(fn stdFunction, a0, a1, a2, a3, a4, a5, a6 uintptr) uintptr {
- mp := getg().m
- mp.libcall.n = 7
- mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0)))
- return stdcall(fn)
-}
-
-// in sys_windows_386.s and sys_windows_amd64.s
-func onosstack(fn unsafe.Pointer, arg uint32)
-func usleep2(usec uint32)
-func switchtothread()
-
-var usleep2Addr unsafe.Pointer
-var switchtothreadAddr unsafe.Pointer
-
-//go:nosplit
-func osyield() {
- onosstack(switchtothreadAddr, 0)
-}
-
-//go:nosplit
-func usleep(us uint32) {
- // Have 1us units; want 100ns units.
- onosstack(usleep2Addr, 10*us)
-}
-
-func ctrlhandler1(_type uint32) uint32 {
- var s uint32
-
- switch _type {
- case _CTRL_C_EVENT, _CTRL_BREAK_EVENT:
- s = _SIGINT
- default:
- return 0
- }
-
- if sigsend(s) {
- return 1
- }
- exit(2) // SIGINT, SIGTERM, etc
- return 0
-}
-
-// in sys_windows_386.s and sys_windows_amd64.s
-func profileloop()
-
-var profiletimer uintptr
-
-func profilem(mp *m) {
- var r *context
- rbuf := make([]byte, unsafe.Sizeof(*r)+15)
-
- tls := &mp.tls[0]
- gp := *((**g)(unsafe.Pointer(tls)))
-
- // align Context to 16 bytes
- r = (*context)(unsafe.Pointer((uintptr(unsafe.Pointer(&rbuf[15]))) &^ 15))
- r.contextflags = _CONTEXT_CONTROL
- stdcall2(_GetThreadContext, mp.thread, uintptr(unsafe.Pointer(r)))
- sigprof(r.ip(), r.sp(), 0, gp, mp)
-}
-
-func profileloop1(param uintptr) uint32 {
- stdcall2(_SetThreadPriority, currentThread, _THREAD_PRIORITY_HIGHEST)
-
- for {
- stdcall2(_WaitForSingleObject, profiletimer, _INFINITE)
- first := (*m)(atomic.Loadp(unsafe.Pointer(&allm)))
- for mp := first; mp != nil; mp = mp.alllink {
- thread := atomic.Loaduintptr(&mp.thread)
- // Do not profile threads blocked on Notes,
- // this includes idle worker threads,
- // idle timer thread, idle heap scavenger, etc.
- if thread == 0 || mp.profilehz == 0 || mp.blocked {
- continue
- }
- stdcall1(_SuspendThread, thread)
- if mp.profilehz != 0 && !mp.blocked {
- profilem(mp)
- }
- stdcall1(_ResumeThread, thread)
- }
- }
-}
-
-var cpuprofilerlock mutex
-
-func resetcpuprofiler(hz int32) {
- lock(&cpuprofilerlock)
- if profiletimer == 0 {
- timer := stdcall3(_CreateWaitableTimerA, 0, 0, 0)
- atomic.Storeuintptr(&profiletimer, timer)
- thread := stdcall6(_CreateThread, 0, 0, funcPC(profileloop), 0, 0, 0)
- stdcall2(_SetThreadPriority, thread, _THREAD_PRIORITY_HIGHEST)
- stdcall1(_CloseHandle, thread)
- }
- unlock(&cpuprofilerlock)
-
- ms := int32(0)
- due := ^int64(^uint64(1 << 63))
- if hz > 0 {
- ms = 1000 / hz
- if ms == 0 {
- ms = 1
- }
- due = int64(ms) * -10000
- }
- stdcall6(_SetWaitableTimer, profiletimer, uintptr(unsafe.Pointer(&due)), uintptr(ms), 0, 0, 0)
- atomic.Store((*uint32)(unsafe.Pointer(&getg().m.profilehz)), uint32(hz))
-}
-
-func memlimit() uintptr {
- return 0
-}
diff --git a/src/runtime/os2_darwin.go b/src/runtime/os2_darwin.go
deleted file mode 100644
index 542bd74219..0000000000
--- a/src/runtime/os2_darwin.go
+++ /dev/null
@@ -1,14 +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 runtime
-
-const (
- _NSIG = 32
- _SI_USER = 0 /* empirically true, but not what headers say */
- _SIG_BLOCK = 1
- _SIG_UNBLOCK = 2
- _SIG_SETMASK = 3
- _SS_DISABLE = 4
-)
diff --git a/src/runtime/os2_dragonfly.go b/src/runtime/os2_dragonfly.go
deleted file mode 100644
index 6ea2da0393..0000000000
--- a/src/runtime/os2_dragonfly.go
+++ /dev/null
@@ -1,15 +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 runtime
-
-const (
- _NSIG = 33
- _SI_USER = 0
- _SS_DISABLE = 4
- _RLIMIT_AS = 10
- _SIG_BLOCK = 1
- _SIG_UNBLOCK = 2
- _SIG_SETMASK = 3
-)
diff --git a/src/runtime/os2_linux_mips64x.go b/src/runtime/os2_linux_mips64x.go
deleted file mode 100644
index 9a6a92a87d..0000000000
--- a/src/runtime/os2_linux_mips64x.go
+++ /dev/null
@@ -1,25 +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.
-
-// +build linux
-// +build mips64 mips64le
-
-package runtime
-
-const (
- _SS_DISABLE = 2
- _NSIG = 65
- _SI_USER = 0
- _SIG_BLOCK = 1
- _SIG_UNBLOCK = 2
- _SIG_SETMASK = 3
- _RLIMIT_AS = 6
-)
-
-type sigset [2]uint64
-
-type rlimit struct {
- rlim_cur uintptr
- rlim_max uintptr
-}
diff --git a/src/runtime/os2_netbsd.go b/src/runtime/os2_netbsd.go
deleted file mode 100644
index 405dd5e727..0000000000
--- a/src/runtime/os2_netbsd.go
+++ /dev/null
@@ -1,18 +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 runtime
-
-const (
- _SS_DISABLE = 4
- _SIG_BLOCK = 1
- _SIG_UNBLOCK = 2
- _SIG_SETMASK = 3
- _NSIG = 33
- _SI_USER = 0
-
- // From NetBSD's <sys/ucontext.h>
- _UC_SIGMASK = 0x01
- _UC_CPU = 0x04
-)
diff --git a/src/runtime/os2_windows.go b/src/runtime/os2_windows.go
deleted file mode 100644
index a867dfeb64..0000000000
--- a/src/runtime/os2_windows.go
+++ /dev/null
@@ -1,19 +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 runtime
-
-func getlasterror() uint32
-func setlasterror(err uint32)
-
-// Function to be called by windows CreateThread
-// to start new os thread.
-func tstart_stdcall(newm *m) uint32
-
-func ctrlhandler(_type uint32) uint32
-
-// TODO(brainman): should not need those
-const (
- _NSIG = 65
-)
diff --git a/src/runtime/os_darwin.go b/src/runtime/os_darwin.go
index e9b8933fb9..a0e3d8ed6b 100644
--- a/src/runtime/os_darwin.go
+++ b/src/runtime/os_darwin.go
@@ -1,4 +1,4 @@
-// Copyright 2014 The Go Authors. All rights reserved.
+// 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.
@@ -24,6 +24,476 @@ func mach_thread_self() uint32
//go:noescape
func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32
+func unimplemented(name string) {
+ println(name, "not implemented")
+ *(*int)(unsafe.Pointer(uintptr(1231))) = 1231
+}
+
+//go:nosplit
+func semawakeup(mp *m) {
+ mach_semrelease(mp.waitsema)
+}
+
+//go:nosplit
+func semacreate(mp *m) {
+ if mp.waitsema != 0 {
+ return
+ }
+ systemstack(func() {
+ mp.waitsema = mach_semcreate()
+ })
+}
+
+// BSD interface for threading.
+func osinit() {
+ // bsdthread_register delayed until end of goenvs so that we
+ // can look at the environment first.
+
+ ncpu = getncpu()
+}
+
+func getncpu() int32 {
+ // Use sysctl to fetch hw.ncpu.
+ mib := [2]uint32{6, 3}
+ out := uint32(0)
+ nout := unsafe.Sizeof(out)
+ ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
+ if ret >= 0 && int32(out) > 0 {
+ return int32(out)
+ }
+ return 1
+}
+
+var urandom_dev = []byte("/dev/urandom\x00")
+
+//go:nosplit
+func getRandomData(r []byte) {
+ fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
+ n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
+ closefd(fd)
+ extendRandom(r, int(n))
+}
+
+func goenvs() {
+ goenvs_unix()
+
+ // Register our thread-creation callback (see sys_darwin_{amd64,386}.s)
+ // but only if we're not using cgo. If we are using cgo we need
+ // to let the C pthread library install its own thread-creation callback.
+ if !iscgo {
+ if bsdthread_register() != 0 {
+ if gogetenv("DYLD_INSERT_LIBRARIES") != "" {
+ throw("runtime: bsdthread_register error (unset DYLD_INSERT_LIBRARIES)")
+ }
+ throw("runtime: bsdthread_register error")
+ }
+ }
+}
+
+// May run with m.p==nil, so write barriers are not allowed.
+//go:nowritebarrier
+func newosproc(mp *m, stk unsafe.Pointer) {
+ if false {
+ print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " id=", mp.id, " ostk=", &mp, "\n")
+ }
+
+ var oset sigset
+ sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
+ errno := bsdthread_create(stk, unsafe.Pointer(mp), funcPC(mstart))
+ sigprocmask(_SIG_SETMASK, &oset, nil)
+
+ if errno < 0 {
+ print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", -errno, ")\n")
+ throw("runtime.newosproc")
+ }
+}
+
+// newosproc0 is a version of newosproc that can be called before the runtime
+// is initialized.
+//
+// As Go uses bsdthread_register when running without cgo, this function is
+// not safe to use after initialization as it does not pass an M as fnarg.
+//
+//go:nosplit
+func newosproc0(stacksize uintptr, fn unsafe.Pointer, fnarg uintptr) {
+ stack := sysAlloc(stacksize, &memstats.stacks_sys)
+ if stack == nil {
+ write(2, unsafe.Pointer(&failallocatestack[0]), int32(len(failallocatestack)))
+ exit(1)
+ }
+ stk := unsafe.Pointer(uintptr(stack) + stacksize)
+
+ var oset sigset
+ sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
+ errno := bsdthread_create(stk, fn, fnarg)
+ sigprocmask(_SIG_SETMASK, &oset, nil)
+
+ if errno < 0 {
+ write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
+ exit(1)
+ }
+}
+
+var failallocatestack = []byte("runtime: failed to allocate stack for the new OS thread\n")
+var failthreadcreate = []byte("runtime: failed to create new OS thread\n")
+
+// Called to do synchronous initialization of Go code built with
+// -buildmode=c-archive or -buildmode=c-shared.
+// None of the Go runtime is initialized.
+//go:nosplit
+//go:nowritebarrierrec
+func libpreinit() {
+ initsig(true)
+}
+
+// Called to initialize a new m (including the bootstrap m).
+// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
+func mpreinit(mp *m) {
+ mp.gsignal = malg(32 * 1024) // OS X wants >= 8K
+ mp.gsignal.m = mp
+}
+
+//go:nosplit
+func msigsave(mp *m) {
+ sigprocmask(_SIG_SETMASK, nil, &mp.sigmask)
+}
+
+//go:nosplit
+func msigrestore(sigmask sigset) {
+ sigprocmask(_SIG_SETMASK, &sigmask, nil)
+}
+
+//go:nosplit
+func sigblock() {
+ sigprocmask(_SIG_SETMASK, &sigset_all, nil)
+}
+
+// Called to initialize a new m (including the bootstrap m).
+// Called on the new thread, cannot allocate memory.
+func minit() {
+ // Initialize signal handling.
+ _g_ := getg()
+
+ // The alternate signal stack is buggy on arm and arm64.
+ // The signal handler handles it directly.
+ // The sigaltstack assembly function does nothing.
+ if GOARCH != "arm" && GOARCH != "arm64" {
+ var st stackt
+ sigaltstack(nil, &st)
+ if st.ss_flags&_SS_DISABLE != 0 {
+ signalstack(&_g_.m.gsignal.stack)
+ _g_.m.newSigstack = true
+ } else {
+ // Use existing signal stack.
+ stsp := uintptr(unsafe.Pointer(st.ss_sp))
+ _g_.m.gsignal.stack.lo = stsp
+ _g_.m.gsignal.stack.hi = stsp + st.ss_size
+ _g_.m.gsignal.stackguard0 = stsp + _StackGuard
+ _g_.m.gsignal.stackguard1 = stsp + _StackGuard
+ _g_.m.gsignal.stackAlloc = st.ss_size
+ _g_.m.newSigstack = false
+ }
+ }
+
+ // restore signal mask from m.sigmask and unblock essential signals
+ nmask := _g_.m.sigmask
+ for i := range sigtable {
+ if sigtable[i].flags&_SigUnblock != 0 {
+ nmask &^= 1 << (uint32(i) - 1)
+ }
+ }
+ sigprocmask(_SIG_SETMASK, &nmask, nil)
+}
+
+// Called from dropm to undo the effect of an minit.
+//go:nosplit
+func unminit() {
+ if getg().m.newSigstack {
+ signalstack(nil)
+ }
+}
+
+// Mach IPC, to get at semaphores
+// Definitions are in /usr/include/mach on a Mac.
+
+func macherror(r int32, fn string) {
+ print("mach error ", fn, ": ", r, "\n")
+ throw("mach error")
+}
+
+const _DebugMach = false
+
+var zerondr machndr
+
+func mach_msgh_bits(a, b uint32) uint32 {
+ return a | b<<8
+}
+
+func mach_msg(h *machheader, op int32, send_size, rcv_size, rcv_name, timeout, notify uint32) int32 {
+ // TODO: Loop on interrupt.
+ return mach_msg_trap(unsafe.Pointer(h), op, send_size, rcv_size, rcv_name, timeout, notify)
+}
+
+// Mach RPC (MIG)
+const (
+ _MinMachMsg = 48
+ _MachReply = 100
+)
+
+type codemsg struct {
+ h machheader
+ ndr machndr
+ code int32
+}
+
+func machcall(h *machheader, maxsize int32, rxsize int32) int32 {
+ _g_ := getg()
+ port := _g_.m.machport
+ if port == 0 {
+ port = mach_reply_port()
+ _g_.m.machport = port
+ }
+
+ h.msgh_bits |= mach_msgh_bits(_MACH_MSG_TYPE_COPY_SEND, _MACH_MSG_TYPE_MAKE_SEND_ONCE)
+ h.msgh_local_port = port
+ h.msgh_reserved = 0
+ id := h.msgh_id
+
+ if _DebugMach {
+ p := (*[10000]unsafe.Pointer)(unsafe.Pointer(h))
+ print("send:\t")
+ var i uint32
+ for i = 0; i < h.msgh_size/uint32(unsafe.Sizeof(p[0])); i++ {
+ print(" ", p[i])
+ if i%8 == 7 {
+ print("\n\t")
+ }
+ }
+ if i%8 != 0 {
+ print("\n")
+ }
+ }
+ ret := mach_msg(h, _MACH_SEND_MSG|_MACH_RCV_MSG, h.msgh_size, uint32(maxsize), port, 0, 0)
+ if ret != 0 {
+ if _DebugMach {
+ print("mach_msg error ", ret, "\n")
+ }
+ return ret
+ }
+ if _DebugMach {
+ p := (*[10000]unsafe.Pointer)(unsafe.Pointer(h))
+ var i uint32
+ for i = 0; i < h.msgh_size/uint32(unsafe.Sizeof(p[0])); i++ {
+ print(" ", p[i])
+ if i%8 == 7 {
+ print("\n\t")
+ }
+ }
+ if i%8 != 0 {
+ print("\n")
+ }
+ }
+ if h.msgh_id != id+_MachReply {
+ if _DebugMach {
+ print("mach_msg _MachReply id mismatch ", h.msgh_id, " != ", id+_MachReply, "\n")
+ }
+ return -303 // MIG_REPLY_MISMATCH
+ }
+ // Look for a response giving the return value.
+ // Any call can send this back with an error,
+ // and some calls only have return values so they
+ // send it back on success too. I don't quite see how
+ // you know it's one of these and not the full response
+ // format, so just look if the message is right.
+ c := (*codemsg)(unsafe.Pointer(h))
+ if uintptr(h.msgh_size) == unsafe.Sizeof(*c) && h.msgh_bits&_MACH_MSGH_BITS_COMPLEX == 0 {
+ if _DebugMach {
+ print("mig result ", c.code, "\n")
+ }
+ return c.code
+ }
+ if h.msgh_size != uint32(rxsize) {
+ if _DebugMach {
+ print("mach_msg _MachReply size mismatch ", h.msgh_size, " != ", rxsize, "\n")
+ }
+ return -307 // MIG_ARRAY_TOO_LARGE
+ }
+ return 0
+}
+
+// Semaphores!
+
+const (
+ tmach_semcreate = 3418
+ rmach_semcreate = tmach_semcreate + _MachReply
+
+ tmach_semdestroy = 3419
+ rmach_semdestroy = tmach_semdestroy + _MachReply
+
+ _KERN_ABORTED = 14
+ _KERN_OPERATION_TIMED_OUT = 49
+)
+
+type tmach_semcreatemsg struct {
+ h machheader
+ ndr machndr
+ policy int32
+ value int32
+}
+
+type rmach_semcreatemsg struct {
+ h machheader
+ body machbody
+ semaphore machport
+}
+
+type tmach_semdestroymsg struct {
+ h machheader
+ body machbody
+ semaphore machport
+}
+
+func mach_semcreate() uint32 {
+ var m [256]uint8
+ tx := (*tmach_semcreatemsg)(unsafe.Pointer(&m))
+ rx := (*rmach_semcreatemsg)(unsafe.Pointer(&m))
+
+ tx.h.msgh_bits = 0
+ tx.h.msgh_size = uint32(unsafe.Sizeof(*tx))
+ tx.h.msgh_remote_port = mach_task_self()
+ tx.h.msgh_id = tmach_semcreate
+ tx.ndr = zerondr
+
+ tx.policy = 0 // 0 = SYNC_POLICY_FIFO
+ tx.value = 0
+
+ for {
+ r := machcall(&tx.h, int32(unsafe.Sizeof(m)), int32(unsafe.Sizeof(*rx)))
+ if r == 0 {
+ break
+ }
+ if r == _KERN_ABORTED { // interrupted
+ continue
+ }
+ macherror(r, "semaphore_create")
+ }
+ if rx.body.msgh_descriptor_count != 1 {
+ unimplemented("mach_semcreate desc count")
+ }
+ return rx.semaphore.name
+}
+
+func mach_semdestroy(sem uint32) {
+ var m [256]uint8
+ tx := (*tmach_semdestroymsg)(unsafe.Pointer(&m))
+
+ tx.h.msgh_bits = _MACH_MSGH_BITS_COMPLEX
+ tx.h.msgh_size = uint32(unsafe.Sizeof(*tx))
+ tx.h.msgh_remote_port = mach_task_self()
+ tx.h.msgh_id = tmach_semdestroy
+ tx.body.msgh_descriptor_count = 1
+ tx.semaphore.name = sem
+ tx.semaphore.disposition = _MACH_MSG_TYPE_MOVE_SEND
+ tx.semaphore._type = 0
+
+ for {
+ r := machcall(&tx.h, int32(unsafe.Sizeof(m)), 0)
+ if r == 0 {
+ break
+ }
+ if r == _KERN_ABORTED { // interrupted
+ continue
+ }
+ macherror(r, "semaphore_destroy")
+ }
+}
+
+// The other calls have simple system call traps in sys_darwin_{amd64,386}.s
+
+func mach_semaphore_wait(sema uint32) int32
+func mach_semaphore_timedwait(sema, sec, nsec uint32) int32
+func mach_semaphore_signal(sema uint32) int32
+func mach_semaphore_signal_all(sema uint32) int32
+
+func semasleep1(ns int64) int32 {
+ _g_ := getg()
+
+ if ns >= 0 {
+ var nsecs int32
+ secs := timediv(ns, 1000000000, &nsecs)
+ r := mach_semaphore_timedwait(_g_.m.waitsema, uint32(secs), uint32(nsecs))
+ if r == _KERN_ABORTED || r == _KERN_OPERATION_TIMED_OUT {
+ return -1
+ }
+ if r != 0 {
+ macherror(r, "semaphore_wait")
+ }
+ return 0
+ }
+
+ for {
+ r := mach_semaphore_wait(_g_.m.waitsema)
+ if r == 0 {
+ break
+ }
+ if r == _KERN_ABORTED { // interrupted
+ continue
+ }
+ macherror(r, "semaphore_wait")
+ }
+ return 0
+}
+
+//go:nosplit
+func semasleep(ns int64) int32 {
+ var r int32
+ systemstack(func() {
+ r = semasleep1(ns)
+ })
+ return r
+}
+
+//go:nosplit
+func mach_semrelease(sem uint32) {
+ for {
+ r := mach_semaphore_signal(sem)
+ if r == 0 {
+ break
+ }
+ if r == _KERN_ABORTED { // interrupted
+ continue
+ }
+
+ // mach_semrelease must be completely nosplit,
+ // because it is called from Go code.
+ // If we're going to die, start that process on the system stack
+ // to avoid a Go stack split.
+ systemstack(func() { macherror(r, "semaphore_signal") })
+ }
+}
+
+//go:nosplit
+func osyield() {
+ usleep(1)
+}
+
+func memlimit() uintptr {
+ // NOTE(rsc): Could use getrlimit here,
+ // like on FreeBSD or Linux, but Darwin doesn't enforce
+ // ulimit -v, so it's unclear why we'd try to stay within
+ // the limit.
+ return 0
+}
+
+const (
+ _NSIG = 32
+ _SI_USER = 0 /* empirically true, but not what headers say */
+ _SIG_BLOCK = 1
+ _SIG_UNBLOCK = 2
+ _SIG_SETMASK = 3
+ _SS_DISABLE = 4
+)
+
//go:noescape
func sigprocmask(how uint32, new, old *sigset)
@@ -40,3 +510,73 @@ func setitimer(mode int32, new, old *itimerval)
func raise(sig int32)
func raiseproc(int32)
+
+//extern SigTabTT runtime·sigtab[];
+
+type sigset uint32
+
+var sigset_all = ^sigset(0)
+
+//go:nosplit
+//go:nowritebarrierrec
+func setsig(i int32, fn uintptr, restart bool) {
+ var sa sigactiont
+ sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK
+ if restart {
+ sa.sa_flags |= _SA_RESTART
+ }
+ sa.sa_mask = ^uint32(0)
+ sa.sa_tramp = unsafe.Pointer(funcPC(sigtramp)) // runtime·sigtramp's job is to call into real handler
+ *(*uintptr)(unsafe.Pointer(&sa.__sigaction_u)) = fn
+ sigaction(uint32(i), &sa, nil)
+}
+
+//go:nosplit
+//go:nowritebarrierrec
+func setsigstack(i int32) {
+ var osa usigactiont
+ sigaction(uint32(i), nil, &osa)
+ handler := *(*uintptr)(unsafe.Pointer(&osa.__sigaction_u))
+ if handler == 0 || handler == _SIG_DFL || handler == _SIG_IGN || osa.sa_flags&_SA_ONSTACK != 0 {
+ return
+ }
+ var sa sigactiont
+ *(*uintptr)(unsafe.Pointer(&sa.__sigaction_u)) = handler
+ sa.sa_tramp = unsafe.Pointer(funcPC(sigtramp))
+ sa.sa_mask = osa.sa_mask
+ sa.sa_flags = osa.sa_flags | _SA_ONSTACK
+ sigaction(uint32(i), &sa, nil)
+}
+
+//go:nosplit
+//go:nowritebarrierrec
+func getsig(i int32) uintptr {
+ var sa usigactiont
+ sigaction(uint32(i), nil, &sa)
+ return *(*uintptr)(unsafe.Pointer(&sa.__sigaction_u))
+}
+
+//go:nosplit
+func signalstack(s *stack) {
+ var st stackt
+ if s == nil {
+ st.ss_flags = _SS_DISABLE
+ } else {
+ st.ss_sp = (*byte)(unsafe.Pointer(s.lo))
+ st.ss_size = s.hi - s.lo
+ st.ss_flags = 0
+ }
+ sigaltstack(&st, nil)
+}
+
+//go:nosplit
+//go:nowritebarrierrec
+func updatesigmask(m sigmask) {
+ s := sigset(m[0])
+ sigprocmask(_SIG_SETMASK, &s, nil)
+}
+
+func unblocksig(sig int32) {
+ mask := sigset(1) << (uint32(sig) - 1)
+ sigprocmask(_SIG_UNBLOCK, &mask, nil)
+}
diff --git a/src/runtime/os_dragonfly.go b/src/runtime/os_dragonfly.go
index c3833a397a..78a150eee5 100644
--- a/src/runtime/os_dragonfly.go
+++ b/src/runtime/os_dragonfly.go
@@ -6,6 +6,16 @@ package runtime
import "unsafe"
+const (
+ _NSIG = 33
+ _SI_USER = 0
+ _SS_DISABLE = 4
+ _RLIMIT_AS = 10
+ _SIG_BLOCK = 1
+ _SIG_UNBLOCK = 2
+ _SIG_SETMASK = 3
+)
+
type mOS struct{}
//go:noescape
@@ -41,3 +51,266 @@ func sys_umtx_wakeup(addr *uint32, val int32) int32
func osyield()
const stackSystem = 0
+
+// From DragonFly's <sys/sysctl.h>
+const (
+ _CTL_HW = 6
+ _HW_NCPU = 3
+)
+
+var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}}
+
+func getncpu() int32 {
+ mib := [2]uint32{_CTL_HW, _HW_NCPU}
+ out := uint32(0)
+ nout := unsafe.Sizeof(out)
+ ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
+ if ret >= 0 {
+ return int32(out)
+ }
+ return 1
+}
+
+//go:nosplit
+func futexsleep(addr *uint32, val uint32, ns int64) {
+ systemstack(func() {
+ futexsleep1(addr, val, ns)
+ })
+}
+
+func futexsleep1(addr *uint32, val uint32, ns int64) {
+ var timeout int32
+ if ns >= 0 {
+ // The timeout is specified in microseconds - ensure that we
+ // do not end up dividing to zero, which would put us to sleep
+ // indefinitely...
+ timeout = timediv(ns, 1000, nil)
+ if timeout == 0 {
+ timeout = 1
+ }
+ }
+
+ // sys_umtx_sleep will return EWOULDBLOCK (EAGAIN) when the timeout
+ // expires or EBUSY if the mutex value does not match.
+ ret := sys_umtx_sleep(addr, int32(val), timeout)
+ if ret >= 0 || ret == -_EINTR || ret == -_EAGAIN || ret == -_EBUSY {
+ return
+ }
+
+ print("umtx_sleep addr=", addr, " val=", val, " ret=", ret, "\n")
+ *(*int32)(unsafe.Pointer(uintptr(0x1005))) = 0x1005
+}
+
+//go:nosplit
+func futexwakeup(addr *uint32, cnt uint32) {
+ ret := sys_umtx_wakeup(addr, int32(cnt))
+ if ret >= 0 {
+ return
+ }
+
+ systemstack(func() {
+ print("umtx_wake_addr=", addr, " ret=", ret, "\n")
+ *(*int32)(unsafe.Pointer(uintptr(0x1006))) = 0x1006
+ })
+}
+
+func lwp_start(uintptr)
+
+// May run with m.p==nil, so write barriers are not allowed.
+//go:nowritebarrier
+func newosproc(mp *m, stk unsafe.Pointer) {
+ if false {
+ print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " lwp_start=", funcPC(lwp_start), " id=", mp.id, " ostk=", &mp, "\n")
+ }
+
+ var oset sigset
+ sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
+
+ params := lwpparams{
+ start_func: funcPC(lwp_start),
+ arg: unsafe.Pointer(mp),
+ stack: uintptr(stk),
+ tid1: unsafe.Pointer(&mp.procid),
+ tid2: nil,
+ }
+
+ lwp_create(&params)
+ sigprocmask(_SIG_SETMASK, &oset, nil)
+}
+
+func osinit() {
+ ncpu = getncpu()
+}
+
+var urandom_dev = []byte("/dev/urandom\x00")
+
+//go:nosplit
+func getRandomData(r []byte) {
+ fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
+ n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
+ closefd(fd)
+ extendRandom(r, int(n))
+}
+
+func goenvs() {
+ goenvs_unix()
+}
+
+// Called to initialize a new m (including the bootstrap m).
+// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
+func mpreinit(mp *m) {
+ mp.gsignal = malg(32 * 1024)
+ mp.gsignal.m = mp
+}
+
+//go:nosplit
+func msigsave(mp *m) {
+ sigprocmask(_SIG_SETMASK, nil, &mp.sigmask)
+}
+
+//go:nosplit
+func msigrestore(sigmask sigset) {
+ sigprocmask(_SIG_SETMASK, &sigmask, nil)
+}
+
+//go:nosplit
+func sigblock() {
+ sigprocmask(_SIG_SETMASK, &sigset_all, nil)
+}
+
+// Called to initialize a new m (including the bootstrap m).
+// Called on the new thread, cannot allocate memory.
+func minit() {
+ _g_ := getg()
+
+ // m.procid is a uint64, but lwp_start writes an int32. Fix it up.
+ _g_.m.procid = uint64(*(*int32)(unsafe.Pointer(&_g_.m.procid)))
+
+ // Initialize signal handling.
+
+ // On DragonFly a thread created by pthread_create inherits
+ // the signal stack of the creating thread. We always create
+ // a new signal stack here, to avoid having two Go threads
+ // using the same signal stack. This breaks the case of a
+ // thread created in C that calls sigaltstack and then calls a
+ // Go function, because we will lose track of the C code's
+ // sigaltstack, but it's the best we can do.
+ signalstack(&_g_.m.gsignal.stack)
+ _g_.m.newSigstack = true
+
+ // restore signal mask from m.sigmask and unblock essential signals
+ nmask := _g_.m.sigmask
+ for i := range sigtable {
+ if sigtable[i].flags&_SigUnblock != 0 {
+ nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
+ }
+ }
+ sigprocmask(_SIG_SETMASK, &nmask, nil)
+}
+
+// Called from dropm to undo the effect of an minit.
+//go:nosplit
+func unminit() {
+ if getg().m.newSigstack {
+ signalstack(nil)
+ }
+}
+
+func memlimit() uintptr {
+ /*
+ TODO: Convert to Go when something actually uses the result.
+
+ Rlimit rl;
+ extern byte runtime·text[], runtime·end[];
+ uintptr used;
+
+ if(runtime·getrlimit(RLIMIT_AS, &rl) != 0)
+ return 0;
+ if(rl.rlim_cur >= 0x7fffffff)
+ return 0;
+
+ // Estimate our VM footprint excluding the heap.
+ // Not an exact science: use size of binary plus
+ // some room for thread stacks.
+ used = runtime·end - runtime·text + (64<<20);
+ if(used >= rl.rlim_cur)
+ return 0;
+
+ // If there's not at least 16 MB left, we're probably
+ // not going to be able to do much. Treat as no limit.
+ rl.rlim_cur -= used;
+ if(rl.rlim_cur < (16<<20))
+ return 0;
+
+ return rl.rlim_cur - used;
+ */
+ return 0
+}
+
+func sigtramp()
+
+type sigactiont struct {
+ sa_sigaction uintptr
+ sa_flags int32
+ sa_mask sigset
+}
+
+//go:nosplit
+//go:nowritebarrierrec
+func setsig(i int32, fn uintptr, restart bool) {
+ var sa sigactiont
+ sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK
+ if restart {
+ sa.sa_flags |= _SA_RESTART
+ }
+ sa.sa_mask = sigset_all
+ if fn == funcPC(sighandler) {
+ fn = funcPC(sigtramp)
+ }
+ sa.sa_sigaction = fn
+ sigaction(i, &sa, nil)
+}
+
+//go:nosplit
+//go:nowritebarrierrec
+func setsigstack(i int32) {
+ throw("setsigstack")
+}
+
+//go:nosplit
+//go:nowritebarrierrec
+func getsig(i int32) uintptr {
+ var sa sigactiont
+ sigaction(i, nil, &sa)
+ if sa.sa_sigaction == funcPC(sigtramp) {
+ return funcPC(sighandler)
+ }
+ return sa.sa_sigaction
+}
+
+//go:nosplit
+func signalstack(s *stack) {
+ var st sigaltstackt
+ if s == nil {
+ st.ss_flags = _SS_DISABLE
+ } else {
+ st.ss_sp = s.lo
+ st.ss_size = s.hi - s.lo
+ st.ss_flags = 0
+ }
+ sigaltstack(&st, nil)
+}
+
+//go:nosplit
+//go:nowritebarrierrec
+func updatesigmask(m sigmask) {
+ var mask sigset
+ copy(mask.__bits[:], m[:])
+ sigprocmask(_SIG_SETMASK, &mask, nil)
+}
+
+func unblocksig(sig int32) {
+ var mask sigset
+ mask.__bits[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31)
+ sigprocmask(_SIG_UNBLOCK, &mask, nil)
+}
diff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go
index dd69743e10..35b57d8a23 100644
--- a/src/runtime/os_linux.go
+++ b/src/runtime/os_linux.go
@@ -1,19 +1,365 @@
-// Copyright 2014 The Go Authors. All rights reserved.
+// 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 runtime
-import "unsafe"
+import (
+ "runtime/internal/sys"
+ "unsafe"
+)
type mOS struct{}
//go:noescape
func futex(addr unsafe.Pointer, op int32, val uint32, ts, addr2 unsafe.Pointer, val3 uint32) int32
+// Linux futex.
+//
+// futexsleep(uint32 *addr, uint32 val)
+// futexwakeup(uint32 *addr)
+//
+// Futexsleep atomically checks if *addr == val and if so, sleeps on addr.
+// Futexwakeup wakes up threads sleeping on addr.
+// Futexsleep is allowed to wake up spuriously.
+
+const (
+ _FUTEX_WAIT = 0
+ _FUTEX_WAKE = 1
+)
+
+// Atomically,
+// if(*addr == val) sleep
+// Might be woken up spuriously; that's allowed.
+// Don't sleep longer than ns; ns < 0 means forever.
+//go:nosplit
+func futexsleep(addr *uint32, val uint32, ns int64) {
+ var ts timespec
+
+ // Some Linux kernels have a bug where futex of
+ // FUTEX_WAIT returns an internal error code
+ // as an errno. Libpthread ignores the return value
+ // here, and so can we: as it says a few lines up,
+ // spurious wakeups are allowed.
+ if ns < 0 {
+ futex(unsafe.Pointer(addr), _FUTEX_WAIT, val, nil, nil, 0)
+ return
+ }
+
+ // It's difficult to live within the no-split stack limits here.
+ // On ARM and 386, a 64-bit divide invokes a general software routine
+ // that needs more stack than we can afford. So we use timediv instead.
+ // But on real 64-bit systems, where words are larger but the stack limit
+ // is not, even timediv is too heavy, and we really need to use just an
+ // ordinary machine instruction.
+ if sys.PtrSize == 8 {
+ ts.set_sec(ns / 1000000000)
+ ts.set_nsec(int32(ns % 1000000000))
+ } else {
+ ts.tv_nsec = 0
+ ts.set_sec(int64(timediv(ns, 1000000000, (*int32)(unsafe.Pointer(&ts.tv_nsec)))))
+ }
+ futex(unsafe.Pointer(addr), _FUTEX_WAIT, val, unsafe.Pointer(&ts), nil, 0)
+}
+
+// If any procs are sleeping on addr, wake up at most cnt.
+//go:nosplit
+func futexwakeup(addr *uint32, cnt uint32) {
+ ret := futex(unsafe.Pointer(addr), _FUTEX_WAKE, cnt, nil, nil, 0)
+ if ret >= 0 {
+ return
+ }
+
+ // I don't know that futex wakeup can return
+ // EAGAIN or EINTR, but if it does, it would be
+ // safe to loop and call futex again.
+ systemstack(func() {
+ print("futexwakeup addr=", addr, " returned ", ret, "\n")
+ })
+
+ *(*int32)(unsafe.Pointer(uintptr(0x1006))) = 0x1006
+}
+
+func getproccount() int32 {
+ // This buffer is huge (8 kB) but we are on the system stack
+ // and there should be plenty of space (64 kB).
+ // Also this is a leaf, so we're not holding up the memory for long.
+ // See golang.org/issue/11823.
+ // The suggested behavior here is to keep trying with ever-larger
+ // buffers, but we don't have a dynamic memory allocator at the
+ // moment, so that's a bit tricky and seems like overkill.
+ const maxCPUs = 64 * 1024
+ var buf [maxCPUs / (sys.PtrSize * 8)]uintptr
+ r := sched_getaffinity(0, unsafe.Sizeof(buf), &buf[0])
+ n := int32(0)
+ for _, v := range buf[:r/sys.PtrSize] {
+ for v != 0 {
+ n += int32(v & 1)
+ v >>= 1
+ }
+ }
+ if n == 0 {
+ n = 1
+ }
+ return n
+}
+
+// Clone, the Linux rfork.
+const (
+ _CLONE_VM = 0x100
+ _CLONE_FS = 0x200
+ _CLONE_FILES = 0x400
+ _CLONE_SIGHAND = 0x800
+ _CLONE_PTRACE = 0x2000
+ _CLONE_VFORK = 0x4000
+ _CLONE_PARENT = 0x8000
+ _CLONE_THREAD = 0x10000
+ _CLONE_NEWNS = 0x20000
+ _CLONE_SYSVSEM = 0x40000
+ _CLONE_SETTLS = 0x80000
+ _CLONE_PARENT_SETTID = 0x100000
+ _CLONE_CHILD_CLEARTID = 0x200000
+ _CLONE_UNTRACED = 0x800000
+ _CLONE_CHILD_SETTID = 0x1000000
+ _CLONE_STOPPED = 0x2000000
+ _CLONE_NEWUTS = 0x4000000
+ _CLONE_NEWIPC = 0x8000000
+
+ cloneFlags = _CLONE_VM | /* share memory */
+ _CLONE_FS | /* share cwd, etc */
+ _CLONE_FILES | /* share fd table */
+ _CLONE_SIGHAND | /* share sig handler table */
+ _CLONE_THREAD /* revisit - okay for now */
+)
+
//go:noescape
func clone(flags int32, stk, mm, gg, fn unsafe.Pointer) int32
+// May run with m.p==nil, so write barriers are not allowed.
+//go:nowritebarrier
+func newosproc(mp *m, stk unsafe.Pointer) {
+ /*
+ * note: strace gets confused if we use CLONE_PTRACE here.
+ */
+ if false {
+ print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " clone=", funcPC(clone), " id=", mp.id, " ostk=", &mp, "\n")
+ }
+
+ // Disable signals during clone, so that the new thread starts
+ // with signals disabled. It will enable them in minit.
+ var oset sigset
+ rtsigprocmask(_SIG_SETMASK, &sigset_all, &oset, int32(unsafe.Sizeof(oset)))
+ ret := clone(cloneFlags, stk, unsafe.Pointer(mp), unsafe.Pointer(mp.g0), unsafe.Pointer(funcPC(mstart)))
+ rtsigprocmask(_SIG_SETMASK, &oset, nil, int32(unsafe.Sizeof(oset)))
+
+ if ret < 0 {
+ print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", -ret, ")\n")
+ throw("newosproc")
+ }
+}
+
+// Version of newosproc that doesn't require a valid G.
+//go:nosplit
+func newosproc0(stacksize uintptr, fn unsafe.Pointer) {
+ stack := sysAlloc(stacksize, &memstats.stacks_sys)
+ if stack == nil {
+ write(2, unsafe.Pointer(&failallocatestack[0]), int32(len(failallocatestack)))
+ exit(1)
+ }
+ ret := clone(cloneFlags, unsafe.Pointer(uintptr(stack)+stacksize), nil, nil, fn)
+ if ret < 0 {
+ write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
+ exit(1)
+ }
+}
+
+var failallocatestack = []byte("runtime: failed to allocate stack for the new OS thread\n")
+var failthreadcreate = []byte("runtime: failed to create new OS thread\n")
+
+const (
+ _AT_NULL = 0 // End of vector
+ _AT_PAGESZ = 6 // System physical page size
+ _AT_RANDOM = 25 // introduced in 2.6.29
+)
+
+func sysargs(argc int32, argv **byte) {
+ n := argc + 1
+
+ // skip over argv, envp to get to auxv
+ for argv_index(argv, n) != nil {
+ n++
+ }
+
+ // skip NULL separator
+ n++
+
+ // now argv+n is auxv
+ auxv := (*[1 << 28]uintptr)(add(unsafe.Pointer(argv), uintptr(n)*sys.PtrSize))
+ for i := 0; auxv[i] != _AT_NULL; i += 2 {
+ tag, val := auxv[i], auxv[i+1]
+ switch tag {
+ case _AT_RANDOM:
+ // The kernel provides a pointer to 16-bytes
+ // worth of random data.
+ startupRandomData = (*[16]byte)(unsafe.Pointer(val))[:]
+
+ case _AT_PAGESZ:
+ // Check that the true physical page size is
+ // compatible with the runtime's assumed
+ // physical page size.
+ if sys.PhysPageSize < val {
+ print("runtime: kernel page size (", val, ") is larger than runtime page size (", sys.PhysPageSize, ")\n")
+ exit(1)
+ }
+ if sys.PhysPageSize%val != 0 {
+ print("runtime: runtime page size (", sys.PhysPageSize, ") is not a multiple of kernel page size (", val, ")\n")
+ exit(1)
+ }
+ }
+
+ archauxv(tag, val)
+ }
+}
+
+func osinit() {
+ ncpu = getproccount()
+}
+
+var urandom_dev = []byte("/dev/urandom\x00")
+
+func getRandomData(r []byte) {
+ if startupRandomData != nil {
+ n := copy(r, startupRandomData)
+ extendRandom(r, n)
+ return
+ }
+ fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
+ n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
+ closefd(fd)
+ extendRandom(r, int(n))
+}
+
+func goenvs() {
+ goenvs_unix()
+}
+
+// Called to do synchronous initialization of Go code built with
+// -buildmode=c-archive or -buildmode=c-shared.
+// None of the Go runtime is initialized.
+//go:nosplit
+//go:nowritebarrierrec
+func libpreinit() {
+ initsig(true)
+}
+
+// Called to initialize a new m (including the bootstrap m).
+// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
+func mpreinit(mp *m) {
+ mp.gsignal = malg(32 * 1024) // Linux wants >= 2K
+ mp.gsignal.m = mp
+}
+
+//go:nosplit
+func msigsave(mp *m) {
+ smask := &mp.sigmask
+ rtsigprocmask(_SIG_SETMASK, nil, smask, int32(unsafe.Sizeof(*smask)))
+}
+
+//go:nosplit
+func msigrestore(sigmask sigset) {
+ rtsigprocmask(_SIG_SETMASK, &sigmask, nil, int32(unsafe.Sizeof(sigmask)))
+}
+
+//go:nosplit
+func sigblock() {
+ rtsigprocmask(_SIG_SETMASK, &sigset_all, nil, int32(unsafe.Sizeof(sigset_all)))
+}
+
+func gettid() uint32
+
+// Called to initialize a new m (including the bootstrap m).
+// Called on the new thread, cannot allocate memory.
+func minit() {
+ // Initialize signal handling.
+ _g_ := getg()
+
+ var st sigaltstackt
+ sigaltstack(nil, &st)
+ if st.ss_flags&_SS_DISABLE != 0 {
+ signalstack(&_g_.m.gsignal.stack)
+ _g_.m.newSigstack = true
+ } else {
+ // Use existing signal stack.
+ stsp := uintptr(unsafe.Pointer(st.ss_sp))
+ _g_.m.gsignal.stack.lo = stsp
+ _g_.m.gsignal.stack.hi = stsp + st.ss_size
+ _g_.m.gsignal.stackguard0 = stsp + _StackGuard
+ _g_.m.gsignal.stackguard1 = stsp + _StackGuard
+ _g_.m.gsignal.stackAlloc = st.ss_size
+ _g_.m.newSigstack = false
+ }
+
+ // for debuggers, in case cgo created the thread
+ _g_.m.procid = uint64(gettid())
+
+ // restore signal mask from m.sigmask and unblock essential signals
+ nmask := _g_.m.sigmask
+ for i := range sigtable {
+ if sigtable[i].flags&_SigUnblock != 0 {
+ sigdelset(&nmask, i)
+ }
+ }
+ rtsigprocmask(_SIG_SETMASK, &nmask, nil, int32(unsafe.Sizeof(nmask)))
+}
+
+// Called from dropm to undo the effect of an minit.
+//go:nosplit
+func unminit() {
+ if getg().m.newSigstack {
+ signalstack(nil)
+ }
+}
+
+func memlimit() uintptr {
+ /*
+ TODO: Convert to Go when something actually uses the result.
+
+ Rlimit rl;
+ extern byte runtime·text[], runtime·end[];
+ uintptr used;
+
+ if(runtime·getrlimit(RLIMIT_AS, &rl) != 0)
+ return 0;
+ if(rl.rlim_cur >= 0x7fffffff)
+ return 0;
+
+ // Estimate our VM footprint excluding the heap.
+ // Not an exact science: use size of binary plus
+ // some room for thread stacks.
+ used = runtime·end - runtime·text + (64<<20);
+ if(used >= rl.rlim_cur)
+ return 0;
+
+ // If there's not at least 16 MB left, we're probably
+ // not going to be able to do much. Treat as no limit.
+ rl.rlim_cur -= used;
+ if(rl.rlim_cur < (16<<20))
+ return 0;
+
+ return rl.rlim_cur - used;
+ */
+
+ return 0
+}
+
+//#ifdef GOARCH_386
+//#define sa_handler k_sa_handler
+//#endif
+
+func sigreturn()
+func sigtramp()
+func cgoSigtramp()
+
//go:noescape
func rt_sigaction(sig uintptr, new, old *sigactiont, size uintptr) int32
@@ -34,3 +380,88 @@ func raiseproc(sig int32)
//go:noescape
func sched_getaffinity(pid, len uintptr, buf *uintptr) int32
func osyield()
+
+//go:nosplit
+//go:nowritebarrierrec
+func setsig(i int32, fn uintptr, restart bool) {
+ var sa sigactiont
+ memclr(unsafe.Pointer(&sa), unsafe.Sizeof(sa))
+ sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTORER
+ if restart {
+ sa.sa_flags |= _SA_RESTART
+ }
+ sigfillset(&sa.sa_mask)
+ // Although Linux manpage says "sa_restorer element is obsolete and
+ // should not be used". x86_64 kernel requires it. Only use it on
+ // x86.
+ if GOARCH == "386" || GOARCH == "amd64" {
+ sa.sa_restorer = funcPC(sigreturn)
+ }
+ if fn == funcPC(sighandler) {
+ if iscgo {
+ fn = funcPC(cgoSigtramp)
+ } else {
+ fn = funcPC(sigtramp)
+ }
+ }
+ sa.sa_handler = fn
+ rt_sigaction(uintptr(i), &sa, nil, unsafe.Sizeof(sa.sa_mask))
+}
+
+//go:nosplit
+//go:nowritebarrierrec
+func setsigstack(i int32) {
+ var sa sigactiont
+ if rt_sigaction(uintptr(i), nil, &sa, unsafe.Sizeof(sa.sa_mask)) != 0 {
+ throw("rt_sigaction failure")
+ }
+ if sa.sa_handler == 0 || sa.sa_handler == _SIG_DFL || sa.sa_handler == _SIG_IGN || sa.sa_flags&_SA_ONSTACK != 0 {
+ return
+ }
+ sa.sa_flags |= _SA_ONSTACK
+ if rt_sigaction(uintptr(i), &sa, nil, unsafe.Sizeof(sa.sa_mask)) != 0 {
+ throw("rt_sigaction failure")
+ }
+}
+
+//go:nosplit
+//go:nowritebarrierrec
+func getsig(i int32) uintptr {
+ var sa sigactiont
+
+ memclr(unsafe.Pointer(&sa), unsafe.Sizeof(sa))
+ if rt_sigaction(uintptr(i), nil, &sa, unsafe.Sizeof(sa.sa_mask)) != 0 {
+ throw("rt_sigaction read failure")
+ }
+ if sa.sa_handler == funcPC(sigtramp) || sa.sa_handler == funcPC(cgoSigtramp) {
+ return funcPC(sighandler)
+ }
+ return sa.sa_handler
+}
+
+//go:nosplit
+func signalstack(s *stack) {
+ var st sigaltstackt
+ if s == nil {
+ st.ss_flags = _SS_DISABLE
+ } else {
+ st.ss_sp = (*byte)(unsafe.Pointer(s.lo))
+ st.ss_size = s.hi - s.lo
+ st.ss_flags = 0
+ }
+ sigaltstack(&st, nil)
+}
+
+//go:nosplit
+//go:nowritebarrierrec
+func updatesigmask(m sigmask) {
+ var mask sigset
+ sigcopyset(&mask, m)
+ rtsigprocmask(_SIG_SETMASK, &mask, nil, int32(unsafe.Sizeof(mask)))
+}
+
+func unblocksig(sig int32) {
+ var mask sigset
+ sigaddset(&mask, int(sig))
+ rtsigprocmask(_SIG_UNBLOCK, &mask, nil, int32(unsafe.Sizeof(mask)))
+}
diff --git a/src/runtime/os_linux_386.go b/src/runtime/os_linux_386.go
deleted file mode 100644
index 0f39cade3b..0000000000
--- a/src/runtime/os_linux_386.go
+++ /dev/null
@@ -1,33 +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 runtime
-
-import (
- "runtime/internal/sys"
- "unsafe"
-)
-
-const (
- _AT_NULL = 0
- _AT_RANDOM = 25
- _AT_SYSINFO = 32
-)
-
-func sysargs(argc int32, argv **byte) {
- // skip over argv, envv to get to auxv
- n := argc + 1
- for argv_index(argv, n) != nil {
- n++
- }
- n++
- auxv := (*[1 << 28]uint32)(add(unsafe.Pointer(argv), uintptr(n)*sys.PtrSize))
-
- for i := 0; auxv[i] != _AT_NULL; i += 2 {
- switch auxv[i] {
- case _AT_RANDOM:
- startupRandomData = (*[16]byte)(unsafe.Pointer(uintptr(auxv[i+1])))[:]
- }
- }
-}
diff --git a/src/runtime/os_linux_arm.go b/src/runtime/os_linux_arm.go
index 8fdfb585ba..8e2765a413 100644
--- a/src/runtime/os_linux_arm.go
+++ b/src/runtime/os_linux_arm.go
@@ -4,16 +4,11 @@
package runtime
-import (
- "runtime/internal/sys"
- "unsafe"
-)
+import "unsafe"
const (
- _AT_NULL = 0
_AT_PLATFORM = 15 // introduced in at least 2.6.11
_AT_HWCAP = 16 // introduced in at least 2.6.11
- _AT_RANDOM = 25 // introduced in 2.6.29
_HWCAP_VFP = 1 << 6 // introduced in at least 2.6.11
_HWCAP_VFPv3 = 1 << 13 // introduced in 2.6.30
@@ -36,33 +31,23 @@ func checkgoarm() {
}
}
-func sysargs(argc int32, argv **byte) {
- // skip over argv, envv to get to auxv
- n := argc + 1
- for argv_index(argv, n) != nil {
- n++
- }
- n++
- auxv := (*[1 << 28]uint32)(add(unsafe.Pointer(argv), uintptr(n)*sys.PtrSize))
-
- for i := 0; auxv[i] != _AT_NULL; i += 2 {
- switch auxv[i] {
- case _AT_RANDOM: // kernel provides a pointer to 16-bytes worth of random data
- startupRandomData = (*[16]byte)(unsafe.Pointer(uintptr(auxv[i+1])))[:]
- // the pointer provided may not be word aligned, so we must treat it
- // as a byte array.
- randomNumber = uint32(startupRandomData[4]) | uint32(startupRandomData[5])<<8 |
- uint32(startupRandomData[6])<<16 | uint32(startupRandomData[7])<<24
+func archauxv(tag, val uintptr) {
+ switch tag {
+ case _AT_RANDOM:
+ // sysargs filled in startupRandomData, but that
+ // pointer may not be word aligned, so we must treat
+ // it as a byte array.
+ randomNumber = uint32(startupRandomData[4]) | uint32(startupRandomData[5])<<8 |
+ uint32(startupRandomData[6])<<16 | uint32(startupRandomData[7])<<24
- case _AT_PLATFORM: // v5l, v6l, v7l
- t := *(*uint8)(unsafe.Pointer(uintptr(auxv[i+1] + 1)))
- if '5' <= t && t <= '7' {
- armArch = t - '0'
- }
-
- case _AT_HWCAP: // CPU capability bit flags
- hwcap = auxv[i+1]
+ case _AT_PLATFORM: // v5l, v6l, v7l
+ t := *(*uint8)(unsafe.Pointer(val + 1))
+ if '5' <= t && t <= '7' {
+ armArch = t - '0'
}
+
+ case _AT_HWCAP: // CPU capability bit flags
+ hwcap = uint32(val)
}
}
diff --git a/src/runtime/os_linux_arm64.go b/src/runtime/os_linux_arm64.go
index 3f994f128b..43262aea14 100644
--- a/src/runtime/os_linux_arm64.go
+++ b/src/runtime/os_linux_arm64.go
@@ -4,13 +4,19 @@
package runtime
-const (
- _AT_NULL = 0
- _AT_RANDOM = 25 // introduced in 2.6.29
-)
-
var randomNumber uint32
+func archauxv(tag, val uintptr) {
+ switch tag {
+ case _AT_RANDOM:
+ // sysargs filled in startupRandomData, but that
+ // pointer may not be word aligned, so we must treat
+ // it as a byte array.
+ randomNumber = uint32(startupRandomData[4]) | uint32(startupRandomData[5])<<8 |
+ uint32(startupRandomData[6])<<16 | uint32(startupRandomData[7])<<24
+ }
+}
+
//go:nosplit
func cputicks() int64 {
// Currently cputicks() is used in blocking profiler and to seed fastrand1().
diff --git a/src/runtime/os2_linux_generic.go b/src/runtime/os_linux_generic.go
index 01e6c8a5ec..a16d140776 100644
--- a/src/runtime/os2_linux_generic.go
+++ b/src/runtime/os_linux_generic.go
@@ -4,6 +4,7 @@
// +build !mips64
// +build !mips64le
+// +build !s390x
// +build linux
package runtime
@@ -27,3 +28,21 @@ type rlimit struct {
rlim_cur uintptr
rlim_max uintptr
}
+
+var sigset_all = sigset{^uint32(0), ^uint32(0)}
+
+func sigaddset(mask *sigset, i int) {
+ (*mask)[(i-1)/32] |= 1 << ((uint32(i) - 1) & 31)
+}
+
+func sigdelset(mask *sigset, i int) {
+ (*mask)[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
+}
+
+func sigfillset(mask *uint64) {
+ *mask = ^uint64(0)
+}
+
+func sigcopyset(mask *sigset, m sigmask) {
+ copy((*mask)[:], m[:])
+}
diff --git a/src/runtime/os_linux_mips64x.go b/src/runtime/os_linux_mips64x.go
index 4d2e9e8a20..92b5c82af7 100644
--- a/src/runtime/os_linux_mips64x.go
+++ b/src/runtime/os_linux_mips64x.go
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build mips64 mips64le
// +build linux
+// +build mips64 mips64le
package runtime
@@ -16,3 +16,38 @@ func cputicks() int64 {
// randomNumber provides better seeding of fastrand1.
return nanotime() + int64(randomNumber)
}
+
+const (
+ _SS_DISABLE = 2
+ _NSIG = 65
+ _SI_USER = 0
+ _SIG_BLOCK = 1
+ _SIG_UNBLOCK = 2
+ _SIG_SETMASK = 3
+ _RLIMIT_AS = 6
+)
+
+type sigset [2]uint64
+
+type rlimit struct {
+ rlim_cur uintptr
+ rlim_max uintptr
+}
+
+var sigset_all = sigset{^uint64(0), ^uint64(0)}
+
+func sigaddset(mask *sigset, i int) {
+ (*mask)[(i-1)/64] |= 1 << ((uint32(i) - 1) & 63)
+}
+
+func sigdelset(mask *sigset, i int) {
+ (*mask)[(i-1)/64] &^= 1 << ((uint32(i) - 1) & 63)
+}
+
+func sigfillset(mask *[2]uint64) {
+ (*mask)[0], (*mask)[1] = ^uint64(0), ^uint64(0)
+}
+
+func sigcopyset(mask *sigset, m sigmask) {
+ (*mask)[0] = uint64(m[0]) | uint64(m[1])<<32
+}
diff --git a/src/runtime/os_linux_noauxv.go b/src/runtime/os_linux_noauxv.go
new file mode 100644
index 0000000000..0b46f594ce
--- /dev/null
+++ b/src/runtime/os_linux_noauxv.go
@@ -0,0 +1,10 @@
+// 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.
+
+// +build !amd64,!arm,!arm64
+
+package runtime
+
+func archauxv(tag, val uintptr) {
+}
diff --git a/src/runtime/os_linux_s390x.go b/src/runtime/os_linux_s390x.go
new file mode 100644
index 0000000000..e659dff716
--- /dev/null
+++ b/src/runtime/os_linux_s390x.go
@@ -0,0 +1,46 @@
+// Copyright 2016 The Go 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 runtime
+
+const (
+ _SS_DISABLE = 2
+ _NSIG = 65
+ _SI_USER = 0
+ _SIG_BLOCK = 0
+ _SIG_UNBLOCK = 1
+ _SIG_SETMASK = 2
+ _RLIMIT_AS = 9
+)
+
+type sigset uint64
+
+type rlimit struct {
+ rlim_cur uintptr
+ rlim_max uintptr
+}
+
+var sigset_all = sigset(^uint64(0))
+
+func sigaddset(mask *sigset, i int) {
+ if i > 64 {
+ throw("unexpected signal greater than 64")
+ }
+ *mask |= 1 << (uint(i) - 1)
+}
+
+func sigdelset(mask *sigset, i int) {
+ if i > 64 {
+ throw("unexpected signal greater than 64")
+ }
+ *mask &^= 1 << (uint(i) - 1)
+}
+
+func sigfillset(mask *uint64) {
+ *mask = ^uint64(0)
+}
+
+func sigcopyset(mask *sigset, m sigmask) {
+ *mask = sigset(uint64(m[0]) | uint64(m[1])<<32)
+}
diff --git a/src/runtime/os_netbsd.go b/src/runtime/os_netbsd.go
index 0fba16d4f4..41f34f7132 100644
--- a/src/runtime/os_netbsd.go
+++ b/src/runtime/os_netbsd.go
@@ -4,7 +4,23 @@
package runtime
-import "unsafe"
+import (
+ "runtime/internal/atomic"
+ "unsafe"
+)
+
+const (
+ _SS_DISABLE = 4
+ _SIG_BLOCK = 1
+ _SIG_UNBLOCK = 2
+ _SIG_SETMASK = 3
+ _NSIG = 33
+ _SI_USER = 0
+
+ // From NetBSD's <sys/ucontext.h>
+ _UC_SIGMASK = 0x01
+ _UC_CPU = 0x04
+)
type mOS struct {
waitsemacount uint32
@@ -45,3 +61,268 @@ func lwp_unpark(lwp int32, hint unsafe.Pointer) int32
func lwp_self() int32
func osyield()
+
+const (
+ _ESRCH = 3
+ _ETIMEDOUT = 60
+
+ // From NetBSD's <sys/time.h>
+ _CLOCK_REALTIME = 0
+ _CLOCK_VIRTUAL = 1
+ _CLOCK_PROF = 2
+ _CLOCK_MONOTONIC = 3
+)
+
+var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}}
+
+// From NetBSD's <sys/sysctl.h>
+const (
+ _CTL_HW = 6
+ _HW_NCPU = 3
+)
+
+func getncpu() int32 {
+ mib := [2]uint32{_CTL_HW, _HW_NCPU}
+ out := uint32(0)
+ nout := unsafe.Sizeof(out)
+ ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
+ if ret >= 0 {
+ return int32(out)
+ }
+ return 1
+}
+
+//go:nosplit
+func semacreate(mp *m) {
+}
+
+//go:nosplit
+func semasleep(ns int64) int32 {
+ _g_ := getg()
+
+ // Compute sleep deadline.
+ var tsp *timespec
+ if ns >= 0 {
+ var ts timespec
+ var nsec int32
+ ns += nanotime()
+ ts.set_sec(timediv(ns, 1000000000, &nsec))
+ ts.set_nsec(nsec)
+ tsp = &ts
+ }
+
+ for {
+ v := atomic.Load(&_g_.m.waitsemacount)
+ if v > 0 {
+ if atomic.Cas(&_g_.m.waitsemacount, v, v-1) {
+ return 0 // semaphore acquired
+ }
+ continue
+ }
+
+ // Sleep until unparked by semawakeup or timeout.
+ ret := lwp_park(tsp, 0, unsafe.Pointer(&_g_.m.waitsemacount), nil)
+ if ret == _ETIMEDOUT {
+ return -1
+ }
+ }
+}
+
+//go:nosplit
+func semawakeup(mp *m) {
+ atomic.Xadd(&mp.waitsemacount, 1)
+ // From NetBSD's _lwp_unpark(2) manual:
+ // "If the target LWP is not currently waiting, it will return
+ // immediately upon the next call to _lwp_park()."
+ ret := lwp_unpark(int32(mp.procid), unsafe.Pointer(&mp.waitsemacount))
+ if ret != 0 && ret != _ESRCH {
+ // semawakeup can be called on signal stack.
+ systemstack(func() {
+ print("thrwakeup addr=", &mp.waitsemacount, " sem=", mp.waitsemacount, " ret=", ret, "\n")
+ })
+ }
+}
+
+// May run with m.p==nil, so write barriers are not allowed.
+//go:nowritebarrier
+func newosproc(mp *m, stk unsafe.Pointer) {
+ if false {
+ print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " id=", mp.id, " ostk=", &mp, "\n")
+ }
+
+ var uc ucontextt
+ getcontext(unsafe.Pointer(&uc))
+
+ uc.uc_flags = _UC_SIGMASK | _UC_CPU
+ uc.uc_link = nil
+ uc.uc_sigmask = sigset_all
+
+ lwp_mcontext_init(&uc.uc_mcontext, stk, mp, mp.g0, funcPC(netbsdMstart))
+
+ ret := lwp_create(unsafe.Pointer(&uc), 0, unsafe.Pointer(&mp.procid))
+ if ret < 0 {
+ print("runtime: failed to create new OS thread (have ", mcount()-1, " already; errno=", -ret, ")\n")
+ throw("runtime.newosproc")
+ }
+}
+
+// netbsdMStart is the function call that starts executing a newly
+// created thread. On NetBSD, a new thread inherits the signal stack
+// of the creating thread. That confuses minit, so we remove that
+// signal stack here before calling the regular mstart. It's a bit
+// baroque to remove a signal stack here only to add one in minit, but
+// it's a simple change that keeps NetBSD working like other OS's.
+// At this point all signals are blocked, so there is no race.
+//go:nosplit
+func netbsdMstart() {
+ signalstack(nil)
+ mstart()
+}
+
+func osinit() {
+ ncpu = getncpu()
+}
+
+var urandom_dev = []byte("/dev/urandom\x00")
+
+//go:nosplit
+func getRandomData(r []byte) {
+ fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
+ n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
+ closefd(fd)
+ extendRandom(r, int(n))
+}
+
+func goenvs() {
+ goenvs_unix()
+}
+
+// Called to initialize a new m (including the bootstrap m).
+// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
+func mpreinit(mp *m) {
+ mp.gsignal = malg(32 * 1024)
+ mp.gsignal.m = mp
+}
+
+//go:nosplit
+func msigsave(mp *m) {
+ sigprocmask(_SIG_SETMASK, nil, &mp.sigmask)
+}
+
+//go:nosplit
+func msigrestore(sigmask sigset) {
+ sigprocmask(_SIG_SETMASK, &sigmask, nil)
+}
+
+//go:nosplit
+func sigblock() {
+ sigprocmask(_SIG_SETMASK, &sigset_all, nil)
+}
+
+// Called to initialize a new m (including the bootstrap m).
+// Called on the new thread, cannot allocate memory.
+func minit() {
+ _g_ := getg()
+ _g_.m.procid = uint64(lwp_self())
+
+ // Initialize signal handling.
+
+ // On NetBSD a thread created by pthread_create inherits the
+ // signal stack of the creating thread. We always create a
+ // new signal stack here, to avoid having two Go threads using
+ // the same signal stack. This breaks the case of a thread
+ // created in C that calls sigaltstack and then calls a Go
+ // function, because we will lose track of the C code's
+ // sigaltstack, but it's the best we can do.
+ signalstack(&_g_.m.gsignal.stack)
+ _g_.m.newSigstack = true
+
+ // restore signal mask from m.sigmask and unblock essential signals
+ nmask := _g_.m.sigmask
+ for i := range sigtable {
+ if sigtable[i].flags&_SigUnblock != 0 {
+ nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
+ }
+ }
+ sigprocmask(_SIG_SETMASK, &nmask, nil)
+}
+
+// Called from dropm to undo the effect of an minit.
+//go:nosplit
+func unminit() {
+ if getg().m.newSigstack {
+ signalstack(nil)
+ }
+}
+
+func memlimit() uintptr {
+ return 0
+}
+
+func sigtramp()
+
+type sigactiont struct {
+ sa_sigaction uintptr
+ sa_mask sigset
+ sa_flags int32
+}
+
+//go:nosplit
+//go:nowritebarrierrec
+func setsig(i int32, fn uintptr, restart bool) {
+ var sa sigactiont
+ sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK
+ if restart {
+ sa.sa_flags |= _SA_RESTART
+ }
+ sa.sa_mask = sigset_all
+ if fn == funcPC(sighandler) {
+ fn = funcPC(sigtramp)
+ }
+ sa.sa_sigaction = fn
+ sigaction(i, &sa, nil)
+}
+
+//go:nosplit
+//go:nowritebarrierrec
+func setsigstack(i int32) {
+ throw("setsigstack")
+}
+
+//go:nosplit
+//go:nowritebarrierrec
+func getsig(i int32) uintptr {
+ var sa sigactiont
+ sigaction(i, nil, &sa)
+ if sa.sa_sigaction == funcPC(sigtramp) {
+ return funcPC(sighandler)
+ }
+ return sa.sa_sigaction
+}
+
+//go:nosplit
+func signalstack(s *stack) {
+ var st sigaltstackt
+ if s == nil {
+ st.ss_flags = _SS_DISABLE
+ } else {
+ st.ss_sp = s.lo
+ st.ss_size = s.hi - s.lo
+ st.ss_flags = 0
+ }
+ sigaltstack(&st, nil)
+}
+
+//go:nosplit
+//go:nowritebarrierrec
+func updatesigmask(m sigmask) {
+ var mask sigset
+ copy(mask.__bits[:], m[:])
+ sigprocmask(_SIG_SETMASK, &mask, nil)
+}
+
+func unblocksig(sig int32) {
+ var mask sigset
+ mask.__bits[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31)
+ sigprocmask(_SIG_UNBLOCK, &mask, nil)
+}
diff --git a/src/runtime/os1_netbsd_386.go b/src/runtime/os_netbsd_386.go
index 037f7e36dc..037f7e36dc 100644
--- a/src/runtime/os1_netbsd_386.go
+++ b/src/runtime/os_netbsd_386.go
diff --git a/src/runtime/os1_netbsd_amd64.go b/src/runtime/os_netbsd_amd64.go
index 5118b0c4ff..5118b0c4ff 100644
--- a/src/runtime/os1_netbsd_amd64.go
+++ b/src/runtime/os_netbsd_amd64.go
diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go
index 8bdf5a271f..9147091a49 100644
--- a/src/runtime/os_windows.go
+++ b/src/runtime/os_windows.go
@@ -1,17 +1,127 @@
-// Copyright 2014 The Go Authors. All rights reserved.
+// 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 runtime
-import "unsafe"
+import (
+ "runtime/internal/atomic"
+ "unsafe"
+)
+
+// TODO(brainman): should not need those
+const (
+ _NSIG = 65
+)
+
+//go:cgo_import_dynamic runtime._AddVectoredExceptionHandler AddVectoredExceptionHandler%2 "kernel32.dll"
+//go:cgo_import_dynamic runtime._CloseHandle CloseHandle%1 "kernel32.dll"
+//go:cgo_import_dynamic runtime._CreateEventA CreateEventA%4 "kernel32.dll"
+//go:cgo_import_dynamic runtime._CreateIoCompletionPort CreateIoCompletionPort%4 "kernel32.dll"
+//go:cgo_import_dynamic runtime._CreateThread CreateThread%6 "kernel32.dll"
+//go:cgo_import_dynamic runtime._CreateWaitableTimerA CreateWaitableTimerA%3 "kernel32.dll"
+//go:cgo_import_dynamic runtime._CryptAcquireContextW CryptAcquireContextW%5 "advapi32.dll"
+//go:cgo_import_dynamic runtime._CryptGenRandom CryptGenRandom%3 "advapi32.dll"
+//go:cgo_import_dynamic runtime._CryptReleaseContext CryptReleaseContext%2 "advapi32.dll"
+//go:cgo_import_dynamic runtime._DuplicateHandle DuplicateHandle%7 "kernel32.dll"
+//go:cgo_import_dynamic runtime._ExitProcess ExitProcess%1 "kernel32.dll"
+//go:cgo_import_dynamic runtime._FreeEnvironmentStringsW FreeEnvironmentStringsW%1 "kernel32.dll"
+//go:cgo_import_dynamic runtime._GetConsoleMode GetConsoleMode%2 "kernel32.dll"
+//go:cgo_import_dynamic runtime._GetEnvironmentStringsW GetEnvironmentStringsW%0 "kernel32.dll"
+//go:cgo_import_dynamic runtime._GetProcAddress GetProcAddress%2 "kernel32.dll"
+//go:cgo_import_dynamic runtime._GetProcessAffinityMask GetProcessAffinityMask%3 "kernel32.dll"
+//go:cgo_import_dynamic runtime._GetQueuedCompletionStatus GetQueuedCompletionStatus%5 "kernel32.dll"
+//go:cgo_import_dynamic runtime._GetStdHandle GetStdHandle%1 "kernel32.dll"
+//go:cgo_import_dynamic runtime._GetSystemInfo GetSystemInfo%1 "kernel32.dll"
+//go:cgo_import_dynamic runtime._GetThreadContext GetThreadContext%2 "kernel32.dll"
+//go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 "kernel32.dll"
+//go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA%1 "kernel32.dll"
+//go:cgo_import_dynamic runtime._NtWaitForSingleObject NtWaitForSingleObject%3 "ntdll.dll"
+//go:cgo_import_dynamic runtime._ResumeThread ResumeThread%1 "kernel32.dll"
+//go:cgo_import_dynamic runtime._SetConsoleCtrlHandler SetConsoleCtrlHandler%2 "kernel32.dll"
+//go:cgo_import_dynamic runtime._SetErrorMode SetErrorMode%1 "kernel32.dll"
+//go:cgo_import_dynamic runtime._SetEvent SetEvent%1 "kernel32.dll"
+//go:cgo_import_dynamic runtime._SetProcessPriorityBoost SetProcessPriorityBoost%2 "kernel32.dll"
+//go:cgo_import_dynamic runtime._SetThreadPriority SetThreadPriority%2 "kernel32.dll"
+//go:cgo_import_dynamic runtime._SetUnhandledExceptionFilter SetUnhandledExceptionFilter%1 "kernel32.dll"
+//go:cgo_import_dynamic runtime._SetWaitableTimer SetWaitableTimer%6 "kernel32.dll"
+//go:cgo_import_dynamic runtime._SuspendThread SuspendThread%1 "kernel32.dll"
+//go:cgo_import_dynamic runtime._SwitchToThread SwitchToThread%0 "kernel32.dll"
+//go:cgo_import_dynamic runtime._VirtualAlloc VirtualAlloc%4 "kernel32.dll"
+//go:cgo_import_dynamic runtime._VirtualFree VirtualFree%3 "kernel32.dll"
+//go:cgo_import_dynamic runtime._WSAGetOverlappedResult WSAGetOverlappedResult%5 "ws2_32.dll"
+//go:cgo_import_dynamic runtime._WaitForSingleObject WaitForSingleObject%2 "kernel32.dll"
+//go:cgo_import_dynamic runtime._WriteConsoleW WriteConsoleW%5 "kernel32.dll"
+//go:cgo_import_dynamic runtime._WriteFile WriteFile%5 "kernel32.dll"
+//go:cgo_import_dynamic runtime._timeBeginPeriod timeBeginPeriod%1 "winmm.dll"
+
+type stdFunction unsafe.Pointer
+
+var (
+ // Following syscalls are available on every Windows PC.
+ // All these variables are set by the Windows executable
+ // loader before the Go program starts.
+ _AddVectoredExceptionHandler,
+ _CloseHandle,
+ _CreateEventA,
+ _CreateIoCompletionPort,
+ _CreateThread,
+ _CreateWaitableTimerA,
+ _CryptAcquireContextW,
+ _CryptGenRandom,
+ _CryptReleaseContext,
+ _DuplicateHandle,
+ _ExitProcess,
+ _FreeEnvironmentStringsW,
+ _GetConsoleMode,
+ _GetEnvironmentStringsW,
+ _GetProcAddress,
+ _GetProcessAffinityMask,
+ _GetQueuedCompletionStatus,
+ _GetStdHandle,
+ _GetSystemInfo,
+ _GetThreadContext,
+ _LoadLibraryW,
+ _LoadLibraryA,
+ _NtWaitForSingleObject,
+ _ResumeThread,
+ _SetConsoleCtrlHandler,
+ _SetErrorMode,
+ _SetEvent,
+ _SetProcessPriorityBoost,
+ _SetThreadPriority,
+ _SetUnhandledExceptionFilter,
+ _SetWaitableTimer,
+ _SuspendThread,
+ _SwitchToThread,
+ _VirtualAlloc,
+ _VirtualFree,
+ _WSAGetOverlappedResult,
+ _WaitForSingleObject,
+ _WriteConsoleW,
+ _WriteFile,
+ _timeBeginPeriod,
+ _ stdFunction
+
+ // Following syscalls are only available on some Windows PCs.
+ // We will load syscalls, if available, before using them.
+ _AddDllDirectory,
+ _AddVectoredContinueHandler,
+ _GetQueuedCompletionStatusEx,
+ _LoadLibraryExW,
+ _ stdFunction
+)
+
+// Function to be called by windows CreateThread
+// to start new os thread.
+func tstart_stdcall(newm *m) uint32
+
+func ctrlhandler(_type uint32) uint32
type mOS struct {
waitsema uintptr // semaphore for parking on locks
}
-type stdFunction *byte
-
//go:linkname os_sigpipe os.sigpipe
func os_sigpipe() {
throw("too many writes on closed pipe")
@@ -30,3 +140,605 @@ func read(fd int32, p unsafe.Pointer, n int32) int32 {
throw("unimplemented")
return -1
}
+
+type sigset struct{}
+
+// Call a Windows function with stdcall conventions,
+// and switch to os stack during the call.
+func asmstdcall(fn unsafe.Pointer)
+
+var asmstdcallAddr unsafe.Pointer
+
+func windowsFindfunc(lib uintptr, name []byte) stdFunction {
+ if name[len(name)-1] != 0 {
+ throw("usage")
+ }
+ f := stdcall2(_GetProcAddress, lib, uintptr(unsafe.Pointer(&name[0])))
+ return stdFunction(unsafe.Pointer(f))
+}
+
+func loadOptionalSyscalls() {
+ var kernel32dll = []byte("kernel32.dll\000")
+ k32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&kernel32dll[0])))
+ if k32 == 0 {
+ throw("kernel32.dll not found")
+ }
+ _AddDllDirectory = windowsFindfunc(k32, []byte("AddDllDirectory\000"))
+ _AddVectoredContinueHandler = windowsFindfunc(k32, []byte("AddVectoredContinueHandler\000"))
+ _GetQueuedCompletionStatusEx = windowsFindfunc(k32, []byte("GetQueuedCompletionStatusEx\000"))
+ _LoadLibraryExW = windowsFindfunc(k32, []byte("LoadLibraryExW\000"))
+}
+
+//go:nosplit
+func getLoadLibrary() uintptr {
+ return uintptr(unsafe.Pointer(_LoadLibraryW))
+}
+
+//go:nosplit
+func getLoadLibraryEx() uintptr {
+ return uintptr(unsafe.Pointer(_LoadLibraryExW))
+}
+
+//go:nosplit
+func getGetProcAddress() uintptr {
+ return uintptr(unsafe.Pointer(_GetProcAddress))
+}
+
+func getproccount() int32 {
+ var mask, sysmask uintptr
+ ret := stdcall3(_GetProcessAffinityMask, currentProcess, uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask)))
+ if ret != 0 {
+ n := 0
+ maskbits := int(unsafe.Sizeof(mask) * 8)
+ for i := 0; i < maskbits; i++ {
+ if mask&(1<<uint(i)) != 0 {
+ n++
+ }
+ }
+ if n != 0 {
+ return int32(n)
+ }
+ }
+ // use GetSystemInfo if GetProcessAffinityMask fails
+ var info systeminfo
+ stdcall1(_GetSystemInfo, uintptr(unsafe.Pointer(&info)))
+ return int32(info.dwnumberofprocessors)
+}
+
+const (
+ currentProcess = ^uintptr(0) // -1 = current process
+ currentThread = ^uintptr(1) // -2 = current thread
+)
+
+// in sys_windows_386.s and sys_windows_amd64.s:
+func externalthreadhandler()
+func getlasterror() uint32
+func setlasterror(err uint32)
+
+// When loading DLLs, we prefer to use LoadLibraryEx with
+// LOAD_LIBRARY_SEARCH_* flags, if available. LoadLibraryEx is not
+// available on old Windows, though, and the LOAD_LIBRARY_SEARCH_*
+// flags are not available on some versions of Windows without a
+// security patch.
+//
+// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says:
+// "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows
+// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on
+// systems that have KB2533623 installed. To determine whether the
+// flags are available, use GetProcAddress to get the address of the
+// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories
+// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_*
+// flags can be used with LoadLibraryEx."
+var useLoadLibraryEx bool
+
+var timeBeginPeriodRetValue uint32
+
+func osinit() {
+ asmstdcallAddr = unsafe.Pointer(funcPC(asmstdcall))
+ usleep2Addr = unsafe.Pointer(funcPC(usleep2))
+ switchtothreadAddr = unsafe.Pointer(funcPC(switchtothread))
+
+ setBadSignalMsg()
+
+ loadOptionalSyscalls()
+
+ useLoadLibraryEx = (_LoadLibraryExW != nil && _AddDllDirectory != nil)
+
+ disableWER()
+
+ externalthreadhandlerp = funcPC(externalthreadhandler)
+
+ initExceptionHandler()
+
+ stdcall2(_SetConsoleCtrlHandler, funcPC(ctrlhandler), 1)
+
+ timeBeginPeriodRetValue = uint32(stdcall1(_timeBeginPeriod, 1))
+
+ ncpu = getproccount()
+
+ // Windows dynamic priority boosting assumes that a process has different types
+ // of dedicated threads -- GUI, IO, computational, etc. Go processes use
+ // equivalent threads that all do a mix of GUI, IO, computations, etc.
+ // In such context dynamic priority boosting does nothing but harm, so we turn it off.
+ stdcall2(_SetProcessPriorityBoost, currentProcess, 1)
+}
+
+//go:nosplit
+func getRandomData(r []byte) {
+ const (
+ prov_rsa_full = 1
+ crypt_verifycontext = 0xF0000000
+ )
+ var handle uintptr
+ n := 0
+ if stdcall5(_CryptAcquireContextW, uintptr(unsafe.Pointer(&handle)), 0, 0, prov_rsa_full, crypt_verifycontext) != 0 {
+ if stdcall3(_CryptGenRandom, handle, uintptr(len(r)), uintptr(unsafe.Pointer(&r[0]))) != 0 {
+ n = len(r)
+ }
+ stdcall2(_CryptReleaseContext, handle, 0)
+ }
+ extendRandom(r, n)
+}
+
+func goenvs() {
+ // strings is a pointer to environment variable pairs in the form:
+ // "envA=valA\x00envB=valB\x00\x00" (in UTF-16)
+ // Two consecutive zero bytes end the list.
+ strings := unsafe.Pointer(stdcall0(_GetEnvironmentStringsW))
+ p := (*[1 << 24]uint16)(strings)[:]
+
+ n := 0
+ for from, i := 0, 0; true; i++ {
+ if p[i] == 0 {
+ // empty string marks the end
+ if i == from {
+ break
+ }
+ from = i + 1
+ n++
+ }
+ }
+ envs = make([]string, n)
+
+ for i := range envs {
+ envs[i] = gostringw(&p[0])
+ for p[0] != 0 {
+ p = p[1:]
+ }
+ p = p[1:] // skip nil byte
+ }
+
+ stdcall1(_FreeEnvironmentStringsW, uintptr(strings))
+}
+
+//go:nosplit
+func exit(code int32) {
+ stdcall1(_ExitProcess, uintptr(code))
+}
+
+//go:nosplit
+func write(fd uintptr, buf unsafe.Pointer, n int32) int32 {
+ const (
+ _STD_OUTPUT_HANDLE = ^uintptr(10) // -11
+ _STD_ERROR_HANDLE = ^uintptr(11) // -12
+ )
+ var handle uintptr
+ switch fd {
+ case 1:
+ handle = stdcall1(_GetStdHandle, _STD_OUTPUT_HANDLE)
+ case 2:
+ handle = stdcall1(_GetStdHandle, _STD_ERROR_HANDLE)
+ default:
+ // assume fd is real windows handle.
+ handle = fd
+ }
+ isASCII := true
+ b := (*[1 << 30]byte)(buf)[:n]
+ for _, x := range b {
+ if x >= 0x80 {
+ isASCII = false
+ break
+ }
+ }
+
+ if !isASCII {
+ var m uint32
+ isConsole := stdcall2(_GetConsoleMode, handle, uintptr(unsafe.Pointer(&m))) != 0
+ // If this is a console output, various non-unicode code pages can be in use.
+ // Use the dedicated WriteConsole call to ensure unicode is printed correctly.
+ if isConsole {
+ return int32(writeConsole(handle, buf, n))
+ }
+ }
+ var written uint32
+ stdcall5(_WriteFile, handle, uintptr(buf), uintptr(n), uintptr(unsafe.Pointer(&written)), 0)
+ return int32(written)
+}
+
+var (
+ utf16ConsoleBack [1000]uint16
+ utf16ConsoleBackLock mutex
+)
+
+// writeConsole writes bufLen bytes from buf to the console File.
+// It returns the number of bytes written.
+func writeConsole(handle uintptr, buf unsafe.Pointer, bufLen int32) int {
+ const surr2 = (surrogateMin + surrogateMax + 1) / 2
+
+ // Do not use defer for unlock. May cause issues when printing a panic.
+ lock(&utf16ConsoleBackLock)
+
+ b := (*[1 << 30]byte)(buf)[:bufLen]
+ s := *(*string)(unsafe.Pointer(&b))
+
+ utf16tmp := utf16ConsoleBack[:]
+
+ total := len(s)
+ w := 0
+ for len(s) > 0 {
+ if w >= len(utf16tmp)-2 {
+ writeConsoleUTF16(handle, utf16tmp[:w])
+ w = 0
+ }
+ r, n := charntorune(s)
+ s = s[n:]
+ if r < 0x10000 {
+ utf16tmp[w] = uint16(r)
+ w++
+ } else {
+ r -= 0x10000
+ utf16tmp[w] = surrogateMin + uint16(r>>10)&0x3ff
+ utf16tmp[w+1] = surr2 + uint16(r)&0x3ff
+ w += 2
+ }
+ }
+ writeConsoleUTF16(handle, utf16tmp[:w])
+ unlock(&utf16ConsoleBackLock)
+ return total
+}
+
+// writeConsoleUTF16 is the dedicated windows calls that correctly prints
+// to the console regardless of the current code page. Input is utf-16 code points.
+// The handle must be a console handle.
+func writeConsoleUTF16(handle uintptr, b []uint16) {
+ l := uint32(len(b))
+ if l == 0 {
+ return
+ }
+ var written uint32
+ stdcall5(_WriteConsoleW,
+ handle,
+ uintptr(unsafe.Pointer(&b[0])),
+ uintptr(l),
+ uintptr(unsafe.Pointer(&written)),
+ 0,
+ )
+ return
+}
+
+//go:nosplit
+func semasleep(ns int64) int32 {
+ // store ms in ns to save stack space
+ if ns < 0 {
+ ns = _INFINITE
+ } else {
+ ns = int64(timediv(ns, 1000000, nil))
+ if ns == 0 {
+ ns = 1
+ }
+ }
+ if stdcall2(_WaitForSingleObject, getg().m.waitsema, uintptr(ns)) != 0 {
+ return -1 // timeout
+ }
+ return 0
+}
+
+//go:nosplit
+func semawakeup(mp *m) {
+ stdcall1(_SetEvent, mp.waitsema)
+}
+
+//go:nosplit
+func semacreate(mp *m) {
+ if mp.waitsema != 0 {
+ return
+ }
+ mp.waitsema = stdcall4(_CreateEventA, 0, 0, 0, 0)
+}
+
+// May run with m.p==nil, so write barriers are not allowed. This
+// function is called by newosproc0, so it is also required to
+// operate without stack guards.
+//go:nowritebarrierc
+//go:nosplit
+func newosproc(mp *m, stk unsafe.Pointer) {
+ const _STACK_SIZE_PARAM_IS_A_RESERVATION = 0x00010000
+ thandle := stdcall6(_CreateThread, 0, 0x20000,
+ funcPC(tstart_stdcall), uintptr(unsafe.Pointer(mp)),
+ _STACK_SIZE_PARAM_IS_A_RESERVATION, 0)
+ if thandle == 0 {
+ print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", getlasterror(), ")\n")
+ throw("runtime.newosproc")
+ }
+}
+
+// Used by the C library build mode. On Linux this function would allocate a
+// stack, but that's not necessary for Windows. No stack guards are present
+// and the GC has not been initialized, so write barriers will fail.
+//go:nowritebarrierc
+//go:nosplit
+func newosproc0(mp *m, stk unsafe.Pointer) {
+ newosproc(mp, stk)
+}
+
+// Called to initialize a new m (including the bootstrap m).
+// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
+func mpreinit(mp *m) {
+}
+
+//go:nosplit
+func msigsave(mp *m) {
+}
+
+//go:nosplit
+func msigrestore(sigmask sigset) {
+}
+
+//go:nosplit
+func sigblock() {
+}
+
+// Called to initialize a new m (including the bootstrap m).
+// Called on the new thread, cannot allocate memory.
+func minit() {
+ var thandle uintptr
+ stdcall7(_DuplicateHandle, currentProcess, currentThread, currentProcess, uintptr(unsafe.Pointer(&thandle)), 0, 0, _DUPLICATE_SAME_ACCESS)
+ atomic.Storeuintptr(&getg().m.thread, thandle)
+}
+
+// Called from dropm to undo the effect of an minit.
+//go:nosplit
+func unminit() {
+ tp := &getg().m.thread
+ stdcall1(_CloseHandle, *tp)
+ *tp = 0
+}
+
+// Described in http://www.dcl.hpi.uni-potsdam.de/research/WRK/2007/08/getting-os-information-the-kuser_shared_data-structure/
+type _KSYSTEM_TIME struct {
+ LowPart uint32
+ High1Time int32
+ High2Time int32
+}
+
+const (
+ _INTERRUPT_TIME = 0x7ffe0008
+ _SYSTEM_TIME = 0x7ffe0014
+)
+
+//go:nosplit
+func systime(addr uintptr) int64 {
+ timeaddr := (*_KSYSTEM_TIME)(unsafe.Pointer(addr))
+
+ var t _KSYSTEM_TIME
+ for i := 1; i < 10000; i++ {
+ // these fields must be read in that order (see URL above)
+ t.High1Time = timeaddr.High1Time
+ t.LowPart = timeaddr.LowPart
+ t.High2Time = timeaddr.High2Time
+ if t.High1Time == t.High2Time {
+ return int64(t.High1Time)<<32 | int64(t.LowPart)
+ }
+ if (i % 100) == 0 {
+ osyield()
+ }
+ }
+ systemstack(func() {
+ throw("interrupt/system time is changing too fast")
+ })
+ return 0
+}
+
+//go:nosplit
+func unixnano() int64 {
+ return (systime(_SYSTEM_TIME) - 116444736000000000) * 100
+}
+
+//go:nosplit
+func nanotime() int64 {
+ return systime(_INTERRUPT_TIME) * 100
+}
+
+// Calling stdcall on os stack.
+// May run during STW, so write barriers are not allowed.
+//go:nowritebarrier
+//go:nosplit
+func stdcall(fn stdFunction) uintptr {
+ gp := getg()
+ mp := gp.m
+ mp.libcall.fn = uintptr(unsafe.Pointer(fn))
+
+ if mp.profilehz != 0 {
+ // leave pc/sp for cpu profiler
+ mp.libcallg.set(gp)
+ mp.libcallpc = getcallerpc(unsafe.Pointer(&fn))
+ // sp must be the last, because once async cpu profiler finds
+ // all three values to be non-zero, it will use them
+ mp.libcallsp = getcallersp(unsafe.Pointer(&fn))
+ }
+ asmcgocall(asmstdcallAddr, unsafe.Pointer(&mp.libcall))
+ mp.libcallsp = 0
+ return mp.libcall.r1
+}
+
+//go:nosplit
+func stdcall0(fn stdFunction) uintptr {
+ mp := getg().m
+ mp.libcall.n = 0
+ mp.libcall.args = uintptr(noescape(unsafe.Pointer(&fn))) // it's unused but must be non-nil, otherwise crashes
+ return stdcall(fn)
+}
+
+//go:nosplit
+func stdcall1(fn stdFunction, a0 uintptr) uintptr {
+ mp := getg().m
+ mp.libcall.n = 1
+ mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0)))
+ return stdcall(fn)
+}
+
+//go:nosplit
+func stdcall2(fn stdFunction, a0, a1 uintptr) uintptr {
+ mp := getg().m
+ mp.libcall.n = 2
+ mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0)))
+ return stdcall(fn)
+}
+
+//go:nosplit
+func stdcall3(fn stdFunction, a0, a1, a2 uintptr) uintptr {
+ mp := getg().m
+ mp.libcall.n = 3
+ mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0)))
+ return stdcall(fn)
+}
+
+//go:nosplit
+func stdcall4(fn stdFunction, a0, a1, a2, a3 uintptr) uintptr {
+ mp := getg().m
+ mp.libcall.n = 4
+ mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0)))
+ return stdcall(fn)
+}
+
+//go:nosplit
+func stdcall5(fn stdFunction, a0, a1, a2, a3, a4 uintptr) uintptr {
+ mp := getg().m
+ mp.libcall.n = 5
+ mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0)))
+ return stdcall(fn)
+}
+
+//go:nosplit
+func stdcall6(fn stdFunction, a0, a1, a2, a3, a4, a5 uintptr) uintptr {
+ mp := getg().m
+ mp.libcall.n = 6
+ mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0)))
+ return stdcall(fn)
+}
+
+//go:nosplit
+func stdcall7(fn stdFunction, a0, a1, a2, a3, a4, a5, a6 uintptr) uintptr {
+ mp := getg().m
+ mp.libcall.n = 7
+ mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0)))
+ return stdcall(fn)
+}
+
+// in sys_windows_386.s and sys_windows_amd64.s
+func onosstack(fn unsafe.Pointer, arg uint32)
+func usleep2(usec uint32)
+func switchtothread()
+
+var usleep2Addr unsafe.Pointer
+var switchtothreadAddr unsafe.Pointer
+
+//go:nosplit
+func osyield() {
+ onosstack(switchtothreadAddr, 0)
+}
+
+//go:nosplit
+func usleep(us uint32) {
+ // Have 1us units; want 100ns units.
+ onosstack(usleep2Addr, 10*us)
+}
+
+func ctrlhandler1(_type uint32) uint32 {
+ var s uint32
+
+ switch _type {
+ case _CTRL_C_EVENT, _CTRL_BREAK_EVENT:
+ s = _SIGINT
+ default:
+ return 0
+ }
+
+ if sigsend(s) {
+ return 1
+ }
+ exit(2) // SIGINT, SIGTERM, etc
+ return 0
+}
+
+// in sys_windows_386.s and sys_windows_amd64.s
+func profileloop()
+
+var profiletimer uintptr
+
+func profilem(mp *m) {
+ var r *context
+ rbuf := make([]byte, unsafe.Sizeof(*r)+15)
+
+ tls := &mp.tls[0]
+ gp := *((**g)(unsafe.Pointer(tls)))
+
+ // align Context to 16 bytes
+ r = (*context)(unsafe.Pointer((uintptr(unsafe.Pointer(&rbuf[15]))) &^ 15))
+ r.contextflags = _CONTEXT_CONTROL
+ stdcall2(_GetThreadContext, mp.thread, uintptr(unsafe.Pointer(r)))
+ sigprof(r.ip(), r.sp(), 0, gp, mp)
+}
+
+func profileloop1(param uintptr) uint32 {
+ stdcall2(_SetThreadPriority, currentThread, _THREAD_PRIORITY_HIGHEST)
+
+ for {
+ stdcall2(_WaitForSingleObject, profiletimer, _INFINITE)
+ first := (*m)(atomic.Loadp(unsafe.Pointer(&allm)))
+ for mp := first; mp != nil; mp = mp.alllink {
+ thread := atomic.Loaduintptr(&mp.thread)
+ // Do not profile threads blocked on Notes,
+ // this includes idle worker threads,
+ // idle timer thread, idle heap scavenger, etc.
+ if thread == 0 || mp.profilehz == 0 || mp.blocked {
+ continue
+ }
+ stdcall1(_SuspendThread, thread)
+ if mp.profilehz != 0 && !mp.blocked {
+ profilem(mp)
+ }
+ stdcall1(_ResumeThread, thread)
+ }
+ }
+}
+
+var cpuprofilerlock mutex
+
+func resetcpuprofiler(hz int32) {
+ lock(&cpuprofilerlock)
+ if profiletimer == 0 {
+ timer := stdcall3(_CreateWaitableTimerA, 0, 0, 0)
+ atomic.Storeuintptr(&profiletimer, timer)
+ thread := stdcall6(_CreateThread, 0, 0, funcPC(profileloop), 0, 0, 0)
+ stdcall2(_SetThreadPriority, thread, _THREAD_PRIORITY_HIGHEST)
+ stdcall1(_CloseHandle, thread)
+ }
+ unlock(&cpuprofilerlock)
+
+ ms := int32(0)
+ due := ^int64(^uint64(1 << 63))
+ if hz > 0 {
+ ms = 1000 / hz
+ if ms == 0 {
+ ms = 1
+ }
+ due = int64(ms) * -10000
+ }
+ stdcall6(_SetWaitableTimer, profiletimer, uintptr(unsafe.Pointer(&due)), uintptr(ms), 0, 0, 0)
+ atomic.Store((*uint32)(unsafe.Pointer(&getg().m.profilehz)), uint32(hz))
+}
+
+func memlimit() uintptr {
+ return 0
+}
diff --git a/src/runtime/panic.go b/src/runtime/panic.go
index 59fbc83369..382a20e4e7 100644
--- a/src/runtime/panic.go
+++ b/src/runtime/panic.go
@@ -205,7 +205,7 @@ func newdefer(siz int32) *_defer {
if d == nil {
// Allocate new defer+args.
total := roundupsize(totaldefersize(uintptr(siz)))
- d = (*_defer)(mallocgc(total, deferType, 0))
+ d = (*_defer)(mallocgc(total, deferType, true))
}
d.siz = siz
gp := mp.curg
diff --git a/src/runtime/pprof/mprof_test.go b/src/runtime/pprof/mprof_test.go
index d15102c703..0fff9d46d9 100644
--- a/src/runtime/pprof/mprof_test.go
+++ b/src/runtime/pprof/mprof_test.go
@@ -82,7 +82,7 @@ func TestMemoryProfiler(t *testing.T) {
# 0x[0-9,a-f]+ runtime/pprof_test\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test.go:61
`, (1<<10)*memoryProfilerRun, (1<<20)*memoryProfilerRun),
- fmt.Sprintf(`0: 0 \[%v: %v\] @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
+ fmt.Sprintf(`0: 0 \[%v: %v\] @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
# 0x[0-9,a-f]+ runtime/pprof_test\.allocateTransient2M\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test.go:27
# 0x[0-9,a-f]+ runtime/pprof_test\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test.go:62
`, memoryProfilerRun, (2<<20)*memoryProfilerRun),
diff --git a/src/runtime/pprof/pprof_test.go b/src/runtime/pprof/pprof_test.go
index fa0af59b37..8b2f3d5291 100644
--- a/src/runtime/pprof/pprof_test.go
+++ b/src/runtime/pprof/pprof_test.go
@@ -530,15 +530,20 @@ func blockChanClose() {
}
func blockSelectRecvAsync() {
+ const numTries = 3
c := make(chan bool, 1)
c2 := make(chan bool, 1)
go func() {
- time.Sleep(blockDelay)
- c <- true
+ for i := 0; i < numTries; i++ {
+ time.Sleep(blockDelay)
+ c <- true
+ }
}()
- select {
- case <-c:
- case <-c2:
+ for i := 0; i < numTries; i++ {
+ select {
+ case <-c:
+ case <-c2:
+ }
}
}
@@ -585,6 +590,9 @@ func func3(c chan int) { <-c }
func func4(c chan int) { <-c }
func TestGoroutineCounts(t *testing.T) {
+ if runtime.GOOS == "openbsd" {
+ testenv.SkipFlaky(t, 15156)
+ }
c := make(chan int)
for i := 0; i < 100; i++ {
if i%10 == 0 {
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index 5145c84aea..ee732e3cf7 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -381,7 +381,7 @@ func badmcall2(fn func(*g)) {
}
func badreflectcall() {
- panic("runtime: arg size to reflect.call more than 1GB")
+ panic(plainError("arg size to reflect.call more than 1GB"))
}
func lockedOSThread() bool {
@@ -402,6 +402,16 @@ func allgadd(gp *g) {
lock(&allglock)
allgs = append(allgs, gp)
allglen = uintptr(len(allgs))
+
+ // Grow GC rescan list if necessary.
+ if len(allgs) > cap(work.rescan.list) {
+ lock(&work.rescan.lock)
+ l := work.rescan.list
+ // Let append do the heavy lifting, but keep the
+ // length the same.
+ work.rescan.list = append(l[:cap(l)], 0)[:len(l)]
+ unlock(&work.rescan.lock)
+ }
unlock(&allglock)
}
@@ -435,9 +445,10 @@ func schedinit() {
tracebackinit()
moduledataverify()
stackinit()
- itabsinit()
mallocinit()
mcommoninit(_g_.m)
+ typelinksinit()
+ itabsinit()
msigsave(_g_.m)
initSigmask = _g_.m.sigmask
@@ -449,6 +460,9 @@ func schedinit() {
sched.lastpoll = uint64(nanotime())
procs := int(ncpu)
+ if procs > _MaxGomaxprocs {
+ procs = _MaxGomaxprocs
+ }
if n := atoi(gogetenv("GOMAXPROCS")); n > 0 {
if n > _MaxGomaxprocs {
n = _MaxGomaxprocs
@@ -639,17 +653,17 @@ func readgstatus(gp *g) uint32 {
return atomic.Load(&gp.atomicstatus)
}
-// Ownership of gscanvalid:
+// Ownership of gcscanvalid:
//
// If gp is running (meaning status == _Grunning or _Grunning|_Gscan),
-// then gp owns gp.gscanvalid, and other goroutines must not modify it.
+// then gp owns gp.gcscanvalid, and other goroutines must not modify it.
//
// Otherwise, a second goroutine can lock the scan state by setting _Gscan
-// in the status bit and then modify gscanvalid, and then unlock the scan state.
+// in the status bit and then modify gcscanvalid, and then unlock the scan state.
//
// Note that the first condition implies an exception to the second:
// if a second goroutine changes gp's status to _Grunning|_Gscan,
-// that second goroutine still does not have the right to modify gscanvalid.
+// that second goroutine still does not have the right to modify gcscanvalid.
// The Gscanstatuses are acting like locks and this releases them.
// If it proves to be a performance hit we should be able to make these
@@ -677,9 +691,6 @@ func casfrom_Gscanstatus(gp *g, oldval, newval uint32) {
dumpgstatus(gp)
throw("casfrom_Gscanstatus: gp->status is not in scan state")
}
- if newval == _Grunning {
- gp.gcscanvalid = false
- }
}
// This will return false if the gp is not in the expected status and the cas fails.
@@ -753,8 +764,9 @@ func casgstatus(gp *g, oldval, newval uint32) {
nextYield = nanotime() + yieldDelay/2
}
}
- if newval == _Grunning {
- gp.gcscanvalid = false
+ if newval == _Grunning && gp.gcscanvalid {
+ // Run queueRescan on the system stack so it has more space.
+ systemstack(func() { queueRescan(gp) })
}
}
@@ -1404,6 +1416,8 @@ func newextram() {
gp.syscallpc = gp.sched.pc
gp.syscallsp = gp.sched.sp
gp.stktopsp = gp.sched.sp
+ gp.gcscanvalid = true // fresh G, so no dequeueRescan necessary
+ gp.gcRescan = -1
// malg returns status as Gidle, change to Gsyscall before adding to allg
// where GC will see it.
casgstatus(gp, _Gidle, _Gsyscall)
@@ -1792,23 +1806,7 @@ func execute(gp *g, inheritTime bool) {
// GoSysExit has to happen when we have a P, but before GoStart.
// So we emit it here.
if gp.syscallsp != 0 && gp.sysblocktraced {
- // Since gp.sysblocktraced is true, we must emit an event.
- // There is a race between the code that initializes sysexitseq
- // and sysexitticks (in exitsyscall, which runs without a P,
- // and therefore is not stopped with the rest of the world)
- // and the code that initializes a new trace.
- // The recorded sysexitseq and sysexitticks must therefore
- // be treated as "best effort". If they are valid for this trace,
- // then great, use them for greater accuracy.
- // But if they're not valid for this trace, assume that the
- // trace was started after the actual syscall exit (but before
- // we actually managed to start the goroutine, aka right now),
- // and assign a fresh time stamp to keep the log consistent.
- seq, ts := gp.sysexitseq, gp.sysexitticks
- if seq == 0 || int64(seq)-int64(trace.seqStart) < 0 {
- seq, ts = tracestamp()
- }
- traceGoSysExit(seq, ts)
+ traceGoSysExit(gp.sysexitticks)
}
traceGoStart()
}
@@ -2225,6 +2223,10 @@ func goexit0(gp *g) {
gp.waitreason = ""
gp.param = nil
+ // Note that gp's stack scan is now "valid" because it has no
+ // stack. We could dequeueRescan, but that takes a lock and
+ // isn't really necessary.
+ gp.gcscanvalid = true
dropg()
if _g_.m.locked&^_LockExternal != 0 {
@@ -2477,7 +2479,6 @@ func exitsyscall(dummy int32) {
}
_g_.sysexitticks = 0
- _g_.sysexitseq = 0
if trace.enabled {
// Wait till traceGoSysBlock event is emitted.
// This ensures consistency of the trace (the goroutine is started after it is blocked).
@@ -2488,7 +2489,7 @@ func exitsyscall(dummy int32) {
// Tracing code can invoke write barriers that cannot run without a P.
// So instead we remember the syscall exit time and emit the event
// in execute when we have a P.
- _g_.sysexitseq, _g_.sysexitticks = tracestamp()
+ _g_.sysexitticks = cputicks()
}
_g_.m.locks--
@@ -2536,7 +2537,7 @@ func exitsyscallfast() bool {
// Denote blocking of the new syscall.
traceGoSysBlock(_g_.m.p.ptr())
// Denote completion of the current syscall.
- traceGoSysExit(tracestamp())
+ traceGoSysExit(0)
})
}
_g_.m.p.ptr().syscalltick++
@@ -2560,7 +2561,7 @@ func exitsyscallfast() bool {
osyield()
}
}
- traceGoSysExit(tracestamp())
+ traceGoSysExit(0)
}
})
if ok {
@@ -2716,6 +2717,7 @@ func newproc1(fn *funcval, argp *uint8, narg int32, nret int32, callerpc uintptr
if newg == nil {
newg = malg(_StackMin)
casgstatus(newg, _Gidle, _Gdead)
+ newg.gcRescan = -1
allgadd(newg) // publishes with a g->status of Gdead so GC scanner doesn't look at uninitialized stack.
}
if newg.stack.hi == 0 {
@@ -2749,6 +2751,17 @@ func newproc1(fn *funcval, argp *uint8, narg int32, nret int32, callerpc uintptr
if isSystemGoroutine(newg) {
atomic.Xadd(&sched.ngsys, +1)
}
+ // The stack is dirty from the argument frame, so queue it for
+ // scanning. Do this before setting it to runnable so we still
+ // own the G. If we're recycling a G, it may already be on the
+ // rescan list.
+ if newg.gcRescan == -1 {
+ queueRescan(newg)
+ } else {
+ // The recycled G is already on the rescan list. Just
+ // mark the stack dirty.
+ newg.gcscanvalid = false
+ }
casgstatus(newg, _Gdead, _Grunnable)
if _p_.goidcache == _p_.goidcacheend {
@@ -2811,8 +2824,13 @@ func gfput(_p_ *p, gp *g) {
_p_.gfreecnt--
gp = _p_.gfree
_p_.gfree = gp.schedlink.ptr()
- gp.schedlink.set(sched.gfree)
- sched.gfree = gp
+ if gp.stack.lo == 0 {
+ gp.schedlink.set(sched.gfreeNoStack)
+ sched.gfreeNoStack = gp
+ } else {
+ gp.schedlink.set(sched.gfreeStack)
+ sched.gfreeStack = gp
+ }
sched.ngfree++
}
unlock(&sched.gflock)
@@ -2824,12 +2842,20 @@ func gfput(_p_ *p, gp *g) {
func gfget(_p_ *p) *g {
retry:
gp := _p_.gfree
- if gp == nil && sched.gfree != nil {
+ if gp == nil && (sched.gfreeStack != nil || sched.gfreeNoStack != nil) {
lock(&sched.gflock)
- for _p_.gfreecnt < 32 && sched.gfree != nil {
+ for _p_.gfreecnt < 32 {
+ if sched.gfreeStack != nil {
+ // Prefer Gs with stacks.
+ gp = sched.gfreeStack
+ sched.gfreeStack = gp.schedlink.ptr()
+ } else if sched.gfreeNoStack != nil {
+ gp = sched.gfreeNoStack
+ sched.gfreeNoStack = gp.schedlink.ptr()
+ } else {
+ break
+ }
_p_.gfreecnt++
- gp = sched.gfree
- sched.gfree = gp.schedlink.ptr()
sched.ngfree--
gp.schedlink.set(_p_.gfree)
_p_.gfree = gp
@@ -2866,8 +2892,13 @@ func gfpurge(_p_ *p) {
_p_.gfreecnt--
gp := _p_.gfree
_p_.gfree = gp.schedlink.ptr()
- gp.schedlink.set(sched.gfree)
- sched.gfree = gp
+ if gp.stack.lo == 0 {
+ gp.schedlink.set(sched.gfreeNoStack)
+ sched.gfreeNoStack = gp
+ } else {
+ gp.schedlink.set(sched.gfreeStack)
+ sched.gfreeStack = gp
+ }
sched.ngfree++
}
unlock(&sched.gflock)
diff --git a/src/runtime/race/testdata/io_test.go b/src/runtime/race/testdata/io_test.go
index 1b3ee3822b..30a121bee4 100644
--- a/src/runtime/race/testdata/io_test.go
+++ b/src/runtime/race/testdata/io_test.go
@@ -7,9 +7,11 @@ package race_test
import (
"fmt"
"io/ioutil"
+ "net"
"net/http"
"os"
"path/filepath"
+ "sync"
"testing"
"time"
)
@@ -41,29 +43,34 @@ func TestNoRaceIOFile(t *testing.T) {
_ = x
}
+var (
+ regHandler sync.Once
+ handlerData int
+)
+
func TestNoRaceIOHttp(t *testing.T) {
- x := 0
- go func() {
+ regHandler.Do(func() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
- x = 41
+ handlerData++
fmt.Fprintf(w, "test")
- x = 42
+ handlerData++
})
- err := http.ListenAndServe("127.0.0.1:23651", nil)
- if err != nil {
- t.Fatalf("http.ListenAndServe: %v", err)
- }
- }()
- time.Sleep(1e7)
- x = 1
- _, err := http.Get("http://127.0.0.1:23651")
+ })
+ ln, err := net.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("net.Listen: %v", err)
+ }
+ defer ln.Close()
+ go http.Serve(ln, nil)
+ handlerData++
+ _, err = http.Get("http://" + ln.Addr().String())
if err != nil {
t.Fatalf("http.Get: %v", err)
}
- x = 2
- _, err = http.Get("http://127.0.0.1:23651")
+ handlerData++
+ _, err = http.Get("http://" + ln.Addr().String())
if err != nil {
t.Fatalf("http.Get: %v", err)
}
- x = 3
+ handlerData++
}
diff --git a/src/runtime/rdebug.go b/src/runtime/rdebug.go
index d966734813..1b213f1934 100644
--- a/src/runtime/rdebug.go
+++ b/src/runtime/rdebug.go
@@ -15,9 +15,8 @@ func setMaxStack(in int) (out int) {
//go:linkname setPanicOnFault runtime/debug.setPanicOnFault
func setPanicOnFault(new bool) (old bool) {
- mp := acquirem()
- old = mp.curg.paniconfault
- mp.curg.paniconfault = new
- releasem(mp)
+ _g_ := getg()
+ old = _g_.paniconfault
+ _g_.paniconfault = new
return old
}
diff --git a/src/runtime/rt0_linux_s390x.s b/src/runtime/rt0_linux_s390x.s
new file mode 100644
index 0000000000..aedd6c7ef2
--- /dev/null
+++ b/src/runtime/rt0_linux_s390x.s
@@ -0,0 +1,20 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+TEXT _rt0_s390x_linux(SB),NOSPLIT|NOFRAME,$0
+ // In a statically linked binary, the stack contains argc,
+ // argv as argc string pointers followed by a NULL, envv as a
+ // sequence of string pointers followed by a NULL, and auxv.
+ // There is no TLS base pointer.
+ //
+ // TODO: Support dynamic linking entry point
+ MOVD 0(R15), R2 // argc
+ ADD $8, R15, R3 // argv
+ BR main(SB)
+
+TEXT main(SB),NOSPLIT|NOFRAME,$0
+ MOVD $runtime·rt0_go(SB), R11
+ BR R11
diff --git a/src/runtime/runtime-gdb_test.go b/src/runtime/runtime-gdb_test.go
index 110d99064f..4f82646dbb 100644
--- a/src/runtime/runtime-gdb_test.go
+++ b/src/runtime/runtime-gdb_test.go
@@ -3,6 +3,7 @@ package runtime_test
import (
"bytes"
"fmt"
+ "internal/testenv"
"io/ioutil"
"os"
"os/exec"
@@ -13,19 +14,22 @@ import (
"testing"
)
-func checkGdbPython(t *testing.T) {
- cmd := exec.Command("gdb", "-nx", "-q", "--batch", "-iex", "python import sys; print('go gdb python support')")
- out, err := cmd.CombinedOutput()
-
- if err != nil {
- t.Skipf("skipping due to issue running gdb: %v", err)
+func checkGdbEnvironment(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ if runtime.GOOS == "darwin" {
+ t.Skip("gdb does not work on darwin")
}
- if string(out) != "go gdb python support\n" {
- t.Skipf("skipping due to lack of python gdb support: %s", out)
+ if final := os.Getenv("GOROOT_FINAL"); final != "" && runtime.GOROOT() != final {
+ t.Skip("gdb test can fail with GOROOT_FINAL pending")
}
+}
+func checkGdbVersion(t *testing.T) {
// Issue 11214 reports various failures with older versions of gdb.
- out, err = exec.Command("gdb", "--version").CombinedOutput()
+ out, err := exec.Command("gdb", "--version").CombinedOutput()
+ if err != nil {
+ t.Skipf("skipping: error executing gdb: %v", err)
+ }
re := regexp.MustCompile(`([0-9]+)\.([0-9]+)`)
matches := re.FindSubmatch(out)
if len(matches) < 3 {
@@ -42,6 +46,18 @@ func checkGdbPython(t *testing.T) {
t.Logf("gdb version %d.%d", major, minor)
}
+func checkGdbPython(t *testing.T) {
+ cmd := exec.Command("gdb", "-nx", "-q", "--batch", "-iex", "python import sys; print('go gdb python support')")
+ out, err := cmd.CombinedOutput()
+
+ if err != nil {
+ t.Skipf("skipping due to issue running gdb: %v", err)
+ }
+ if string(out) != "go gdb python support\n" {
+ t.Skipf("skipping due to lack of python gdb support: %s", out)
+ }
+}
+
const helloSource = `
package main
import "fmt"
@@ -57,13 +73,8 @@ func main() {
`
func TestGdbPython(t *testing.T) {
- if runtime.GOOS == "darwin" {
- t.Skip("gdb does not work on darwin")
- }
- if final := os.Getenv("GOROOT_FINAL"); final != "" && runtime.GOROOT() != final {
- t.Skip("gdb test can fail with GOROOT_FINAL pending")
- }
-
+ checkGdbEnvironment(t)
+ checkGdbVersion(t)
checkGdbPython(t)
dir, err := ioutil.TempDir("", "go-build")
@@ -104,7 +115,7 @@ func TestGdbPython(t *testing.T) {
// stack frames on RISC architectures.
canBackTrace := false
switch runtime.GOARCH {
- case "amd64", "386", "ppc64", "ppc64le", "arm", "arm64", "mips64", "mips64le":
+ case "amd64", "386", "ppc64", "ppc64le", "arm", "arm64", "mips64", "mips64le", "s390x":
canBackTrace = true
args = append(args,
"-ex", "echo BEGIN goroutine 2 bt\n",
@@ -162,3 +173,82 @@ func TestGdbPython(t *testing.T) {
t.Logf("gdb cannot backtrace for GOARCH=%s, skipped goroutine backtrace test", runtime.GOARCH)
}
}
+
+const backtraceSource = `
+package main
+
+//go:noinline
+func aaa() bool { return bbb() }
+
+//go:noinline
+func bbb() bool { return ccc() }
+
+//go:noinline
+func ccc() bool { return ddd() }
+
+//go:noinline
+func ddd() bool { return f() }
+
+//go:noinline
+func eee() bool { return true }
+
+var f = eee
+
+func main() {
+ _ = aaa()
+}
+`
+
+// TestGdbBacktrace tests that gdb can unwind the stack correctly
+// using only the DWARF debug info.
+func TestGdbBacktrace(t *testing.T) {
+ checkGdbEnvironment(t)
+ checkGdbVersion(t)
+
+ dir, err := ioutil.TempDir("", "go-build")
+ if err != nil {
+ t.Fatalf("failed to create temp directory: %v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ // Build the source code.
+ src := filepath.Join(dir, "main.go")
+ err = ioutil.WriteFile(src, []byte(backtraceSource), 0644)
+ if err != nil {
+ t.Fatalf("failed to create file: %v", err)
+ }
+ cmd := exec.Command("go", "build", "-o", "a.exe")
+ cmd.Dir = dir
+ out, err := testEnv(cmd).CombinedOutput()
+ if err != nil {
+ t.Fatalf("building source %v\n%s", err, out)
+ }
+
+ // Execute gdb commands.
+ args := []string{"-nx", "-batch",
+ "-ex", "break main.eee",
+ "-ex", "run",
+ "-ex", "backtrace",
+ "-ex", "continue",
+ filepath.Join(dir, "a.exe"),
+ }
+ got, _ := exec.Command("gdb", args...).CombinedOutput()
+
+ // Check that the backtrace matches the source code.
+ bt := []string{
+ "eee",
+ "ddd",
+ "ccc",
+ "bbb",
+ "aaa",
+ "main",
+ }
+ for i, name := range bt {
+ s := fmt.Sprintf("#%v.*main\\.%v", i, name)
+ re := regexp.MustCompile(s)
+ if found := re.Find(got) != nil; !found {
+ t.Errorf("could not find '%v' in backtrace", s)
+ t.Fatalf("gdb output:\n%v", string(got))
+ }
+ }
+}
diff --git a/src/runtime/runtime1.go b/src/runtime/runtime1.go
index 95bebac593..9089383904 100644
--- a/src/runtime/runtime1.go
+++ b/src/runtime/runtime1.go
@@ -477,10 +477,51 @@ func gomcache() *mcache {
}
//go:linkname reflect_typelinks reflect.typelinks
-func reflect_typelinks() [][]*_type {
- ret := [][]*_type{firstmoduledata.typelinks}
+func reflect_typelinks() ([]unsafe.Pointer, [][]int32) {
+ sections := []unsafe.Pointer{unsafe.Pointer(firstmoduledata.types)}
+ ret := [][]int32{firstmoduledata.typelinks}
for datap := firstmoduledata.next; datap != nil; datap = datap.next {
+ sections = append(sections, unsafe.Pointer(datap.types))
ret = append(ret, datap.typelinks)
}
- return ret
+ return sections, ret
+}
+
+// reflect_resolveNameOff resolves a name offset from a base pointer.
+//go:linkname reflect_resolveNameOff reflect.resolveNameOff
+func reflect_resolveNameOff(ptrInModule unsafe.Pointer, off int32) unsafe.Pointer {
+ return unsafe.Pointer(resolveNameOff(ptrInModule, nameOff(off)).bytes)
+}
+
+// reflect_resolveTypeOff resolves an *rtype offset from a base type.
+//go:linkname reflect_resolveTypeOff reflect.resolveTypeOff
+func reflect_resolveTypeOff(rtype unsafe.Pointer, off int32) unsafe.Pointer {
+ return unsafe.Pointer((*_type)(rtype).typeOff(typeOff(off)))
+}
+
+// reflect_resolveTextOff resolves an function pointer offset from a base type.
+//go:linkname reflect_resolveTextOff reflect.resolveTextOff
+func reflect_resolveTextOff(rtype unsafe.Pointer, off int32) unsafe.Pointer {
+ return (*_type)(rtype).textOff(textOff(off))
+
+}
+
+// reflect_addReflectOff adds a pointer to the reflection offset lookup map.
+//go:linkname reflect_addReflectOff reflect.addReflectOff
+func reflect_addReflectOff(ptr unsafe.Pointer) int32 {
+ lock(&reflectOffs.lock)
+ if reflectOffs.m == nil {
+ reflectOffs.m = make(map[int32]unsafe.Pointer)
+ reflectOffs.minv = make(map[unsafe.Pointer]int32)
+ reflectOffs.next = -1
+ }
+ id, found := reflectOffs.minv[ptr]
+ if !found {
+ id = reflectOffs.next
+ reflectOffs.next-- // use negative offsets as IDs to aid debugging
+ reflectOffs.m[id] = ptr
+ reflectOffs.minv[ptr] = id
+ }
+ unlock(&reflectOffs.lock)
+ return id
}
diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go
index e0137f7e97..d35b897c3e 100644
--- a/src/runtime/runtime2.go
+++ b/src/runtime/runtime2.go
@@ -10,9 +10,7 @@ import (
"unsafe"
)
-/*
- * defined constants
- */
+// defined constants
const (
// G status
//
@@ -99,6 +97,10 @@ const (
_Pdead
)
+// Mutual exclusion locks. In the uncontended case,
+// as fast as spin locks (just a few user-level instructions),
+// but on the contention path they sleep in the kernel.
+// A zeroed Mutex is unlocked (no need to initialize each lock).
type mutex struct {
// Futex-based impl treats it as uint32 key,
// while sema-based impl as M* waitm.
@@ -106,6 +108,26 @@ type mutex struct {
key uintptr
}
+// sleep and wakeup on one-time events.
+// before any calls to notesleep or notewakeup,
+// must call noteclear to initialize the Note.
+// then, exactly one thread can call notesleep
+// and exactly one thread can call notewakeup (once).
+// once notewakeup has been called, the notesleep
+// will return. future notesleep will return immediately.
+// subsequent noteclear must be called only after
+// previous notesleep has returned, e.g. it's disallowed
+// to call noteclear straight after notewakeup.
+//
+// notetsleep is like notesleep but wakes up after
+// a given number of nanoseconds even if the event
+// has not yet happened. if a goroutine uses notetsleep to
+// wake up early, it must wait to call noteclear until it
+// can be sure that no other goroutine is calling
+// notewakeup.
+//
+// notesleep/notetsleep are generally called on g0,
+// notetsleepg is similar to notetsleep but is called on user g.
type note struct {
// Futex-based impl treats it as uint32 key,
// while sema-based impl as M* waitm.
@@ -310,16 +332,17 @@ type g struct {
waitsince int64 // approx time when the g become blocked
waitreason string // if status==Gwaiting
schedlink guintptr
- preempt bool // preemption signal, duplicates stackguard0 = stackpreempt
- paniconfault bool // panic (instead of crash) on unexpected fault address
- preemptscan bool // preempted g does scan for gc
- gcscandone bool // g has scanned stack; protected by _Gscan bit in status
- gcscanvalid bool // false at start of gc cycle, true if G has not run since last scan
- throwsplit bool // must not split stack
- raceignore int8 // ignore race detection events
- sysblocktraced bool // StartTrace has emitted EvGoInSyscall about this goroutine
- sysexitticks int64 // cputicks when syscall has returned (for tracing)
- sysexitseq uint64 // trace seq when syscall has returned (for tracing)
+ preempt bool // preemption signal, duplicates stackguard0 = stackpreempt
+ paniconfault bool // panic (instead of crash) on unexpected fault address
+ preemptscan bool // preempted g does scan for gc
+ gcscandone bool // g has scanned stack; protected by _Gscan bit in status
+ gcscanvalid bool // false at start of gc cycle, true if G has not run since last scan; transition from true to false by calling queueRescan and false to true by calling dequeueRescan
+ throwsplit bool // must not split stack
+ raceignore int8 // ignore race detection events
+ sysblocktraced bool // StartTrace has emitted EvGoInSyscall about this goroutine
+ sysexitticks int64 // cputicks when syscall has returned (for tracing)
+ traceseq uint64 // trace event sequencer
+ tracelastp puintptr // last P emitted an event for this goroutine
lockedm *m
sig uint32
writebuf []byte
@@ -331,7 +354,14 @@ type g struct {
racectx uintptr
waiting *sudog // sudog structures this g is waiting on (that have a valid elem ptr); in lock order
- // Per-G gcController state
+ // Per-G GC state
+
+ // gcRescan is this G's index in work.rescan.list. If this is
+ // -1, this G is not on the rescan list.
+ //
+ // If gcphase != _GCoff and this G is visible to the garbage
+ // collector, writes to this are protected by work.rescan.lock.
+ gcRescan int32
// gcAssistBytes is this G's GC assist credit in terms of
// bytes allocated. If this is positive, then the G has credit
@@ -397,8 +427,8 @@ type m struct {
waittraceskip int
startingtrace bool
syscalltick uint32
- //#ifdef GOOS_windows
- thread uintptr // thread handle
+ thread uintptr // thread handle
+
// these are here because they are too large to be on the stack
// of low-level NOSPLIT functions.
libcall libcall
@@ -406,7 +436,7 @@ type m struct {
libcallsp uintptr
libcallg guintptr
syscall libcall // stores syscall parameters on windows
- //#endif
+
mOS
}
@@ -500,9 +530,10 @@ type schedt struct {
runqsize int32
// Global cache of dead G's.
- gflock mutex
- gfree *g
- ngfree int32
+ gflock mutex
+ gfreeStack *g
+ gfreeNoStack *g
+ ngfree int32
// Central cache of sudog structs.
sudoglock mutex
@@ -530,10 +561,10 @@ type schedt struct {
totaltime int64 // ∫gomaxprocs dt up to procresizetime
}
-// The m->locked word holds two pieces of state counting active calls to LockOSThread/lockOSThread.
+// The m.locked word holds two pieces of state counting active calls to LockOSThread/lockOSThread.
// The low bit (LockExternal) is a boolean reporting whether any LockOSThread call is active.
// External locks are not recursive; a second lock is silently ignored.
-// The upper bits of m->locked record the nesting depth of calls to lockOSThread
+// The upper bits of m.locked record the nesting depth of calls to lockOSThread
// (counting up by LockInternal), popped by unlockOSThread (counting down by LockInternal).
// Internal locks can be recursive. For instance, a lock for cgo can occur while the main
// goroutine is holding the lock during the initialization phase.
@@ -603,13 +634,6 @@ type forcegcstate struct {
idle uint32
}
-/*
- * known to compiler
- */
-const (
- _Structrnd = sys.RegSize
-)
-
// startup_random_data holds random bytes initialized at startup. These come from
// the ELF AT_RANDOM auxiliary vector (vdso_linux_amd64.go or os_linux_386.go).
var startupRandomData []byte
@@ -635,9 +659,7 @@ func extendRandom(r []byte, n int) {
}
}
-/*
- * deferred subroutine calls
- */
+// deferred subroutine calls
type _defer struct {
siz int32
started bool
@@ -648,9 +670,7 @@ type _defer struct {
link *_defer
}
-/*
- * panics
- */
+// panics
type _panic struct {
argp unsafe.Pointer // pointer to arguments of deferred call run during panic; cannot move - known to liblink
arg interface{} // argument to panic
@@ -659,10 +679,7 @@ type _panic struct {
aborted bool // the panic was aborted
}
-/*
- * stack traces
- */
-
+// stack traces
type stkframe struct {
fn *_func // function being run
pc uintptr // program counter within fn
@@ -682,10 +699,8 @@ const (
_TraceJumpStack // if traceback is on a systemstack, resume trace at g that called into it
)
-const (
- // The maximum number of frames we print for a traceback
- _TracebackMaxFrames = 100
-)
+// The maximum number of frames we print for a traceback
+const _TracebackMaxFrames = 100
var (
emptystring string
@@ -716,46 +731,3 @@ var (
islibrary bool // -buildmode=c-shared
isarchive bool // -buildmode=c-archive
)
-
-/*
- * mutual exclusion locks. in the uncontended case,
- * as fast as spin locks (just a few user-level instructions),
- * but on the contention path they sleep in the kernel.
- * a zeroed Mutex is unlocked (no need to initialize each lock).
- */
-
-/*
- * sleep and wakeup on one-time events.
- * before any calls to notesleep or notewakeup,
- * must call noteclear to initialize the Note.
- * then, exactly one thread can call notesleep
- * and exactly one thread can call notewakeup (once).
- * once notewakeup has been called, the notesleep
- * will return. future notesleep will return immediately.
- * subsequent noteclear must be called only after
- * previous notesleep has returned, e.g. it's disallowed
- * to call noteclear straight after notewakeup.
- *
- * notetsleep is like notesleep but wakes up after
- * a given number of nanoseconds even if the event
- * has not yet happened. if a goroutine uses notetsleep to
- * wake up early, it must wait to call noteclear until it
- * can be sure that no other goroutine is calling
- * notewakeup.
- *
- * notesleep/notetsleep are generally called on g0,
- * notetsleepg is similar to notetsleep but is called on user g.
- */
-// bool runtime·notetsleep(Note*, int64); // false - timeout
-// bool runtime·notetsleepg(Note*, int64); // false - timeout
-
-/*
- * Lock-free stack.
- * Initialize uint64 head to 0, compare with 0 to test for emptiness.
- * The stack does not keep pointers to nodes,
- * so they can be garbage collected if there are no other pointers to nodes.
- */
-
-// for mmap, we only pass the lower 32 bits of file offset to the
-// assembly routine; the higher bits (if required), should be provided
-// by the assembly routine as 0.
diff --git a/src/runtime/select.go b/src/runtime/select.go
index c80c833b15..433048fb79 100644
--- a/src/runtime/select.go
+++ b/src/runtime/select.go
@@ -594,7 +594,7 @@ retc:
sclose:
// send on closed channel
selunlock(scases, lockorder)
- panic("send on closed channel")
+ panic(plainError("send on closed channel"))
}
func (c *hchan) sortkey() uintptr {
@@ -626,7 +626,7 @@ const (
func reflect_rselect(cases []runtimeSelect) (chosen int, recvOK bool) {
// flagNoScan is safe here, because all objects are also referenced from cases.
size := selectsize(uintptr(len(cases)))
- sel := (*hselect)(mallocgc(size, nil, flagNoScan))
+ sel := (*hselect)(mallocgc(size, nil, true))
newselect(sel, int64(size), int32(len(cases)))
r := new(bool)
for i := range cases {
diff --git a/src/runtime/signal_dragonfly.go b/src/runtime/signal_dragonfly.go
index f507a07233..8e9ce17c86 100644
--- a/src/runtime/signal_dragonfly.go
+++ b/src/runtime/signal_dragonfly.go
@@ -14,14 +14,14 @@ var sigtable = [...]sigTabT{
/* 1 */ {_SigNotify + _SigKill, "SIGHUP: terminal line hangup"},
/* 2 */ {_SigNotify + _SigKill, "SIGINT: interrupt"},
/* 3 */ {_SigNotify + _SigThrow, "SIGQUIT: quit"},
- /* 4 */ {_SigThrow, "SIGILL: illegal instruction"},
- /* 5 */ {_SigThrow, "SIGTRAP: trace trap"},
+ /* 4 */ {_SigThrow + _SigUnblock, "SIGILL: illegal instruction"},
+ /* 5 */ {_SigThrow + _SigUnblock, "SIGTRAP: trace trap"},
/* 6 */ {_SigNotify + _SigThrow, "SIGABRT: abort"},
/* 7 */ {_SigThrow, "SIGEMT: emulate instruction executed"},
- /* 8 */ {_SigPanic, "SIGFPE: floating-point exception"},
+ /* 8 */ {_SigPanic + _SigUnblock, "SIGFPE: floating-point exception"},
/* 9 */ {0, "SIGKILL: kill"},
- /* 10 */ {_SigPanic, "SIGBUS: bus error"},
- /* 11 */ {_SigPanic, "SIGSEGV: segmentation violation"},
+ /* 10 */ {_SigPanic + _SigUnblock, "SIGBUS: bus error"},
+ /* 11 */ {_SigPanic + _SigUnblock, "SIGSEGV: segmentation violation"},
/* 12 */ {_SigThrow, "SIGSYS: bad system call"},
/* 13 */ {_SigNotify, "SIGPIPE: write to broken pipe"},
/* 14 */ {_SigNotify, "SIGALRM: alarm clock"},
@@ -30,14 +30,14 @@ var sigtable = [...]sigTabT{
/* 17 */ {0, "SIGSTOP: stop"},
/* 18 */ {_SigNotify + _SigDefault, "SIGTSTP: keyboard stop"},
/* 19 */ {_SigNotify + _SigDefault, "SIGCONT: continue after stop"},
- /* 20 */ {_SigNotify, "SIGCHLD: child status has changed"},
+ /* 20 */ {_SigNotify + _SigUnblock, "SIGCHLD: child status has changed"},
/* 21 */ {_SigNotify + _SigDefault, "SIGTTIN: background read from tty"},
/* 22 */ {_SigNotify + _SigDefault, "SIGTTOU: background write to tty"},
/* 23 */ {_SigNotify, "SIGIO: i/o now possible"},
/* 24 */ {_SigNotify, "SIGXCPU: cpu limit exceeded"},
/* 25 */ {_SigNotify, "SIGXFSZ: file size limit exceeded"},
/* 26 */ {_SigNotify, "SIGVTALRM: virtual alarm clock"},
- /* 27 */ {_SigNotify, "SIGPROF: profiling alarm clock"},
+ /* 27 */ {_SigNotify + _SigUnblock, "SIGPROF: profiling alarm clock"},
/* 28 */ {_SigNotify, "SIGWINCH: window size change"},
/* 29 */ {_SigNotify, "SIGINFO: status request from keyboard"},
/* 30 */ {_SigNotify, "SIGUSR1: user-defined signal 1"},
diff --git a/src/runtime/signal_freebsd.go b/src/runtime/signal_freebsd.go
index cd2068a62c..c8b09e92d9 100644
--- a/src/runtime/signal_freebsd.go
+++ b/src/runtime/signal_freebsd.go
@@ -16,14 +16,14 @@ var sigtable = [...]sigTabT{
/* 1 */ {_SigNotify + _SigKill, "SIGHUP: terminal line hangup"},
/* 2 */ {_SigNotify + _SigKill, "SIGINT: interrupt"},
/* 3 */ {_SigNotify + _SigThrow, "SIGQUIT: quit"},
- /* 4 */ {_SigThrow, "SIGILL: illegal instruction"},
- /* 5 */ {_SigThrow, "SIGTRAP: trace trap"},
+ /* 4 */ {_SigThrow + _SigUnblock, "SIGILL: illegal instruction"},
+ /* 5 */ {_SigThrow + _SigUnblock, "SIGTRAP: trace trap"},
/* 6 */ {_SigNotify + _SigThrow, "SIGABRT: abort"},
/* 7 */ {_SigThrow, "SIGEMT: emulate instruction executed"},
- /* 8 */ {_SigPanic, "SIGFPE: floating-point exception"},
+ /* 8 */ {_SigPanic + _SigUnblock, "SIGFPE: floating-point exception"},
/* 9 */ {0, "SIGKILL: kill"},
- /* 10 */ {_SigPanic, "SIGBUS: bus error"},
- /* 11 */ {_SigPanic, "SIGSEGV: segmentation violation"},
+ /* 10 */ {_SigPanic + _SigUnblock, "SIGBUS: bus error"},
+ /* 11 */ {_SigPanic + _SigUnblock, "SIGSEGV: segmentation violation"},
/* 12 */ {_SigNotify, "SIGSYS: bad system call"},
/* 13 */ {_SigNotify, "SIGPIPE: write to broken pipe"},
/* 14 */ {_SigNotify, "SIGALRM: alarm clock"},
@@ -32,14 +32,14 @@ var sigtable = [...]sigTabT{
/* 17 */ {0, "SIGSTOP: stop"},
/* 18 */ {_SigNotify + _SigDefault, "SIGTSTP: keyboard stop"},
/* 19 */ {_SigNotify + _SigDefault, "SIGCONT: continue after stop"},
- /* 20 */ {_SigNotify, "SIGCHLD: child status has changed"},
+ /* 20 */ {_SigNotify + _SigUnblock, "SIGCHLD: child status has changed"},
/* 21 */ {_SigNotify + _SigDefault, "SIGTTIN: background read from tty"},
/* 22 */ {_SigNotify + _SigDefault, "SIGTTOU: background write to tty"},
/* 23 */ {_SigNotify, "SIGIO: i/o now possible"},
/* 24 */ {_SigNotify, "SIGXCPU: cpu limit exceeded"},
/* 25 */ {_SigNotify, "SIGXFSZ: file size limit exceeded"},
/* 26 */ {_SigNotify, "SIGVTALRM: virtual alarm clock"},
- /* 27 */ {_SigNotify, "SIGPROF: profiling alarm clock"},
+ /* 27 */ {_SigNotify + _SigUnblock, "SIGPROF: profiling alarm clock"},
/* 28 */ {_SigNotify, "SIGWINCH: window size change"},
/* 29 */ {_SigNotify, "SIGINFO: status request from keyboard"},
/* 30 */ {_SigNotify, "SIGUSR1: user-defined signal 1"},
diff --git a/src/runtime/signal_linux_s390x.go b/src/runtime/signal_linux_s390x.go
new file mode 100644
index 0000000000..155d3a326f
--- /dev/null
+++ b/src/runtime/signal_linux_s390x.go
@@ -0,0 +1,208 @@
+// Copyright 2016 The Go 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 runtime
+
+import (
+ "runtime/internal/sys"
+ "unsafe"
+)
+
+type sigctxt struct {
+ info *siginfo
+ ctxt unsafe.Pointer
+}
+
+func (c *sigctxt) regs() *sigcontext {
+ return (*sigcontext)(unsafe.Pointer(&(*ucontext)(c.ctxt).uc_mcontext))
+}
+func (c *sigctxt) r0() uint64 { return c.regs().gregs[0] }
+func (c *sigctxt) r1() uint64 { return c.regs().gregs[1] }
+func (c *sigctxt) r2() uint64 { return c.regs().gregs[2] }
+func (c *sigctxt) r3() uint64 { return c.regs().gregs[3] }
+func (c *sigctxt) r4() uint64 { return c.regs().gregs[4] }
+func (c *sigctxt) r5() uint64 { return c.regs().gregs[5] }
+func (c *sigctxt) r6() uint64 { return c.regs().gregs[6] }
+func (c *sigctxt) r7() uint64 { return c.regs().gregs[7] }
+func (c *sigctxt) r8() uint64 { return c.regs().gregs[8] }
+func (c *sigctxt) r9() uint64 { return c.regs().gregs[9] }
+func (c *sigctxt) r10() uint64 { return c.regs().gregs[10] }
+func (c *sigctxt) r11() uint64 { return c.regs().gregs[11] }
+func (c *sigctxt) r12() uint64 { return c.regs().gregs[12] }
+func (c *sigctxt) r13() uint64 { return c.regs().gregs[13] }
+func (c *sigctxt) r14() uint64 { return c.regs().gregs[14] }
+func (c *sigctxt) r15() uint64 { return c.regs().gregs[15] }
+func (c *sigctxt) link() uint64 { return c.regs().gregs[14] }
+func (c *sigctxt) sp() uint64 { return c.regs().gregs[15] }
+func (c *sigctxt) pc() uint64 { return c.regs().psw_addr }
+func (c *sigctxt) sigcode() uint32 { return uint32(c.info.si_code) }
+func (c *sigctxt) sigaddr() uint64 { return c.info.si_addr }
+
+func (c *sigctxt) set_r0(x uint64) { c.regs().gregs[0] = x }
+func (c *sigctxt) set_r13(x uint64) { c.regs().gregs[13] = x }
+func (c *sigctxt) set_link(x uint64) { c.regs().gregs[14] = x }
+func (c *sigctxt) set_sp(x uint64) { c.regs().gregs[15] = x }
+func (c *sigctxt) set_pc(x uint64) { c.regs().psw_addr = x }
+func (c *sigctxt) set_sigcode(x uint32) { c.info.si_code = int32(x) }
+func (c *sigctxt) set_sigaddr(x uint64) {
+ *(*uintptr)(add(unsafe.Pointer(c.info), 2*sys.PtrSize)) = uintptr(x)
+}
+
+func dumpregs(c *sigctxt) {
+ print("r0 ", hex(c.r0()), "\t")
+ print("r1 ", hex(c.r1()), "\n")
+ print("r2 ", hex(c.r2()), "\t")
+ print("r3 ", hex(c.r3()), "\n")
+ print("r4 ", hex(c.r4()), "\t")
+ print("r5 ", hex(c.r5()), "\n")
+ print("r6 ", hex(c.r6()), "\t")
+ print("r7 ", hex(c.r7()), "\n")
+ print("r8 ", hex(c.r8()), "\t")
+ print("r9 ", hex(c.r9()), "\n")
+ print("r10 ", hex(c.r10()), "\t")
+ print("r11 ", hex(c.r11()), "\n")
+ print("r12 ", hex(c.r12()), "\t")
+ print("r13 ", hex(c.r13()), "\n")
+ print("r14 ", hex(c.r14()), "\t")
+ print("r15 ", hex(c.r15()), "\n")
+ print("pc ", hex(c.pc()), "\t")
+ print("link ", hex(c.link()), "\n")
+}
+
+var crashing int32
+
+// May run during STW, so write barriers are not allowed.
+//
+//go:nowritebarrierrec
+func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
+ _g_ := getg()
+ c := &sigctxt{info, ctxt}
+
+ if sig == _SIGPROF {
+ sigprof(uintptr(c.pc()), uintptr(c.sp()), uintptr(c.link()), gp, _g_.m)
+ return
+ }
+ flags := int32(_SigThrow)
+ if sig < uint32(len(sigtable)) {
+ flags = sigtable[sig].flags
+ }
+ if c.sigcode() != _SI_USER && flags&_SigPanic != 0 {
+ // Make it look like a call to the signal func.
+ // Have to pass arguments out of band since
+ // augmenting the stack frame would break
+ // the unwinding code.
+ gp.sig = sig
+ gp.sigcode0 = uintptr(c.sigcode())
+ gp.sigcode1 = uintptr(c.sigaddr())
+ gp.sigpc = uintptr(c.pc())
+
+ // We arrange link, and pc to pretend the panicking
+ // function calls sigpanic directly.
+ // Always save LINK to stack so that panics in leaf
+ // functions are correctly handled. This smashes
+ // the stack frame but we're not going back there
+ // anyway.
+ sp := c.sp() - sys.MinFrameSize
+ c.set_sp(sp)
+ *(*uint64)(unsafe.Pointer(uintptr(sp))) = c.link()
+
+ pc := uintptr(gp.sigpc)
+
+ // If we don't recognize the PC as code
+ // but we do recognize the link register as code,
+ // then assume this was a call to non-code and treat like
+ // pc == 0, to make unwinding show the context.
+ if pc != 0 && findfunc(pc) == nil && findfunc(uintptr(c.link())) != nil {
+ pc = 0
+ }
+
+ // Don't bother saving PC if it's zero, which is
+ // probably a call to a nil func: the old link register
+ // is more useful in the stack trace.
+ if pc != 0 {
+ c.set_link(uint64(pc))
+ }
+
+ // In case we are panicking from external C code
+ c.set_r0(0)
+ c.set_r13(uint64(uintptr(unsafe.Pointer(gp))))
+ c.set_pc(uint64(funcPC(sigpanic)))
+ return
+ }
+
+ if c.sigcode() == _SI_USER || flags&_SigNotify != 0 {
+ if sigsend(sig) {
+ return
+ }
+ }
+
+ if c.sigcode() == _SI_USER && signal_ignored(sig) {
+ return
+ }
+
+ if flags&_SigKill != 0 {
+ dieFromSignal(int32(sig))
+ }
+
+ if flags&_SigThrow == 0 {
+ return
+ }
+
+ _g_.m.throwing = 1
+ _g_.m.caughtsig.set(gp)
+
+ if crashing == 0 {
+ startpanic()
+ }
+
+ if sig < uint32(len(sigtable)) {
+ print(sigtable[sig].name, "\n")
+ } else {
+ print("Signal ", sig, "\n")
+ }
+
+ print("PC=", hex(c.pc()), " m=", _g_.m.id, "\n")
+ if _g_.m.lockedg != nil && _g_.m.ncgo > 0 && gp == _g_.m.g0 {
+ print("signal arrived during cgo execution\n")
+ gp = _g_.m.lockedg
+ }
+ print("\n")
+
+ level, _, docrash := gotraceback()
+ if level > 0 {
+ goroutineheader(gp)
+ tracebacktrap(uintptr(c.pc()), uintptr(c.sp()), uintptr(c.link()), gp)
+ if crashing > 0 && gp != _g_.m.curg && _g_.m.curg != nil && readgstatus(_g_.m.curg)&^_Gscan == _Grunning {
+ // tracebackothers on original m skipped this one; trace it now.
+ goroutineheader(_g_.m.curg)
+ traceback(^uintptr(0), ^uintptr(0), 0, gp)
+ } else if crashing == 0 {
+ tracebackothers(gp)
+ print("\n")
+ }
+ dumpregs(c)
+ }
+
+ if docrash {
+ crashing++
+ if crashing < sched.mcount {
+ // There are other m's that need to dump their stacks.
+ // Relay SIGQUIT to the next m by sending it to the current process.
+ // All m's that have already received SIGQUIT have signal masks blocking
+ // receipt of any signals, so the SIGQUIT will go to an m that hasn't seen it yet.
+ // When the last m receives the SIGQUIT, it will fall through to the call to
+ // crash below. Just in case the relaying gets botched, each m involved in
+ // the relay sleeps for 5 seconds and then does the crash/exit itself.
+ // In expected operation, the last m has received the SIGQUIT and run
+ // crash/exit and the process is gone, all long before any of the
+ // 5-second sleeps have finished.
+ print("\n-----\n\n")
+ raiseproc(_SIGQUIT)
+ usleep(5 * 1000 * 1000)
+ }
+ crash()
+ }
+
+ exit(2)
+}
diff --git a/src/runtime/signal_openbsd.go b/src/runtime/signal_openbsd.go
index 3c50190da4..9275279860 100644
--- a/src/runtime/signal_openbsd.go
+++ b/src/runtime/signal_openbsd.go
@@ -16,14 +16,14 @@ var sigtable = [...]sigTabT{
/* 1 */ {_SigNotify + _SigKill, "SIGHUP: terminal line hangup"},
/* 2 */ {_SigNotify + _SigKill, "SIGINT: interrupt"},
/* 3 */ {_SigNotify + _SigThrow, "SIGQUIT: quit"},
- /* 4 */ {_SigThrow, "SIGILL: illegal instruction"},
- /* 5 */ {_SigThrow, "SIGTRAP: trace trap"},
+ /* 4 */ {_SigThrow + _SigUnblock, "SIGILL: illegal instruction"},
+ /* 5 */ {_SigThrow + _SigUnblock, "SIGTRAP: trace trap"},
/* 6 */ {_SigNotify + _SigThrow, "SIGABRT: abort"},
/* 7 */ {_SigThrow, "SIGEMT: emulate instruction executed"},
- /* 8 */ {_SigPanic, "SIGFPE: floating-point exception"},
+ /* 8 */ {_SigPanic + _SigUnblock, "SIGFPE: floating-point exception"},
/* 9 */ {0, "SIGKILL: kill"},
- /* 10 */ {_SigPanic, "SIGBUS: bus error"},
- /* 11 */ {_SigPanic, "SIGSEGV: segmentation violation"},
+ /* 10 */ {_SigPanic + _SigUnblock, "SIGBUS: bus error"},
+ /* 11 */ {_SigPanic + _SigUnblock, "SIGSEGV: segmentation violation"},
/* 12 */ {_SigThrow, "SIGSYS: bad system call"},
/* 13 */ {_SigNotify, "SIGPIPE: write to broken pipe"},
/* 14 */ {_SigNotify, "SIGALRM: alarm clock"},
@@ -32,14 +32,14 @@ var sigtable = [...]sigTabT{
/* 17 */ {0, "SIGSTOP: stop"},
/* 18 */ {_SigNotify + _SigDefault, "SIGTSTP: keyboard stop"},
/* 19 */ {_SigNotify + _SigDefault, "SIGCONT: continue after stop"},
- /* 20 */ {_SigNotify, "SIGCHLD: child status has changed"},
+ /* 20 */ {_SigNotify + _SigUnblock, "SIGCHLD: child status has changed"},
/* 21 */ {_SigNotify + _SigDefault, "SIGTTIN: background read from tty"},
/* 22 */ {_SigNotify + _SigDefault, "SIGTTOU: background write to tty"},
/* 23 */ {_SigNotify, "SIGIO: i/o now possible"},
/* 24 */ {_SigNotify, "SIGXCPU: cpu limit exceeded"},
/* 25 */ {_SigNotify, "SIGXFSZ: file size limit exceeded"},
/* 26 */ {_SigNotify, "SIGVTALRM: virtual alarm clock"},
- /* 27 */ {_SigNotify, "SIGPROF: profiling alarm clock"},
+ /* 27 */ {_SigNotify + _SigUnblock, "SIGPROF: profiling alarm clock"},
/* 28 */ {_SigNotify, "SIGWINCH: window size change"},
/* 29 */ {_SigNotify, "SIGINFO: status request from keyboard"},
/* 30 */ {_SigNotify, "SIGUSR1: user-defined signal 1"},
diff --git a/src/runtime/sigtab_linux_generic.go b/src/runtime/sigtab_linux_generic.go
index 32c40c4768..e97497f18c 100644
--- a/src/runtime/sigtab_linux_generic.go
+++ b/src/runtime/sigtab_linux_generic.go
@@ -45,7 +45,7 @@ var sigtable = [...]sigTabT{
/* 28 */ {_SigNotify, "SIGWINCH: window size change"},
/* 29 */ {_SigNotify, "SIGIO: i/o now possible"},
/* 30 */ {_SigNotify, "SIGPWR: power failure restart"},
- /* 31 */ {_SigNotify, "SIGSYS: bad system call"},
+ /* 31 */ {_SigThrow + _SigUnblock, "SIGSYS: bad system call"},
/* 32 */ {_SigSetStack + _SigUnblock, "signal 32"}, /* SIGCANCEL; see issue 6997 */
/* 33 */ {_SigSetStack + _SigUnblock, "signal 33"}, /* SIGSETXID; see issues 3871, 9400, 12498 */
/* 34 */ {_SigNotify, "signal 34"},
diff --git a/src/runtime/sigtab_linux_mips64x.go b/src/runtime/sigtab_linux_mips64x.go
index dbd50f7b1f..f7d81811ba 100644
--- a/src/runtime/sigtab_linux_mips64x.go
+++ b/src/runtime/sigtab_linux_mips64x.go
@@ -25,7 +25,7 @@ var sigtable = [...]sigTabT{
/* 9 */ {0, "SIGKILL: kill"},
/* 10 */ {_SigPanic + _SigUnblock, "SIGBUS: bus error"},
/* 11 */ {_SigPanic + _SigUnblock, "SIGSEGV: segmentation violation"},
- /* 12 */ {_SigNotify, "SIGSYS: bad system call"},
+ /* 12 */ {_SigThrow + _SigUnblock, "SIGSYS: bad system call"},
/* 13 */ {_SigNotify, "SIGPIPE: write to broken pipe"},
/* 14 */ {_SigNotify, "SIGALRM: alarm clock"},
/* 15 */ {_SigNotify + _SigKill, "SIGTERM: termination"},
diff --git a/src/runtime/slice.go b/src/runtime/slice.go
index 0bc0299f72..e15e6c4dc6 100644
--- a/src/runtime/slice.go
+++ b/src/runtime/slice.go
@@ -14,39 +14,69 @@ type slice struct {
cap int
}
+// maxElems is a lookup table containing the maximum capacity for a slice.
+// The index is the size of the slice element.
+var maxElems = [...]uintptr{
+ ^uintptr(0),
+ _MaxMem / 1, _MaxMem / 2, _MaxMem / 3, _MaxMem / 4,
+ _MaxMem / 5, _MaxMem / 6, _MaxMem / 7, _MaxMem / 8,
+ _MaxMem / 9, _MaxMem / 10, _MaxMem / 11, _MaxMem / 12,
+ _MaxMem / 13, _MaxMem / 14, _MaxMem / 15, _MaxMem / 16,
+ _MaxMem / 17, _MaxMem / 18, _MaxMem / 19, _MaxMem / 20,
+ _MaxMem / 21, _MaxMem / 22, _MaxMem / 23, _MaxMem / 24,
+ _MaxMem / 25, _MaxMem / 26, _MaxMem / 27, _MaxMem / 28,
+ _MaxMem / 29, _MaxMem / 30, _MaxMem / 31, _MaxMem / 32,
+}
+
+// maxSliceCap returns the maximum capacity for a slice.
+func maxSliceCap(elemsize uintptr) uintptr {
+ if elemsize < uintptr(len(maxElems)) {
+ return maxElems[elemsize]
+ }
+ return _MaxMem / elemsize
+}
+
// TODO: take uintptrs instead of int64s?
-func makeslice(t *slicetype, len64, cap64 int64) slice {
- // NOTE: The len > MaxMem/elemsize check here is not strictly necessary,
+func makeslice(et *_type, len64, cap64 int64) slice {
+ // NOTE: The len > maxElements check here is not strictly necessary,
// but it produces a 'len out of range' error instead of a 'cap out of range' error
// when someone does make([]T, bignumber). 'cap out of range' is true too,
// but since the cap is only being supplied implicitly, saying len is clearer.
// See issue 4085.
+ maxElements := maxSliceCap(et.size)
len := int(len64)
- if len64 < 0 || int64(len) != len64 || t.elem.size > 0 && uintptr(len) > _MaxMem/t.elem.size {
+ if len64 < 0 || int64(len) != len64 || uintptr(len) > maxElements {
panic(errorString("makeslice: len out of range"))
}
+
cap := int(cap64)
- if cap < len || int64(cap) != cap64 || t.elem.size > 0 && uintptr(cap) > _MaxMem/t.elem.size {
+ if cap < len || int64(cap) != cap64 || uintptr(cap) > maxElements {
panic(errorString("makeslice: cap out of range"))
}
- p := newarray(t.elem, uintptr(cap))
+
+ p := mallocgc(et.size*uintptr(cap), et, true)
return slice{p, len, cap}
}
// growslice handles slice growth during append.
-// It is passed the slice type, the old slice, and the desired new minimum capacity,
+// It is passed the slice element type, the old slice, and the desired new minimum capacity,
// and it returns a new slice with at least that capacity, with the old data
// copied into it.
-func growslice(t *slicetype, old slice, cap int) slice {
+// The new slice's length is set to the old slice's length,
+// NOT to the new requested capacity.
+// This is for codegen convenience. The old slice's length is used immediately
+// to calculate where to write new values during an append.
+// TODO: When the old backend is gone, reconsider this decision.
+// The SSA backend might prefer the new length or to return only ptr/cap and save stack space.
+func growslice(et *_type, old slice, cap int) slice {
if raceenabled {
- callerpc := getcallerpc(unsafe.Pointer(&t))
- racereadrangepc(old.array, uintptr(old.len*int(t.elem.size)), callerpc, funcPC(growslice))
+ callerpc := getcallerpc(unsafe.Pointer(&et))
+ racereadrangepc(old.array, uintptr(old.len*int(et.size)), callerpc, funcPC(growslice))
}
if msanenabled {
- msanread(old.array, uintptr(old.len*int(t.elem.size)))
+ msanread(old.array, uintptr(old.len*int(et.size)))
}
- et := t.elem
if et.size == 0 {
if cap < old.cap {
panic(errorString("growslice: cap out of range"))
@@ -70,38 +100,35 @@ func growslice(t *slicetype, old slice, cap int) slice {
}
}
- var lenmem, capmem, maxcap uintptr
+ var lenmem, capmem uintptr
const ptrSize = unsafe.Sizeof((*byte)(nil))
switch et.size {
case 1:
lenmem = uintptr(old.len)
capmem = roundupsize(uintptr(newcap))
newcap = int(capmem)
- maxcap = _MaxMem
case ptrSize:
lenmem = uintptr(old.len) * ptrSize
capmem = roundupsize(uintptr(newcap) * ptrSize)
newcap = int(capmem / ptrSize)
- maxcap = _MaxMem / ptrSize
default:
lenmem = uintptr(old.len) * et.size
capmem = roundupsize(uintptr(newcap) * et.size)
newcap = int(capmem / et.size)
- maxcap = _MaxMem / et.size
}
- if cap < old.cap || uintptr(newcap) > maxcap {
+ if cap < old.cap || uintptr(newcap) > maxSliceCap(et.size) {
panic(errorString("growslice: cap out of range"))
}
var p unsafe.Pointer
if et.kind&kindNoPointers != 0 {
- p = rawmem(capmem)
+ p = mallocgc(capmem, nil, false)
memmove(p, old.array, lenmem)
memclr(add(p, lenmem), capmem-lenmem)
} else {
// Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan uninitialized memory.
- p = newarray(et, uintptr(newcap))
+ p = mallocgc(capmem, et, true)
if !writeBarrier.enabled {
memmove(p, old.array, lenmem)
} else {
diff --git a/src/runtime/softfloat_arm.go b/src/runtime/softfloat_arm.go
index b1f1a72925..648b2e1169 100644
--- a/src/runtime/softfloat_arm.go
+++ b/src/runtime/softfloat_arm.go
@@ -168,14 +168,15 @@ execute:
}
return 1
}
- if i == 0xe08bb00d {
- // add sp to r11.
- // might be part of a large stack offset address
+ if i&0xfffffff0 == 0xe08bb000 {
+ r := i & 0xf
+ // add r to r11.
+ // might be part of a large offset address calculation
// (or might not, but again no harm done).
- regs[11] += regs[13]
+ regs[11] += regs[r]
if fptrace > 0 {
- print("*** cpu R[11] += R[13] ", hex(regs[11]), "\n")
+ print("*** cpu R[11] += R[", r, "] ", hex(regs[11]), "\n")
}
return 1
}
diff --git a/src/runtime/stack.go b/src/runtime/stack.go
index 1ca737e920..ac4efc114b 100644
--- a/src/runtime/stack.go
+++ b/src/runtime/stack.go
@@ -634,8 +634,8 @@ func adjustframe(frame *stkframe, arg unsafe.Pointer) bool {
// Adjust local variables if stack frame has been allocated.
size := frame.varp - frame.sp
var minsize uintptr
- switch sys.TheChar {
- case '7':
+ switch sys.ArchFamily {
+ case sys.ARM64:
minsize = sys.SpAlign
default:
minsize = sys.MinFrameSize
@@ -662,7 +662,7 @@ func adjustframe(frame *stkframe, arg unsafe.Pointer) bool {
}
// Adjust saved base pointer if there is one.
- if sys.TheChar == '6' && frame.argp-frame.varp == 2*sys.RegSize {
+ if sys.ArchFamily == sys.AMD64 && frame.argp-frame.varp == 2*sys.RegSize {
if !framepointer_enabled {
print("runtime: found space for saved base pointer, but no framepointer experiment\n")
print("argp=", hex(frame.argp), " varp=", hex(frame.varp), "\n")
@@ -969,7 +969,7 @@ func newstack() {
throw("missing stack in newstack")
}
sp := gp.sched.sp
- if sys.TheChar == '6' || sys.TheChar == '8' {
+ if sys.ArchFamily == sys.AMD64 || sys.ArchFamily == sys.I386 {
// The call to morestack cost a word.
sp -= sys.PtrSize
}
@@ -1016,6 +1016,7 @@ func newstack() {
gp.preemptscan = false
gp.preempt = false
casfrom_Gscanstatus(gp, _Gscanwaiting, _Gwaiting)
+ // This clears gcscanvalid.
casgstatus(gp, _Gwaiting, _Grunning)
gp.stackguard0 = gp.stack.lo + _StackGuard
gogo(&gp.sched) // never return
diff --git a/src/runtime/string.go b/src/runtime/string.go
index 2d20e0a9c3..ef28ba9828 100644
--- a/src/runtime/string.go
+++ b/src/runtime/string.go
@@ -139,7 +139,8 @@ func slicebytetostringtmp(b []byte) string {
func stringtoslicebyte(buf *tmpBuf, s string) []byte {
var b []byte
if buf != nil && len(s) <= len(buf) {
- b = buf[:len(s):len(s)]
+ *buf = tmpBuf{}
+ b = buf[:len(s)]
} else {
b = rawbyteslice(len(s))
}
@@ -171,7 +172,8 @@ func stringtoslicerune(buf *[tmpStringBufSize]rune, s string) []rune {
}
var a []rune
if buf != nil && n <= len(buf) {
- a = buf[:n:n]
+ *buf = [tmpStringBufSize]rune{}
+ a = buf[:n]
} else {
a = rawruneslice(n)
}
@@ -284,7 +286,7 @@ func stringiter2(s string, k int) (int, rune) {
// The storage is not zeroed. Callers should use
// b to set the string contents and then drop b.
func rawstring(size int) (s string, b []byte) {
- p := mallocgc(uintptr(size), nil, flagNoScan|flagNoZero)
+ p := mallocgc(uintptr(size), nil, false)
stringStructOf(&s).str = p
stringStructOf(&s).len = size
@@ -302,7 +304,7 @@ func rawstring(size int) (s string, b []byte) {
// rawbyteslice allocates a new byte slice. The byte slice is not zeroed.
func rawbyteslice(size int) (b []byte) {
cap := roundupsize(uintptr(size))
- p := mallocgc(cap, nil, flagNoScan|flagNoZero)
+ p := mallocgc(cap, nil, false)
if cap != uintptr(size) {
memclr(add(p, uintptr(size)), cap-uintptr(size))
}
@@ -317,7 +319,7 @@ func rawruneslice(size int) (b []rune) {
throw("out of memory")
}
mem := roundupsize(uintptr(size) * 4)
- p := mallocgc(mem, nil, flagNoScan|flagNoZero)
+ p := mallocgc(mem, nil, false)
if mem != uintptr(size)*4 {
memclr(add(p, uintptr(size)*4), mem-uintptr(size)*4)
}
diff --git a/src/runtime/string_test.go b/src/runtime/string_test.go
index ee9709e87d..0f1d82a481 100644
--- a/src/runtime/string_test.go
+++ b/src/runtime/string_test.go
@@ -238,17 +238,35 @@ func TestRangeStringCast(t *testing.T) {
}
}
+func isZeroed(b []byte) bool {
+ for _, x := range b {
+ if x != 0 {
+ return false
+ }
+ }
+ return true
+}
+
+func isZeroedR(r []rune) bool {
+ for _, x := range r {
+ if x != 0 {
+ return false
+ }
+ }
+ return true
+}
+
func TestString2Slice(t *testing.T) {
// Make sure we don't return slices that expose
// an unzeroed section of stack-allocated temp buf
// between len and cap. See issue 14232.
s := "foož"
b := ([]byte)(s)
- if cap(b) != 5 {
- t.Errorf("want cap of 5, got %d", cap(b))
+ if !isZeroed(b[len(b):cap(b)]) {
+ t.Errorf("extra bytes not zeroed")
}
r := ([]rune)(s)
- if cap(r) != 4 {
- t.Errorf("want cap of 4, got %d", cap(r))
+ if !isZeroedR(r[len(r):cap(r)]) {
+ t.Errorf("extra runes not zeroed")
}
}
diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go
index 158bdcea0d..2df390253a 100644
--- a/src/runtime/symtab.go
+++ b/src/runtime/symtab.go
@@ -127,8 +127,9 @@ type moduledata struct {
bss, ebss uintptr
noptrbss, enoptrbss uintptr
end, gcdata, gcbss uintptr
+ types, etypes uintptr
- typelinks []*_type
+ typelinks []int32 // offsets from types
itablinks []*itab
modulename string
@@ -136,6 +137,8 @@ type moduledata struct {
gcdatamask, gcbssmask bitvector
+ typemap map[typeOff]*_type // offset to *_rtype in previous module
+
next *moduledata
}
diff --git a/src/runtime/sys_linux_s390x.s b/src/runtime/sys_linux_s390x.s
new file mode 100644
index 0000000000..f43792bd51
--- /dev/null
+++ b/src/runtime/sys_linux_s390x.s
@@ -0,0 +1,440 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// System calls and other system stuff for Linux s390x; see
+// /usr/include/asm/unistd.h for the syscall number definitions.
+
+#include "go_asm.h"
+#include "go_tls.h"
+#include "textflag.h"
+
+#define SYS_exit 1
+#define SYS_read 3
+#define SYS_write 4
+#define SYS_open 5
+#define SYS_close 6
+#define SYS_getpid 20
+#define SYS_kill 37
+#define SYS_fcntl 55
+#define SYS_gettimeofday 78
+#define SYS_mmap 90
+#define SYS_munmap 91
+#define SYS_setitimer 104
+#define SYS_clone 120
+#define SYS_select 142
+#define SYS_sched_yield 158
+#define SYS_rt_sigreturn 173
+#define SYS_rt_sigaction 174
+#define SYS_rt_sigprocmask 175
+#define SYS_sigaltstack 186
+#define SYS_ugetrlimit 191
+#define SYS_madvise 219
+#define SYS_mincore 218
+#define SYS_gettid 236
+#define SYS_tkill 237
+#define SYS_futex 238
+#define SYS_sched_getaffinity 240
+#define SYS_exit_group 248
+#define SYS_epoll_create 249
+#define SYS_epoll_ctl 250
+#define SYS_epoll_wait 251
+#define SYS_clock_gettime 260
+#define SYS_epoll_create1 327
+
+TEXT runtime·exit(SB),NOSPLIT|NOFRAME,$0-4
+ MOVW code+0(FP), R2
+ MOVW $SYS_exit_group, R1
+ SYSCALL
+ RET
+
+TEXT runtime·exit1(SB),NOSPLIT|NOFRAME,$0-4
+ MOVW code+0(FP), R2
+ MOVW $SYS_exit, R1
+ SYSCALL
+ RET
+
+TEXT runtime·open(SB),NOSPLIT|NOFRAME,$0-20
+ MOVD name+0(FP), R2
+ MOVW mode+8(FP), R3
+ MOVW perm+12(FP), R4
+ MOVW $SYS_open, R1
+ SYSCALL
+ MOVD $-4095, R3
+ CMPUBLT R2, R3, 2(PC)
+ MOVW $-1, R2
+ MOVW R2, ret+16(FP)
+ RET
+
+TEXT runtime·closefd(SB),NOSPLIT|NOFRAME,$0-12
+ MOVW fd+0(FP), R2
+ MOVW $SYS_close, R1
+ SYSCALL
+ MOVD $-4095, R3
+ CMPUBLT R2, R3, 2(PC)
+ MOVW $-1, R2
+ MOVW R2, ret+8(FP)
+ RET
+
+TEXT runtime·write(SB),NOSPLIT|NOFRAME,$0-28
+ MOVD fd+0(FP), R2
+ MOVD p+8(FP), R3
+ MOVW n+16(FP), R4
+ MOVW $SYS_write, R1
+ SYSCALL
+ MOVD $-4095, R3
+ CMPUBLT R2, R3, 2(PC)
+ MOVW $-1, R2
+ MOVW R2, ret+24(FP)
+ RET
+
+TEXT runtime·read(SB),NOSPLIT|NOFRAME,$0-28
+ MOVW fd+0(FP), R2
+ MOVD p+8(FP), R3
+ MOVW n+16(FP), R4
+ MOVW $SYS_read, R1
+ SYSCALL
+ MOVD $-4095, R3
+ CMPUBLT R2, R3, 2(PC)
+ MOVW $-1, R2
+ MOVW R2, ret+24(FP)
+ RET
+
+TEXT runtime·getrlimit(SB),NOSPLIT|NOFRAME,$0-20
+ MOVW kind+0(FP), R2
+ MOVD limit+8(FP), R3
+ MOVW $SYS_ugetrlimit, R1
+ SYSCALL
+ MOVW R2, ret+16(FP)
+ RET
+
+TEXT runtime·usleep(SB),NOSPLIT,$16-4
+ MOVW usec+0(FP), R2
+ MOVD R2, R4
+ MOVW $1000000, R3
+ DIVD R3, R2
+ MOVD R2, 8(R15)
+ MULLD R2, R3
+ SUB R3, R4
+ MOVD R4, 16(R15)
+
+ // select(0, 0, 0, 0, &tv)
+ MOVW $0, R2
+ MOVW $0, R3
+ MOVW $0, R4
+ MOVW $0, R5
+ ADD $8, R15, R6
+ MOVW $SYS_select, R1
+ SYSCALL
+ RET
+
+TEXT runtime·gettid(SB),NOSPLIT,$0-4
+ MOVW $SYS_gettid, R1
+ SYSCALL
+ MOVW R2, ret+0(FP)
+ RET
+
+TEXT runtime·raise(SB),NOSPLIT|NOFRAME,$0
+ MOVW $SYS_gettid, R1
+ SYSCALL
+ MOVW R2, R2 // arg 1 tid
+ MOVW sig+0(FP), R3 // arg 2
+ MOVW $SYS_tkill, R1
+ SYSCALL
+ RET
+
+TEXT runtime·raiseproc(SB),NOSPLIT|NOFRAME,$0
+ MOVW $SYS_getpid, R1
+ SYSCALL
+ MOVW R2, R2 // arg 1 pid
+ MOVW sig+0(FP), R3 // arg 2
+ MOVW $SYS_kill, R1
+ SYSCALL
+ RET
+
+TEXT runtime·setitimer(SB),NOSPLIT|NOFRAME,$0-24
+ MOVW mode+0(FP), R2
+ MOVD new+8(FP), R3
+ MOVD old+16(FP), R4
+ MOVW $SYS_setitimer, R1
+ SYSCALL
+ RET
+
+TEXT runtime·mincore(SB),NOSPLIT|NOFRAME,$0-28
+ MOVD addr+0(FP), R2
+ MOVD n+8(FP), R3
+ MOVD dst+16(FP), R4
+ MOVW $SYS_mincore, R1
+ SYSCALL
+ MOVW R2, ret+24(FP)
+ RET
+
+// func now() (sec int64, nsec int32)
+TEXT time·now(SB),NOSPLIT,$16
+ MOVD $0(R15), R2
+ MOVD $0, R3
+ MOVW $SYS_gettimeofday, R1
+ SYSCALL
+ MOVD 0(R15), R2 // sec
+ MOVD 8(R15), R4 // usec
+ MOVD $1000, R3
+ MULLD R3, R4
+ MOVD R2, sec+0(FP)
+ MOVW R4, nsec+8(FP)
+ RET
+
+TEXT runtime·nanotime(SB),NOSPLIT,$16
+ MOVW $1, R2 // CLOCK_MONOTONIC
+ MOVD $0(R15), R3
+ MOVW $SYS_clock_gettime, R1
+ SYSCALL
+ MOVD 0(R15), R2 // sec
+ MOVD 8(R15), R4 // nsec
+ // sec is in R2, nsec in R4
+ // return nsec in R2
+ MOVD $1000000000, R3
+ MULLD R3, R2
+ ADD R4, R2
+ MOVD R2, ret+0(FP)
+ RET
+
+TEXT runtime·rtsigprocmask(SB),NOSPLIT|NOFRAME,$0-28
+ MOVW sig+0(FP), R2
+ MOVD new+8(FP), R3
+ MOVD old+16(FP), R4
+ MOVW size+24(FP), R5
+ MOVW $SYS_rt_sigprocmask, R1
+ SYSCALL
+ MOVD $-4095, R3
+ CMPUBLT R2, R3, 2(PC)
+ MOVD R0, 0(R0) // crash
+ RET
+
+TEXT runtime·rt_sigaction(SB),NOSPLIT|NOFRAME,$0-36
+ MOVD sig+0(FP), R2
+ MOVD new+8(FP), R3
+ MOVD old+16(FP), R4
+ MOVD size+24(FP), R5
+ MOVW $SYS_rt_sigaction, R1
+ SYSCALL
+ MOVW R2, ret+32(FP)
+ RET
+
+TEXT runtime·sigfwd(SB),NOSPLIT,$0-32
+ MOVW sig+8(FP), R2
+ MOVD info+16(FP), R3
+ MOVD ctx+24(FP), R4
+ MOVD fn+0(FP), R5
+ BL R5
+ RET
+
+TEXT runtime·sigtramp(SB),NOSPLIT,$64
+ // initialize essential registers (just in case)
+ XOR R0, R0
+
+ // this might be called in external code context,
+ // where g is not set.
+ MOVB runtime·iscgo(SB), R6
+ CMPBEQ R6, $0, 2(PC)
+ BL runtime·load_g(SB)
+
+ MOVW R2, 8(R15)
+ MOVD R3, 16(R15)
+ MOVD R4, 24(R15)
+ MOVD $runtime·sigtrampgo(SB), R5
+ BL R5
+ RET
+
+TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0
+ BR runtime·sigtramp(SB)
+
+// func mmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) unsafe.Pointer
+TEXT runtime·mmap(SB),NOSPLIT,$48-40
+ MOVD addr+0(FP), R2
+ MOVD n+8(FP), R3
+ MOVW prot+16(FP), R4
+ MOVW flags+20(FP), R5
+ MOVW fd+24(FP), R6
+ MOVWZ off+28(FP), R7
+
+ // s390x uses old_mmap, so the arguments need to be placed into
+ // a struct and a pointer to the struct passed to mmap.
+ MOVD R2, addr-48(SP)
+ MOVD R3, n-40(SP)
+ MOVD R4, prot-32(SP)
+ MOVD R5, flags-24(SP)
+ MOVD R6, fd-16(SP)
+ MOVD R7, off-8(SP)
+
+ MOVD $addr-48(SP), R2
+ MOVW $SYS_mmap, R1
+ SYSCALL
+ MOVD $-4095, R3
+ CMPUBLT R2, R3, 2(PC)
+ NEG R2
+ MOVD R2, ret+32(FP)
+ RET
+
+TEXT runtime·munmap(SB),NOSPLIT|NOFRAME,$0
+ MOVD addr+0(FP), R2
+ MOVD n+8(FP), R3
+ MOVW $SYS_munmap, R1
+ SYSCALL
+ MOVD $-4095, R3
+ CMPUBLT R2, R3, 2(PC)
+ MOVD R0, 0(R0) // crash
+ RET
+
+TEXT runtime·madvise(SB),NOSPLIT|NOFRAME,$0
+ MOVD addr+0(FP), R2
+ MOVD n+8(FP), R3
+ MOVW flags+16(FP), R4
+ MOVW $SYS_madvise, R1
+ SYSCALL
+ // ignore failure - maybe pages are locked
+ RET
+
+// int64 futex(int32 *uaddr, int32 op, int32 val,
+// struct timespec *timeout, int32 *uaddr2, int32 val2);
+TEXT runtime·futex(SB),NOSPLIT|NOFRAME,$0
+ MOVD addr+0(FP), R2
+ MOVW op+8(FP), R3
+ MOVW val+12(FP), R4
+ MOVD ts+16(FP), R5
+ MOVD addr2+24(FP), R6
+ MOVW val3+32(FP), R7
+ MOVW $SYS_futex, R1
+ SYSCALL
+ MOVW R2, ret+40(FP)
+ RET
+
+// int32 clone(int32 flags, void *stk, M *mp, G *gp, void (*fn)(void));
+TEXT runtime·clone(SB),NOSPLIT|NOFRAME,$0
+ MOVW flags+0(FP), R3
+ MOVD stk+8(FP), R2
+
+ // Copy mp, gp, fn off parent stack for use by child.
+ // Careful: Linux system call clobbers ???.
+ MOVD mm+16(FP), R7
+ MOVD gg+24(FP), R8
+ MOVD fn+32(FP), R9
+
+ MOVD R7, -8(R2)
+ MOVD R8, -16(R2)
+ MOVD R9, -24(R2)
+ MOVD $1234, R7
+ MOVD R7, -32(R2)
+
+ SYSCALL $SYS_clone
+
+ // In parent, return.
+ CMPBEQ R2, $0, 3(PC)
+ MOVW R2, ret+40(FP)
+ RET
+
+ // In child, on new stack.
+ // initialize essential registers
+ XOR R0, R0
+ MOVD -32(R15), R7
+ CMP R7, $1234
+ BEQ 2(PC)
+ MOVD R0, 0(R0)
+
+ // Initialize m->procid to Linux tid
+ SYSCALL $SYS_gettid
+
+ MOVD -24(R15), R9 // fn
+ MOVD -16(R15), R8 // g
+ MOVD -8(R15), R7 // m
+
+ CMPBEQ R7, $0, nog
+ CMP R8, $0
+ BEQ nog
+
+ MOVD R2, m_procid(R7)
+
+ // In child, set up new stack
+ MOVD R7, g_m(R8)
+ MOVD R8, g
+ //CALL runtime·stackcheck(SB)
+
+nog:
+ // Call fn
+ BL R9
+
+ // It shouldn't return. If it does, exit that thread.
+ MOVW $111, R2
+ MOVW $SYS_exit, R1
+ SYSCALL
+ BR -2(PC) // keep exiting
+
+TEXT runtime·sigaltstack(SB),NOSPLIT|NOFRAME,$0
+ MOVD new+0(FP), R2
+ MOVD old+8(FP), R3
+ MOVW $SYS_sigaltstack, R1
+ SYSCALL
+ MOVD $-4095, R3
+ CMPUBLT R2, R3, 2(PC)
+ MOVD R0, 0(R0) // crash
+ RET
+
+TEXT runtime·osyield(SB),NOSPLIT|NOFRAME,$0
+ MOVW $SYS_sched_yield, R1
+ SYSCALL
+ RET
+
+TEXT runtime·sched_getaffinity(SB),NOSPLIT|NOFRAME,$0
+ MOVD pid+0(FP), R2
+ MOVD len+8(FP), R3
+ MOVD buf+16(FP), R4
+ MOVW $SYS_sched_getaffinity, R1
+ SYSCALL
+ MOVW R2, ret+24(FP)
+ RET
+
+// int32 runtime·epollcreate(int32 size);
+TEXT runtime·epollcreate(SB),NOSPLIT|NOFRAME,$0
+ MOVW size+0(FP), R2
+ MOVW $SYS_epoll_create, R1
+ SYSCALL
+ MOVW R2, ret+8(FP)
+ RET
+
+// int32 runtime·epollcreate1(int32 flags);
+TEXT runtime·epollcreate1(SB),NOSPLIT|NOFRAME,$0
+ MOVW flags+0(FP), R2
+ MOVW $SYS_epoll_create1, R1
+ SYSCALL
+ MOVW R2, ret+8(FP)
+ RET
+
+// func epollctl(epfd, op, fd int32, ev *epollEvent) int
+TEXT runtime·epollctl(SB),NOSPLIT|NOFRAME,$0
+ MOVW epfd+0(FP), R2
+ MOVW op+4(FP), R3
+ MOVW fd+8(FP), R4
+ MOVD ev+16(FP), R5
+ MOVW $SYS_epoll_ctl, R1
+ SYSCALL
+ MOVW R2, ret+24(FP)
+ RET
+
+// int32 runtime·epollwait(int32 epfd, EpollEvent *ev, int32 nev, int32 timeout);
+TEXT runtime·epollwait(SB),NOSPLIT|NOFRAME,$0
+ MOVW epfd+0(FP), R2
+ MOVD ev+8(FP), R3
+ MOVW nev+16(FP), R4
+ MOVW timeout+20(FP), R5
+ MOVW $SYS_epoll_wait, R1
+ SYSCALL
+ MOVW R2, ret+24(FP)
+ RET
+
+// void runtime·closeonexec(int32 fd);
+TEXT runtime·closeonexec(SB),NOSPLIT|NOFRAME,$0
+ MOVW fd+0(FP), R2 // fd
+ MOVD $2, R3 // F_SETFD
+ MOVD $1, R4 // FD_CLOEXEC
+ MOVW $SYS_fcntl, R1
+ SYSCALL
+ RET
diff --git a/src/runtime/sys_s390x.go b/src/runtime/sys_s390x.go
new file mode 100644
index 0000000000..2aa81e75c0
--- /dev/null
+++ b/src/runtime/sys_s390x.go
@@ -0,0 +1,45 @@
+// Copyright 2016 The Go 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 runtime
+
+import "unsafe"
+
+// adjust Gobuf as if it executed a call to fn with context ctxt
+// and then did an immediate Gosave.
+func gostartcall(buf *gobuf, fn, ctxt unsafe.Pointer) {
+ if buf.lr != 0 {
+ throw("invalid use of gostartcall")
+ }
+ buf.lr = buf.pc
+ buf.pc = uintptr(fn)
+ buf.ctxt = ctxt
+}
+
+// Called to rewind context saved during morestack back to beginning of function.
+// To help us, the linker emits a jmp back to the beginning right after the
+// call to morestack. We just have to decode and apply that jump.
+func rewindmorestack(buf *gobuf) {
+ var inst uint64
+ if buf.pc&1 == 0 && buf.pc != 0 {
+ inst = *(*uint64)(unsafe.Pointer(buf.pc))
+ switch inst >> 48 {
+ case 0xa7f4: // BRC (branch relative on condition) instruction.
+ inst >>= 32
+ inst &= 0xFFFF
+ offset := int64(int16(inst))
+ offset <<= 1
+ buf.pc += uintptr(offset)
+ return
+ case 0xc0f4: // BRCL (branch relative on condition long) instruction.
+ inst >>= 16
+ inst = inst & 0xFFFFFFFF
+ inst = (inst << 1) & 0xFFFFFFFF
+ buf.pc += uintptr(int32(inst))
+ return
+ }
+ }
+ print("runtime: pc=", hex(buf.pc), " ", hex(inst), "\n")
+ throw("runtime: misuse of rewindmorestack")
+}
diff --git a/src/runtime/syscall_windows_test.go b/src/runtime/syscall_windows_test.go
index ff045338c1..4a10749682 100644
--- a/src/runtime/syscall_windows_test.go
+++ b/src/runtime/syscall_windows_test.go
@@ -622,6 +622,13 @@ uintptr_t cfunc(callback f, uintptr_t n) {
}
}
+func TestTimeBeginPeriod(t *testing.T) {
+ const TIMERR_NOERROR = 0
+ if *runtime.TimeBeginPeriodRetValue != TIMERR_NOERROR {
+ t.Fatalf("timeBeginPeriod failed: it returned %d", *runtime.TimeBeginPeriodRetValue)
+ }
+}
+
// removeOneCPU removes one (any) cpu from affinity mask.
// It returns new affinity mask.
func removeOneCPU(mask uintptr) (uintptr, error) {
@@ -874,21 +881,10 @@ var (
modwinmm = syscall.NewLazyDLL("winmm.dll")
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
- proctimeBeginPeriod = modwinmm.NewProc("timeBeginPeriod")
- proctimeEndPeriod = modwinmm.NewProc("timeEndPeriod")
-
procCreateEvent = modkernel32.NewProc("CreateEventW")
procSetEvent = modkernel32.NewProc("SetEvent")
)
-func timeBeginPeriod(period uint32) {
- syscall.Syscall(proctimeBeginPeriod.Addr(), 1, uintptr(period), 0, 0)
-}
-
-func timeEndPeriod(period uint32) {
- syscall.Syscall(proctimeEndPeriod.Addr(), 1, uintptr(period), 0, 0)
-}
-
func createEvent() (syscall.Handle, error) {
r0, _, e0 := syscall.Syscall6(procCreateEvent.Addr(), 4, 0, 0, 0, 0, 0, 0)
if r0 == 0 {
@@ -905,7 +901,7 @@ func setEvent(h syscall.Handle) error {
return nil
}
-func benchChanToSyscallPing(b *testing.B) {
+func BenchmarkChanToSyscallPing(b *testing.B) {
n := b.N
ch := make(chan int)
event, err := createEvent()
@@ -927,17 +923,7 @@ func benchChanToSyscallPing(b *testing.B) {
}
}
-func BenchmarkChanToSyscallPing1ms(b *testing.B) {
- timeBeginPeriod(1)
- benchChanToSyscallPing(b)
- timeEndPeriod(1)
-}
-
-func BenchmarkChanToSyscallPing15ms(b *testing.B) {
- benchChanToSyscallPing(b)
-}
-
-func benchSyscallToSyscallPing(b *testing.B) {
+func BenchmarkSyscallToSyscallPing(b *testing.B) {
n := b.N
event1, err := createEvent()
if err != nil {
@@ -965,17 +951,7 @@ func benchSyscallToSyscallPing(b *testing.B) {
}
}
-func BenchmarkSyscallToSyscallPing1ms(b *testing.B) {
- timeBeginPeriod(1)
- benchSyscallToSyscallPing(b)
- timeEndPeriod(1)
-}
-
-func BenchmarkSyscallToSyscallPing15ms(b *testing.B) {
- benchSyscallToSyscallPing(b)
-}
-
-func benchChanToChanPing(b *testing.B) {
+func BenchmarkChanToChanPing(b *testing.B) {
n := b.N
ch1 := make(chan int)
ch2 := make(chan int)
@@ -991,28 +967,8 @@ func benchChanToChanPing(b *testing.B) {
}
}
-func BenchmarkChanToChanPing1ms(b *testing.B) {
- timeBeginPeriod(1)
- benchChanToChanPing(b)
- timeEndPeriod(1)
-}
-
-func BenchmarkChanToChanPing15ms(b *testing.B) {
- benchChanToChanPing(b)
-}
-
-func benchOsYield(b *testing.B) {
+func BenchmarkOsYield(b *testing.B) {
for i := 0; i < b.N; i++ {
runtime.OsYield()
}
}
-
-func BenchmarkOsYield1ms(b *testing.B) {
- timeBeginPeriod(1)
- benchOsYield(b)
- timeEndPeriod(1)
-}
-
-func BenchmarkOsYield15ms(b *testing.B) {
- benchOsYield(b)
-}
diff --git a/src/runtime/tls_s390x.s b/src/runtime/tls_s390x.s
new file mode 100644
index 0000000000..cb6a21c114
--- /dev/null
+++ b/src/runtime/tls_s390x.s
@@ -0,0 +1,51 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "go_asm.h"
+#include "go_tls.h"
+#include "funcdata.h"
+#include "textflag.h"
+
+// We have to resort to TLS variable to save g (R13).
+// One reason is that external code might trigger
+// SIGSEGV, and our runtime.sigtramp don't even know we
+// are in external code, and will continue to use R13,
+// this might well result in another SIGSEGV.
+
+// save_g saves the g register into pthread-provided
+// thread-local memory, so that we can call externally compiled
+// s390x code that will overwrite this register.
+//
+// If !iscgo, this is a no-op.
+//
+// NOTE: setg_gcc<> assume this clobbers only R10 and R11.
+TEXT runtime·save_g(SB),NOSPLIT|NOFRAME,$0-0
+ MOVB runtime·iscgo(SB), R10
+ CMPBEQ R10, $0, nocgo
+ MOVW AR0, R11
+ SLD $32, R11
+ MOVW AR1, R11
+ MOVD runtime·tls_g(SB), R10
+ MOVD g, 0(R10)(R11*1)
+nocgo:
+ RET
+
+// load_g loads the g register from pthread-provided
+// thread-local memory, for use after calling externally compiled
+// s390x code that overwrote those registers.
+//
+// This is never called directly from C code (it doesn't have to
+// follow the C ABI), but it may be called from a C context, where the
+// usual Go registers aren't set up.
+//
+// NOTE: _cgo_topofstack assumes this only clobbers g (R13), R10 and R11.
+TEXT runtime·load_g(SB),NOSPLIT|NOFRAME,$0-0
+ MOVW AR0, R11
+ SLD $32, R11
+ MOVW AR1, R11
+ MOVD runtime·tls_g(SB), R10
+ MOVD 0(R10)(R11*1), g
+ RET
+
+GLOBL runtime·tls_g+0(SB),TLSBSS,$8
diff --git a/src/runtime/trace.go b/src/runtime/trace.go
index 805c34f483..092f941f0c 100644
--- a/src/runtime/trace.go
+++ b/src/runtime/trace.go
@@ -13,7 +13,6 @@
package runtime
import (
- "runtime/internal/atomic"
"runtime/internal/sys"
"unsafe"
)
@@ -23,25 +22,25 @@ const (
traceEvNone = 0 // unused
traceEvBatch = 1 // start of per-P batch of events [pid, timestamp]
traceEvFrequency = 2 // contains tracer timer frequency [frequency (ticks per second)]
- traceEvStack = 3 // stack [stack id, number of PCs, array of PCs]
+ traceEvStack = 3 // stack [stack id, number of PCs, array of {PC, func string ID, file string ID, line}]
traceEvGomaxprocs = 4 // current value of GOMAXPROCS [timestamp, GOMAXPROCS, stack id]
traceEvProcStart = 5 // start of P [timestamp, thread id]
traceEvProcStop = 6 // stop of P [timestamp]
- traceEvGCStart = 7 // GC start [timestamp, stack id]
+ traceEvGCStart = 7 // GC start [timestamp, seq, stack id]
traceEvGCDone = 8 // GC done [timestamp]
traceEvGCScanStart = 9 // GC scan start [timestamp]
traceEvGCScanDone = 10 // GC scan done [timestamp]
traceEvGCSweepStart = 11 // GC sweep start [timestamp, stack id]
traceEvGCSweepDone = 12 // GC sweep done [timestamp]
- traceEvGoCreate = 13 // goroutine creation [timestamp, new goroutine id, start PC, stack id]
- traceEvGoStart = 14 // goroutine starts running [timestamp, goroutine id]
+ traceEvGoCreate = 13 // goroutine creation [timestamp, new goroutine id, new stack id, stack id]
+ traceEvGoStart = 14 // goroutine starts running [timestamp, goroutine id, seq]
traceEvGoEnd = 15 // goroutine ends [timestamp]
traceEvGoStop = 16 // goroutine stops (like in select{}) [timestamp, stack]
traceEvGoSched = 17 // goroutine calls Gosched [timestamp, stack]
traceEvGoPreempt = 18 // goroutine is preempted [timestamp, stack]
traceEvGoSleep = 19 // goroutine calls Sleep [timestamp, stack]
traceEvGoBlock = 20 // goroutine blocks [timestamp, stack]
- traceEvGoUnblock = 21 // goroutine is unblocked [timestamp, goroutine id, stack]
+ traceEvGoUnblock = 21 // goroutine is unblocked [timestamp, goroutine id, seq, stack]
traceEvGoBlockSend = 22 // goroutine blocks on chan send [timestamp, stack]
traceEvGoBlockRecv = 23 // goroutine blocks on chan recv [timestamp, stack]
traceEvGoBlockSelect = 24 // goroutine blocks on select [timestamp, stack]
@@ -49,15 +48,19 @@ const (
traceEvGoBlockCond = 26 // goroutine blocks on Cond [timestamp, stack]
traceEvGoBlockNet = 27 // goroutine blocks on network [timestamp, stack]
traceEvGoSysCall = 28 // syscall enter [timestamp, stack]
- traceEvGoSysExit = 29 // syscall exit [timestamp, goroutine id, real timestamp]
+ traceEvGoSysExit = 29 // syscall exit [timestamp, goroutine id, seq, real timestamp]
traceEvGoSysBlock = 30 // syscall blocks [timestamp]
- traceEvGoWaiting = 31 // denotes that goroutine is blocked when tracing starts [goroutine id]
- traceEvGoInSyscall = 32 // denotes that goroutine is in syscall when tracing starts [goroutine id]
+ traceEvGoWaiting = 31 // denotes that goroutine is blocked when tracing starts [timestamp, goroutine id]
+ traceEvGoInSyscall = 32 // denotes that goroutine is in syscall when tracing starts [timestamp, goroutine id]
traceEvHeapAlloc = 33 // memstats.heap_live change [timestamp, heap_alloc]
traceEvNextGC = 34 // memstats.next_gc change [timestamp, next_gc]
traceEvTimerGoroutine = 35 // denotes timer goroutine [timer goroutine id]
traceEvFutileWakeup = 36 // denotes that the previous wakeup of this goroutine was futile [timestamp]
- traceEvCount = 37
+ traceEvString = 37 // string dictionary entry [ID, length, string]
+ traceEvGoStartLocal = 38 // goroutine starts running on the same P as the last event [timestamp, goroutine id]
+ traceEvGoUnblockLocal = 39 // goroutine is unblocked on the same P as the last event [timestamp, goroutine id, stack]
+ traceEvGoSysExitLocal = 40 // syscall exit on the same P as the last event [timestamp, goroutine id, real timestamp]
+ traceEvCount = 41
)
const (
@@ -104,6 +107,7 @@ var trace struct {
ticksEnd int64 // cputicks when tracing was stopped
timeStart int64 // nanotime when tracing was started
timeEnd int64 // nanotime when tracing was stopped
+ seqGC uint64 // GC start/done sequencer
reading traceBufPtr // buffer currently handed off to user
empty traceBufPtr // stack of empty buffers
fullHead traceBufPtr // queue of full buffers
@@ -111,35 +115,19 @@ var trace struct {
reader *g // goroutine that called ReadTrace, or nil
stackTab traceStackTable // maps stack traces to unique ids
+ // Dictionary for traceEvString.
+ // Currently this is used only for func/file:line info after tracing session,
+ // so we assume single-threaded access.
+ strings map[string]uint64
+ stringSeq uint64
+
bufLock mutex // protects buf
buf traceBufPtr // global trace buffer, used when running without a p
}
-var traceseq uint64 // global trace sequence number
-
-// tracestamp returns a consistent sequence number, time stamp pair
-// for use in a trace. We need to make sure that time stamp ordering
-// (assuming synchronized CPUs) and sequence ordering match.
-// To do that, we increment traceseq, grab ticks, and increment traceseq again.
-// We treat odd traceseq as a sign that another thread is in the middle
-// of the sequence and spin until it is done.
-// Not splitting stack to avoid preemption, just in case the call sites
-// that used to call xadd64 and cputicks are sensitive to that.
-//go:nosplit
-func tracestamp() (seq uint64, ts int64) {
- seq = atomic.Load64(&traceseq)
- for seq&1 != 0 || !atomic.Cas64(&traceseq, seq, seq+1) {
- seq = atomic.Load64(&traceseq)
- }
- ts = cputicks()
- atomic.Store64(&traceseq, seq+2)
- return seq >> 1, ts
-}
-
// traceBufHeader is per-P tracing buffer.
type traceBufHeader struct {
link traceBufPtr // in trace.empty/full
- lastSeq uint64 // sequence number of last event
lastTicks uint64 // when we wrote the last event
pos int // next write offset in arr
stk [traceStackSize]uintptr // scratch buffer for traceback
@@ -187,11 +175,6 @@ func StartTrace() error {
return errorString("tracing is already enabled")
}
- trace.seqStart, trace.ticksStart = tracestamp()
- trace.timeStart = nanotime()
- trace.headerWritten = false
- trace.footerWritten = false
-
// Can't set trace.enabled yet. While the world is stopped, exitsyscall could
// already emit a delayed event (see exitTicks in exitsyscall) if we set trace.enabled here.
// That would lead to an inconsistent trace:
@@ -204,12 +187,15 @@ func StartTrace() error {
for _, gp := range allgs {
status := readgstatus(gp)
if status != _Gdead {
- traceGoCreate(gp, gp.startpc)
+ traceGoCreate(gp, gp.startpc) // also resets gp.traceseq/tracelastp
}
if status == _Gwaiting {
+ // traceEvGoWaiting is implied to have seq=1.
+ gp.traceseq++
traceEvent(traceEvGoWaiting, -1, uint64(gp.goid))
}
if status == _Gsyscall {
+ gp.traceseq++
traceEvent(traceEvGoInSyscall, -1, uint64(gp.goid))
} else {
gp.sysblocktraced = false
@@ -217,6 +203,17 @@ func StartTrace() error {
}
traceProcStart()
traceGoStart()
+ // Note: ticksStart needs to be set after we emit traceEvGoInSyscall events.
+ // If we do it the other way around, it is possible that exitsyscall will
+ // query sysexitticks after ticksStart but before traceEvGoInSyscall timestamp.
+ // It will lead to a false conclusion that cputicks is broken.
+ trace.ticksStart = cputicks()
+ trace.timeStart = nanotime()
+ trace.headerWritten = false
+ trace.footerWritten = false
+ trace.strings = make(map[string]uint64)
+ trace.stringSeq = 0
+ trace.seqGC = 0
_g_.m.startingtrace = false
trace.enabled = true
@@ -272,8 +269,6 @@ func StopTrace() {
trace.enabled = false
trace.shutdown = true
- trace.stackTab.dump()
-
unlock(&trace.bufLock)
startTheWorld()
@@ -309,6 +304,7 @@ func StopTrace() {
trace.empty = buf.ptr().link
sysFree(unsafe.Pointer(buf), unsafe.Sizeof(*buf.ptr()), &memstats.other_sys)
}
+ trace.strings = nil
trace.shutdown = false
unlock(&trace.lock)
}
@@ -348,7 +344,7 @@ func ReadTrace() []byte {
trace.headerWritten = true
trace.lockOwner = nil
unlock(&trace.lock)
- return []byte("go 1.5 trace\x00\x00\x00\x00")
+ return []byte("go 1.7 trace\x00\x00\x00\x00")
}
// Wait for new data.
if trace.fullHead == 0 && !trace.shutdown {
@@ -374,12 +370,13 @@ func ReadTrace() []byte {
var data []byte
data = append(data, traceEvFrequency|0<<traceArgCountShift)
data = traceAppend(data, uint64(freq))
- data = traceAppend(data, 0)
if timers.gp != nil {
data = append(data, traceEvTimerGoroutine|0<<traceArgCountShift)
data = traceAppend(data, uint64(timers.gp.goid))
- data = traceAppend(data, 0)
}
+ // This will emit a bunch of full buffers, we will pick them up
+ // on the next iteration.
+ trace.stackTab.dump()
return data
}
// Done.
@@ -483,19 +480,14 @@ func traceEvent(ev byte, skip int, args ...uint64) {
(*bufp).set(buf)
}
- seq, ticksraw := tracestamp()
- seqDiff := seq - buf.lastSeq
- ticks := uint64(ticksraw) / traceTickDiv
+ ticks := uint64(cputicks()) / traceTickDiv
tickDiff := ticks - buf.lastTicks
if buf.pos == 0 {
buf.byte(traceEvBatch | 1<<traceArgCountShift)
buf.varint(uint64(pid))
- buf.varint(seq)
buf.varint(ticks)
- seqDiff = 0
tickDiff = 0
}
- buf.lastSeq = seq
buf.lastTicks = ticks
narg := byte(len(args))
if skip >= 0 {
@@ -514,7 +506,6 @@ func traceEvent(ev byte, skip int, args ...uint64) {
buf.varint(0)
lenp = &buf.arr[buf.pos-1]
}
- buf.varint(seqDiff)
buf.varint(tickDiff)
for _, a := range args {
buf.varint(a)
@@ -603,6 +594,29 @@ func traceFlush(buf traceBufPtr) traceBufPtr {
return buf
}
+func traceString(buf *traceBuf, s string) (uint64, *traceBuf) {
+ if s == "" {
+ return 0, buf
+ }
+ if id, ok := trace.strings[s]; ok {
+ return id, buf
+ }
+
+ trace.stringSeq++
+ id := trace.stringSeq
+ trace.strings[s] = id
+
+ size := 1 + 2*traceBytesPerNumber + len(s)
+ if len(buf.arr)-buf.pos < size {
+ buf = traceFlush(traceBufPtrOf(buf)).ptr()
+ }
+ buf.byte(traceEvString)
+ buf.varint(id)
+ buf.varint(uint64(len(s)))
+ buf.pos += copy(buf.arr[buf.pos:], s)
+ return id, buf
+}
+
// traceAppend appends v to buf in little-endian-base-128 encoding.
func traceAppend(buf []byte, v uint64) []byte {
for ; v >= 0x80; v >>= 7 {
@@ -716,23 +730,28 @@ func (tab *traceStackTable) newStack(n int) *traceStack {
// dump writes all previously cached stacks to trace buffers,
// releases all memory and resets state.
func (tab *traceStackTable) dump() {
- var tmp [(2 + traceStackSize) * traceBytesPerNumber]byte
+ frames := make(map[uintptr]traceFrame)
+ var tmp [(2 + 4*traceStackSize) * traceBytesPerNumber]byte
buf := traceFlush(0).ptr()
for _, stk := range tab.tab {
stk := stk.ptr()
for ; stk != nil; stk = stk.link.ptr() {
- maxSize := 1 + (3+stk.n)*traceBytesPerNumber
- if len(buf.arr)-buf.pos < maxSize {
- buf = traceFlush(traceBufPtrOf(buf)).ptr()
- }
- // Form the event in the temp buffer, we need to know the actual length.
tmpbuf := tmp[:0]
tmpbuf = traceAppend(tmpbuf, uint64(stk.id))
tmpbuf = traceAppend(tmpbuf, uint64(stk.n))
for _, pc := range stk.stack() {
+ var frame traceFrame
+ frame, buf = traceFrameForPC(buf, frames, pc)
tmpbuf = traceAppend(tmpbuf, uint64(pc))
+ tmpbuf = traceAppend(tmpbuf, uint64(frame.funcID))
+ tmpbuf = traceAppend(tmpbuf, uint64(frame.fileID))
+ tmpbuf = traceAppend(tmpbuf, uint64(frame.line))
}
// Now copy to the buffer.
+ size := 1 + traceBytesPerNumber + len(tmpbuf)
+ if len(buf.arr)-buf.pos < size {
+ buf = traceFlush(traceBufPtrOf(buf)).ptr()
+ }
buf.byte(traceEvStack | 3<<traceArgCountShift)
buf.varint(uint64(len(tmpbuf)))
buf.pos += copy(buf.arr[buf.pos:], tmpbuf)
@@ -747,6 +766,39 @@ func (tab *traceStackTable) dump() {
*tab = traceStackTable{}
}
+type traceFrame struct {
+ funcID uint64
+ fileID uint64
+ line uint64
+}
+
+func traceFrameForPC(buf *traceBuf, frames map[uintptr]traceFrame, pc uintptr) (traceFrame, *traceBuf) {
+ if frame, ok := frames[pc]; ok {
+ return frame, buf
+ }
+
+ var frame traceFrame
+ f := findfunc(pc)
+ if f == nil {
+ frames[pc] = frame
+ return frame, buf
+ }
+
+ fn := funcname(f)
+ const maxLen = 1 << 10
+ if len(fn) > maxLen {
+ fn = fn[len(fn)-maxLen:]
+ }
+ frame.funcID, buf = traceString(buf, fn)
+ file, line := funcline(f, pc-sys.PCQuantum)
+ frame.line = uint64(line)
+ if len(file) > maxLen {
+ file = file[len(file)-maxLen:]
+ }
+ frame.fileID, buf = traceString(buf, file)
+ return frame, buf
+}
+
// traceAlloc is a non-thread-safe region allocator.
// It holds a linked list of traceAllocBlock.
type traceAlloc struct {
@@ -820,7 +872,8 @@ func traceProcStop(pp *p) {
}
func traceGCStart() {
- traceEvent(traceEvGCStart, 3)
+ traceEvent(traceEvGCStart, 3, trace.seqGC)
+ trace.seqGC++
}
func traceGCDone() {
@@ -844,11 +897,23 @@ func traceGCSweepDone() {
}
func traceGoCreate(newg *g, pc uintptr) {
- traceEvent(traceEvGoCreate, 2, uint64(newg.goid), uint64(pc))
+ newg.traceseq = 0
+ newg.tracelastp = getg().m.p
+ // +PCQuantum because traceFrameForPC expects return PCs and subtracts PCQuantum.
+ id := trace.stackTab.put([]uintptr{pc + sys.PCQuantum})
+ traceEvent(traceEvGoCreate, 2, uint64(newg.goid), uint64(id))
}
func traceGoStart() {
- traceEvent(traceEvGoStart, -1, uint64(getg().m.curg.goid))
+ _g_ := getg().m.curg
+ _p_ := _g_.m.p
+ _g_.traceseq++
+ if _g_.tracelastp == _p_ {
+ traceEvent(traceEvGoStartLocal, -1, uint64(_g_.goid))
+ } else {
+ _g_.tracelastp = _p_
+ traceEvent(traceEvGoStart, -1, uint64(_g_.goid), _g_.traceseq)
+ }
}
func traceGoEnd() {
@@ -856,10 +921,14 @@ func traceGoEnd() {
}
func traceGoSched() {
+ _g_ := getg()
+ _g_.tracelastp = _g_.m.p
traceEvent(traceEvGoSched, 1)
}
func traceGoPreempt() {
+ _g_ := getg()
+ _g_.tracelastp = _g_.m.p
traceEvent(traceEvGoPreempt, 1)
}
@@ -871,19 +940,37 @@ func traceGoPark(traceEv byte, skip int, gp *g) {
}
func traceGoUnpark(gp *g, skip int) {
- traceEvent(traceEvGoUnblock, skip, uint64(gp.goid))
+ _p_ := getg().m.p
+ gp.traceseq++
+ if gp.tracelastp == _p_ {
+ traceEvent(traceEvGoUnblockLocal, skip, uint64(gp.goid))
+ } else {
+ gp.tracelastp = _p_
+ traceEvent(traceEvGoUnblock, skip, uint64(gp.goid), gp.traceseq)
+ }
}
func traceGoSysCall() {
traceEvent(traceEvGoSysCall, 1)
}
-func traceGoSysExit(seq uint64, ts int64) {
- if int64(seq)-int64(trace.seqStart) < 0 {
- // The timestamp was obtained during a previous tracing session, ignore.
- return
+func traceGoSysExit(ts int64) {
+ if ts != 0 && ts < trace.ticksStart {
+ // There is a race between the code that initializes sysexitticks
+ // (in exitsyscall, which runs without a P, and therefore is not
+ // stopped with the rest of the world) and the code that initializes
+ // a new trace. The recorded sysexitticks must therefore be treated
+ // as "best effort". If they are valid for this trace, then great,
+ // use them for greater accuracy. But if they're not valid for this
+ // trace, assume that the trace was started after the actual syscall
+ // exit (but before we actually managed to start the goroutine,
+ // aka right now), and assign a fresh time stamp to keep the log consistent.
+ ts = 0
}
- traceEvent(traceEvGoSysExit, -1, uint64(getg().m.curg.goid), seq, uint64(ts)/traceTickDiv)
+ _g_ := getg().m.curg
+ _g_.traceseq++
+ _g_.tracelastp = _g_.m.p
+ traceEvent(traceEvGoSysExit, -1, uint64(_g_.goid), _g_.traceseq, uint64(ts)/traceTickDiv)
}
func traceGoSysBlock(pp *p) {
diff --git a/src/runtime/trace/trace_stack_test.go b/src/runtime/trace/trace_stack_test.go
index b99ec687d5..52a71bfb94 100644
--- a/src/runtime/trace/trace_stack_test.go
+++ b/src/runtime/trace/trace_stack_test.go
@@ -125,14 +125,7 @@ func TestTraceSymbolize(t *testing.T) {
<-pipeReadDone
Stop()
- events, _, err := parseTrace(t, buf)
- if err != nil {
- t.Fatalf("failed to parse trace: %v", err)
- }
- err = trace.Symbolize(events, os.Args[0])
- if err != nil {
- t.Fatalf("failed to symbolize trace: %v", err)
- }
+ events, _ := parseTrace(t, buf)
// Now check that the stacks are correct.
type frame struct {
@@ -149,6 +142,9 @@ func TestTraceSymbolize(t *testing.T) {
{"runtime/trace_test.TestTraceSymbolize", 106},
{"testing.tRunner", 0},
}},
+ {trace.EvGoStart, []frame{
+ {"runtime/trace_test.TestTraceSymbolize.func1", 37},
+ }},
{trace.EvGoSched, []frame{
{"runtime/trace_test.TestTraceSymbolize", 107},
{"testing.tRunner", 0},
diff --git a/src/runtime/trace/trace_test.go b/src/runtime/trace/trace_test.go
index b787a2fc27..5fad3fb7f0 100644
--- a/src/runtime/trace/trace_test.go
+++ b/src/runtime/trace/trace_test.go
@@ -52,7 +52,7 @@ func TestTrace(t *testing.T) {
t.Fatalf("failed to start tracing: %v", err)
}
Stop()
- _, err := trace.Parse(buf)
+ _, err := trace.Parse(buf, "")
if err == trace.ErrTimeOrder {
t.Skipf("skipping trace: %v", err)
}
@@ -61,13 +61,13 @@ func TestTrace(t *testing.T) {
}
}
-func parseTrace(t *testing.T, r io.Reader) ([]*trace.Event, map[uint64]*trace.GDesc, error) {
- events, err := trace.Parse(r)
+func parseTrace(t *testing.T, r io.Reader) ([]*trace.Event, map[uint64]*trace.GDesc) {
+ events, err := trace.Parse(r, "")
if err == trace.ErrTimeOrder {
t.Skipf("skipping trace: %v", err)
}
if err != nil {
- return nil, nil, err
+ t.Fatalf("failed to parse trace: %v", err)
}
gs := trace.GoroutineStats(events)
for goid := range gs {
@@ -75,7 +75,31 @@ func parseTrace(t *testing.T, r io.Reader) ([]*trace.Event, map[uint64]*trace.GD
// But still check that RelatedGoroutines does not crash, hang, etc.
_ = trace.RelatedGoroutines(events, goid)
}
- return events, gs, nil
+ return events, gs
+}
+
+func testBrokenTimestamps(t *testing.T, data []byte) {
+ // On some processors cputicks (used to generate trace timestamps)
+ // produce non-monotonic timestamps. It is important that the parser
+ // distinguishes logically inconsistent traces (e.g. missing, excessive
+ // or misordered events) from broken timestamps. The former is a bug
+ // in tracer, the latter is a machine issue.
+ // So now that we have a consistent trace, test that (1) parser does
+ // not return a logical error in case of broken timestamps
+ // and (2) broken timestamps are eventually detected and reported.
+ trace.BreakTimestampsForTesting = true
+ defer func() {
+ trace.BreakTimestampsForTesting = false
+ }()
+ for i := 0; i < 1e4; i++ {
+ _, err := trace.Parse(bytes.NewReader(data), "")
+ if err == trace.ErrTimeOrder {
+ return
+ }
+ if err != nil {
+ t.Fatalf("failed to parse trace: %v", err)
+ }
+ }
}
func TestTraceStress(t *testing.T) {
@@ -209,10 +233,9 @@ func TestTraceStress(t *testing.T) {
runtime.GOMAXPROCS(procs)
Stop()
- _, _, err = parseTrace(t, buf)
- if err != nil {
- t.Fatalf("failed to parse trace: %v", err)
- }
+ trace := buf.Bytes()
+ parseTrace(t, buf)
+ testBrokenTimestamps(t, trace)
}
// Do a bunch of various stuff (timers, GC, network, etc) in a separate goroutine.
@@ -353,9 +376,9 @@ func TestTraceStressStartStop(t *testing.T) {
}
time.Sleep(time.Millisecond)
Stop()
- if _, _, err := parseTrace(t, buf); err != nil {
- t.Fatalf("failed to parse trace: %v", err)
- }
+ trace := buf.Bytes()
+ parseTrace(t, buf)
+ testBrokenTimestamps(t, trace)
}
<-outerDone
}
@@ -413,10 +436,7 @@ func TestTraceFutileWakeup(t *testing.T) {
done.Wait()
Stop()
- events, _, err := parseTrace(t, buf)
- if err != nil {
- t.Fatalf("failed to parse trace: %v", err)
- }
+ events, _ := parseTrace(t, buf)
// Check that (1) trace does not contain EvFutileWakeup events and
// (2) there are no consecutive EvGoBlock/EvGCStart/EvGoBlock events
// (we call runtime.Gosched between all operations, so these would be futile wakeups).
diff --git a/src/runtime/type.go b/src/runtime/type.go
index fbf6f9973c..608c601abd 100644
--- a/src/runtime/type.go
+++ b/src/runtime/type.go
@@ -6,15 +6,20 @@
package runtime
-import (
- "runtime/internal/sys"
- "unsafe"
-)
+import "unsafe"
-// tflag is documented in ../reflect/type.go.
+// tflag is documented in reflect/type.go.
+//
+// tflag values must be kept in sync with copies in:
+// cmd/compile/internal/gc/reflect.go
+// cmd/link/internal/ld/decodesym.go
+// reflect/type.go
type tflag uint8
-const tflagUncommon tflag = 1
+const (
+ tflagUncommon tflag = 1 << 0
+ tflagExtraStar tflag = 1 << 1
+)
// Needs to be in sync with ../cmd/compile/internal/ld/decodesym.go:/^func.commonsize,
// ../cmd/compile/internal/gc/reflect.go:/^func.dcommontype and
@@ -31,8 +36,17 @@ type _type struct {
// 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
+ gcdata *byte
+ str nameOff
+ _ int32
+}
+
+func (t *_type) string() string {
+ s := t.nameOff(t.str).name()
+ if t.tflag&tflagExtraStar != 0 {
+ return s[1:]
+ }
+ return s
}
func (t *_type) uncommon() *uncommontype {
@@ -102,33 +116,160 @@ func hasPrefix(s, prefix string) bool {
}
func (t *_type) name() string {
- if hasPrefix(t._string, "map[") {
+ s := t.string()
+ if hasPrefix(s, "map[") {
return ""
}
- if hasPrefix(t._string, "struct {") {
+ if hasPrefix(s, "struct {") {
return ""
}
- if hasPrefix(t._string, "chan ") {
+ if hasPrefix(s, "chan ") {
return ""
}
- if hasPrefix(t._string, "chan<-") {
+ if hasPrefix(s, "chan<-") {
return ""
}
- if hasPrefix(t._string, "func(") {
+ if hasPrefix(s, "func(") {
return ""
}
- switch t._string[0] {
+ if hasPrefix(s, "interface {") {
+ return ""
+ }
+ switch s[0] {
case '[', '*', '<':
return ""
}
- i := len(t._string) - 1
+ i := len(s) - 1
for i >= 0 {
- if t._string[i] == '.' {
+ if s[i] == '.' {
break
}
i--
}
- return t._string[i+1:]
+ return s[i+1:]
+}
+
+// reflectOffs holds type offsets defined at run time by the reflect package.
+//
+// When a type is defined at run time, its *rtype data lives on the heap.
+// There are a wide range of possible addresses the heap may use, that
+// may not be representable as a 32-bit offset. Moreover the GC may
+// one day start moving heap memory, in which case there is no stable
+// offset that can be defined.
+//
+// To provide stable offsets, we add pin *rtype objects in a global map
+// and treat the offset as an identifier. We use negative offsets that
+// do not overlap with any compile-time module offsets.
+//
+// Entries are created by reflect.addReflectOff.
+var reflectOffs struct {
+ lock mutex
+ next int32
+ m map[int32]unsafe.Pointer
+ minv map[unsafe.Pointer]int32
+}
+
+func resolveNameOff(ptrInModule unsafe.Pointer, off nameOff) name {
+ if off == 0 {
+ return name{}
+ }
+ base := uintptr(ptrInModule)
+ var md *moduledata
+ for next := &firstmoduledata; next != nil; next = next.next {
+ if base >= next.types && base < next.etypes {
+ md = next
+ break
+ }
+ }
+ if md == nil {
+ lock(&reflectOffs.lock)
+ res, found := reflectOffs.m[int32(off)]
+ unlock(&reflectOffs.lock)
+ if !found {
+ println("runtime: nameOff", hex(off), "base", hex(base), "not in ranges:")
+ for next := &firstmoduledata; next != nil; next = next.next {
+ println("\ttypes", hex(next.types), "etypes", hex(next.etypes))
+ }
+ throw("runtime: name offset base pointer out of range")
+ }
+ return name{(*byte)(res)}
+ }
+ res := md.types + uintptr(off)
+ if res > md.etypes {
+ println("runtime: nameOff", hex(off), "out of range", hex(md.types), "-", hex(md.etypes))
+ throw("runtime: name offset out of range")
+ }
+ return name{(*byte)(unsafe.Pointer(res))}
+}
+
+func (t *_type) nameOff(off nameOff) name {
+ return resolveNameOff(unsafe.Pointer(t), off)
+}
+
+func (t *_type) typeOff(off typeOff) *_type {
+ if off == 0 {
+ return nil
+ }
+ base := uintptr(unsafe.Pointer(t))
+ var md *moduledata
+ for next := &firstmoduledata; next != nil; next = next.next {
+ if base >= next.types && base < next.etypes {
+ md = next
+ break
+ }
+ }
+ if md == nil {
+ lock(&reflectOffs.lock)
+ res := reflectOffs.m[int32(off)]
+ unlock(&reflectOffs.lock)
+ if res == nil {
+ println("runtime: typeOff", hex(off), "base", hex(base), "not in ranges:")
+ for next := &firstmoduledata; next != nil; next = next.next {
+ println("\ttypes", hex(next.types), "etypes", hex(next.etypes))
+ }
+ throw("runtime: type offset base pointer out of range")
+ }
+ return (*_type)(res)
+ }
+ if t := md.typemap[off]; t != nil {
+ return t
+ }
+ res := md.types + uintptr(off)
+ if res > md.etypes {
+ println("runtime: typeOff", hex(off), "out of range", hex(md.types), "-", hex(md.etypes))
+ throw("runtime: type offset out of range")
+ }
+ return (*_type)(unsafe.Pointer(res))
+}
+
+func (t *_type) textOff(off textOff) unsafe.Pointer {
+ base := uintptr(unsafe.Pointer(t))
+ var md *moduledata
+ for next := &firstmoduledata; next != nil; next = next.next {
+ if base >= next.types && base < next.etypes {
+ md = next
+ break
+ }
+ }
+ if md == nil {
+ lock(&reflectOffs.lock)
+ res := reflectOffs.m[int32(off)]
+ unlock(&reflectOffs.lock)
+ if res == nil {
+ println("runtime: textOff", hex(off), "base", hex(base), "not in ranges:")
+ for next := &firstmoduledata; next != nil; next = next.next {
+ println("\ttypes", hex(next.types), "etypes", hex(next.etypes))
+ }
+ throw("runtime: text offset base pointer out of range")
+ }
+ return res
+ }
+ res := md.text + uintptr(off)
+ if res > md.etext {
+ println("runtime: textOff", hex(off), "out of range", hex(md.text), "-", hex(md.etext))
+ throw("runtime: text offset out of range")
+ }
+ return unsafe.Pointer(res)
}
func (t *functype) in() []*_type {
@@ -154,26 +295,31 @@ func (t *functype) dotdotdot() bool {
return t.outCount&(1<<15) != 0
}
+type nameOff int32
+type typeOff int32
+type textOff int32
+
type method struct {
- name name
- mtyp *_type
- ifn unsafe.Pointer
- tfn unsafe.Pointer
+ name nameOff
+ mtyp typeOff
+ ifn textOff
+ tfn textOff
}
type uncommontype struct {
- pkgpath *string
- mhdr []method
+ pkgpath nameOff
+ mcount uint16 // number of methods
+ moff uint16 // offset from this uncommontype to [mcount]method
}
type imethod struct {
- name name
- _type *_type
+ name nameOff
+ ityp typeOff
}
type interfacetype struct {
typ _type
- pkgpath *string
+ pkgpath name
mhdr []imethod
}
@@ -229,7 +375,7 @@ type structfield struct {
type structtype struct {
typ _type
- pkgPath *string
+ pkgPath name
fields []structfield
}
@@ -239,19 +385,19 @@ type name struct {
bytes *byte
}
-func (n *name) data(off int) *byte {
+func (n name) data(off int) *byte {
return (*byte)(add(unsafe.Pointer(n.bytes), uintptr(off)))
}
-func (n *name) isExported() bool {
+func (n name) isExported() bool {
return (*n.bytes)&(1<<0) != 0
}
-func (n *name) nameLen() int {
+func (n name) nameLen() int {
return int(uint16(*n.data(1))<<8 | uint16(*n.data(2)))
}
-func (n *name) tagLen() int {
+func (n name) tagLen() int {
if *n.data(0)&(1<<1) == 0 {
return 0
}
@@ -259,7 +405,10 @@ func (n *name) tagLen() int {
return int(uint16(*n.data(off))<<8 | uint16(*n.data(off + 1)))
}
-func (n *name) name() (s string) {
+func (n name) name() (s string) {
+ if n.bytes == nil {
+ return ""
+ }
nl := n.nameLen()
if nl == 0 {
return ""
@@ -270,14 +419,219 @@ func (n *name) name() (s string) {
return s
}
-func (n *name) pkgPath() *string {
- if *n.data(0)&(1<<2) == 0 {
- return nil
+func (n name) tag() (s string) {
+ tl := n.tagLen()
+ if tl == 0 {
+ return ""
+ }
+ nl := n.nameLen()
+ hdr := (*stringStruct)(unsafe.Pointer(&s))
+ hdr.str = unsafe.Pointer(n.data(3 + nl + 2))
+ hdr.len = tl
+ return s
+}
+
+func (n name) pkgPath() string {
+ if n.bytes == nil || *n.data(0)&(1<<2) == 0 {
+ return ""
}
off := 3 + n.nameLen()
if tl := n.tagLen(); tl > 0 {
off += 2 + tl
}
- off = int(round(uintptr(off), sys.PtrSize))
- return *(**string)(unsafe.Pointer(n.data(off)))
+ var nameOff nameOff
+ copy((*[4]byte)(unsafe.Pointer(&nameOff))[:], (*[4]byte)(unsafe.Pointer(n.data(off)))[:])
+ pkgPathName := resolveNameOff(unsafe.Pointer(n.bytes), nameOff)
+ return pkgPathName.name()
+}
+
+// typelinksinit scans the types from extra modules and builds the
+// moduledata typemap used to de-duplicate type pointers.
+func typelinksinit() {
+ if firstmoduledata.next == nil {
+ return
+ }
+ typehash := make(map[uint32][]*_type)
+
+ modules := []*moduledata{}
+ for md := &firstmoduledata; md != nil; md = md.next {
+ modules = append(modules, md)
+ }
+ prev, modules := modules[len(modules)-1], modules[:len(modules)-1]
+ for len(modules) > 0 {
+ // Collect types from the previous module into typehash.
+ collect:
+ for _, tl := range prev.typelinks {
+ var t *_type
+ if prev.typemap == nil {
+ t = (*_type)(unsafe.Pointer(prev.types + uintptr(tl)))
+ } else {
+ t = prev.typemap[typeOff(tl)]
+ }
+ // Add to typehash if not seen before.
+ tlist := typehash[t.hash]
+ for _, tcur := range tlist {
+ if tcur == t {
+ continue collect
+ }
+ }
+ typehash[t.hash] = append(tlist, t)
+ }
+
+ // If any of this module's typelinks match a type from a
+ // prior module, prefer that prior type by adding the offset
+ // to this module's typemap.
+ md := modules[len(modules)-1]
+ md.typemap = make(map[typeOff]*_type, len(md.typelinks))
+ for _, tl := range md.typelinks {
+ t := (*_type)(unsafe.Pointer(md.types + uintptr(tl)))
+ for _, candidate := range typehash[t.hash] {
+ if typesEqual(t, candidate) {
+ t = candidate
+ break
+ }
+ }
+ md.typemap[typeOff(tl)] = t
+ }
+
+ prev, modules = md, modules[:len(modules)-1]
+ }
+}
+
+// typesEqual reports whether two types are equal.
+//
+// Everywhere in the runtime and reflect packages, it is assumed that
+// there is exactly one *_type per Go type, so that pointer equality
+// can be used to test if types are equal. There is one place that
+// breaks this assumption: buildmode=shared. In this case a type can
+// appear as two different pieces of memory. This is hidden from the
+// runtime and reflect package by the per-module typemap built in
+// typelinksinit. It uses typesEqual to map types from later modules
+// back into earlier ones.
+//
+// Only typelinksinit needs this function.
+func typesEqual(t, v *_type) bool {
+ if t == v {
+ return true
+ }
+ kind := t.kind & kindMask
+ if kind != v.kind&kindMask {
+ return false
+ }
+ if t.string() != v.string() {
+ return false
+ }
+ ut := t.uncommon()
+ uv := v.uncommon()
+ if ut != nil || uv != nil {
+ if ut == nil || uv == nil {
+ return false
+ }
+ pkgpatht := t.nameOff(ut.pkgpath).name()
+ pkgpathv := v.nameOff(uv.pkgpath).name()
+ if pkgpatht != pkgpathv {
+ return false
+ }
+ }
+ if kindBool <= kind && kind <= kindComplex128 {
+ return true
+ }
+ switch kind {
+ case kindString, kindUnsafePointer:
+ return true
+ case kindArray:
+ at := (*arraytype)(unsafe.Pointer(t))
+ av := (*arraytype)(unsafe.Pointer(v))
+ return typesEqual(at.elem, av.elem) && at.len == av.len
+ case kindChan:
+ ct := (*chantype)(unsafe.Pointer(t))
+ cv := (*chantype)(unsafe.Pointer(v))
+ return ct.dir == cv.dir && typesEqual(ct.elem, cv.elem)
+ case kindFunc:
+ ft := (*functype)(unsafe.Pointer(t))
+ fv := (*functype)(unsafe.Pointer(v))
+ if ft.outCount != fv.outCount || ft.inCount != fv.inCount {
+ return false
+ }
+ tin, vin := ft.in(), fv.in()
+ for i := 0; i < len(tin); i++ {
+ if !typesEqual(tin[i], vin[i]) {
+ return false
+ }
+ }
+ tout, vout := ft.out(), fv.out()
+ for i := 0; i < len(tout); i++ {
+ if !typesEqual(tout[i], vout[i]) {
+ return false
+ }
+ }
+ return true
+ case kindInterface:
+ it := (*interfacetype)(unsafe.Pointer(t))
+ iv := (*interfacetype)(unsafe.Pointer(v))
+ if it.pkgpath.name() != iv.pkgpath.name() {
+ return false
+ }
+ if len(it.mhdr) != len(iv.mhdr) {
+ return false
+ }
+ for i := range it.mhdr {
+ tm := &it.mhdr[i]
+ vm := &iv.mhdr[i]
+ tname := it.typ.nameOff(tm.name)
+ vname := iv.typ.nameOff(vm.name)
+ if tname.name() != vname.name() {
+ return false
+ }
+ if tname.pkgPath() != vname.pkgPath() {
+ return false
+ }
+ if !typesEqual(it.typ.typeOff(tm.ityp), iv.typ.typeOff(vm.ityp)) {
+ return false
+ }
+ }
+ return true
+ case kindMap:
+ mt := (*maptype)(unsafe.Pointer(t))
+ mv := (*maptype)(unsafe.Pointer(v))
+ return typesEqual(mt.key, mv.key) && typesEqual(mt.elem, mv.elem)
+ case kindPtr:
+ pt := (*ptrtype)(unsafe.Pointer(t))
+ pv := (*ptrtype)(unsafe.Pointer(v))
+ return typesEqual(pt.elem, pv.elem)
+ case kindSlice:
+ st := (*slicetype)(unsafe.Pointer(t))
+ sv := (*slicetype)(unsafe.Pointer(v))
+ return typesEqual(st.elem, sv.elem)
+ case kindStruct:
+ st := (*structtype)(unsafe.Pointer(t))
+ sv := (*structtype)(unsafe.Pointer(v))
+ if len(st.fields) != len(sv.fields) {
+ return false
+ }
+ for i := range st.fields {
+ tf := &st.fields[i]
+ vf := &sv.fields[i]
+ if tf.name.name() != vf.name.name() {
+ return false
+ }
+ if tf.name.pkgPath() != vf.name.pkgPath() {
+ return false
+ }
+ if !typesEqual(tf.typ, vf.typ) {
+ return false
+ }
+ if tf.name.tag() != vf.name.tag() {
+ return false
+ }
+ if tf.offset != vf.offset {
+ return false
+ }
+ }
+ return true
+ default:
+ println("runtime: impossible type kind", kind)
+ throw("runtime: impossible type kind")
+ return false
+ }
}
diff --git a/src/runtime/unaligned1.go b/src/runtime/unaligned1.go
index 6bd9018352..754d63b417 100644
--- a/src/runtime/unaligned1.go
+++ b/src/runtime/unaligned1.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build 386 amd64 amd64p32 arm64 ppc64 ppc64le
+// +build 386 amd64 amd64p32 arm64 ppc64 ppc64le s390x
package runtime
diff --git a/src/runtime/vdso_linux_amd64.go b/src/runtime/vdso_linux_amd64.go
index 42571e063c..8a970dfbe6 100644
--- a/src/runtime/vdso_linux_amd64.go
+++ b/src/runtime/vdso_linux_amd64.go
@@ -4,10 +4,7 @@
package runtime
-import (
- "runtime/internal/sys"
- "unsafe"
-)
+import "unsafe"
// Look up symbols in the Linux vDSO.
@@ -21,9 +18,7 @@ import (
// http://refspecs.linuxfoundation.org/LSB_3.2.0/LSB-Core-generic/LSB-Core-generic/symversion.html
const (
- _AT_RANDOM = 25
_AT_SYSINFO_EHDR = 33
- _AT_NULL = 0 /* End of vector */
_PT_LOAD = 1 /* Loadable program segment */
_PT_DYNAMIC = 2 /* Dynamic linking information */
@@ -294,37 +289,18 @@ func vdso_parse_symbols(info *vdso_info, version int32) {
}
}
-func sysargs(argc int32, argv **byte) {
- n := argc + 1
-
- // skip envp to get to ELF auxiliary vector.
- for argv_index(argv, n) != nil {
- n++
- }
-
- // skip NULL separator
- n++
-
- // now argv+n is auxv
- auxv := (*[1 << 32]elf64Auxv)(add(unsafe.Pointer(argv), uintptr(n)*sys.PtrSize))
-
- for i := 0; auxv[i].a_type != _AT_NULL; i++ {
- av := &auxv[i]
- switch av.a_type {
- case _AT_SYSINFO_EHDR:
- if av.a_val == 0 {
- // Something went wrong
- continue
- }
- var info vdso_info
- // TODO(rsc): I don't understand why the compiler thinks info escapes
- // when passed to the three functions below.
- info1 := (*vdso_info)(noescape(unsafe.Pointer(&info)))
- vdso_init_from_sysinfo_ehdr(info1, (*elf64Ehdr)(unsafe.Pointer(uintptr(av.a_val))))
- vdso_parse_symbols(info1, vdso_find_version(info1, &linux26))
-
- case _AT_RANDOM:
- startupRandomData = (*[16]byte)(unsafe.Pointer(uintptr(av.a_val)))[:]
+func archauxv(tag, val uintptr) {
+ switch tag {
+ case _AT_SYSINFO_EHDR:
+ if val == 0 {
+ // Something went wrong
+ return
}
+ var info vdso_info
+ // TODO(rsc): I don't understand why the compiler thinks info escapes
+ // when passed to the three functions below.
+ info1 := (*vdso_info)(noescape(unsafe.Pointer(&info)))
+ vdso_init_from_sysinfo_ehdr(info1, (*elf64Ehdr)(unsafe.Pointer(val)))
+ vdso_parse_symbols(info1, vdso_find_version(info1, &linux26))
}
}
diff --git a/src/runtime/vdso_none.go b/src/runtime/vdso_none.go
index b4e0a0e349..efae23f6ee 100644
--- a/src/runtime/vdso_none.go
+++ b/src/runtime/vdso_none.go
@@ -2,9 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build !linux !amd64
-// +build !linux !386
-// +build !linux !arm
+// +build !linux
package runtime
diff --git a/src/runtime/vlop_arm_test.go b/src/runtime/vlop_arm_test.go
index 1a211196f2..85cea923a9 100644
--- a/src/runtime/vlop_arm_test.go
+++ b/src/runtime/vlop_arm_test.go
@@ -82,3 +82,47 @@ func TestUsplit(t *testing.T) {
}
}
}
+
+//go:noinline
+func armFloatWrite(a *[129]float64) {
+ // This used to miscompile on arm5.
+ // The offset is too big to fit in a load.
+ // So the code does:
+ // ldr r0, [sp, #8]
+ // bl 6f690 <_sfloat>
+ // ldr fp, [pc, #32] ; (address of 128.0)
+ // vldr d0, [fp]
+ // ldr fp, [pc, #28] ; (1024)
+ // add fp, fp, r0
+ // vstr d0, [fp]
+ // The software floating-point emulator gives up on the add.
+ // This causes the store to not work.
+ // See issue 15440.
+ a[128] = 128.0
+}
+func TestArmFloatBigOffsetWrite(t *testing.T) {
+ var a [129]float64
+ for i := 0; i < 128; i++ {
+ a[i] = float64(i)
+ }
+ armFloatWrite(&a)
+ for i, x := range a {
+ if x != float64(i) {
+ t.Errorf("bad entry %d:%f\n", i, x)
+ }
+ }
+}
+
+//go:noinline
+func armFloatRead(a *[129]float64) float64 {
+ return a[128]
+}
+func TestArmFloatBigOffsetRead(t *testing.T) {
+ var a [129]float64
+ for i := 0; i < 129; i++ {
+ a[i] = float64(i)
+ }
+ if x := armFloatRead(&a); x != 128.0 {
+ t.Errorf("bad value %f\n", x)
+ }
+}
diff --git a/src/runtime/vlrt.go b/src/runtime/vlrt.go
index 2419f78ce2..cd37828ae4 100644
--- a/src/runtime/vlrt.go
+++ b/src/runtime/vlrt.go
@@ -195,7 +195,6 @@ func dodiv(n, d uint64) (q, r uint64) {
if GOARCH == "arm" {
// arm doesn't have a division instruction, so
// slowdodiv is the best that we can do.
- // TODO: revisit for arm64.
return slowdodiv(n, d)
}
diff --git a/src/strconv/atof.go b/src/strconv/atof.go
index ce76252340..ada85e9fed 100644
--- a/src/strconv/atof.go
+++ b/src/strconv/atof.go
@@ -244,7 +244,9 @@ func readFloat(s string) (mantissa uint64, exp int, neg, trunc, ok bool) {
return
}
- exp = dp - ndMant
+ if mantissa != 0 {
+ exp = dp - ndMant
+ }
ok = true
return
diff --git a/src/strconv/atof_test.go b/src/strconv/atof_test.go
index 9f70cc1fd7..0a89c3e0bf 100644
--- a/src/strconv/atof_test.go
+++ b/src/strconv/atof_test.go
@@ -42,6 +42,30 @@ var atoftests = []atofTest{
{"1e-20", "1e-20", nil},
{"625e-3", "0.625", nil},
+ // zeros
+ {"0", "0", nil},
+ {"0e0", "0", nil},
+ {"-0e0", "-0", nil},
+ {"+0e0", "0", nil},
+ {"0e-0", "0", nil},
+ {"-0e-0", "-0", nil},
+ {"+0e-0", "0", nil},
+ {"0e+0", "0", nil},
+ {"-0e+0", "-0", nil},
+ {"+0e+0", "0", nil},
+ {"0e+01234567890123456789", "0", nil},
+ {"0.00e-01234567890123456789", "0", nil},
+ {"-0e+01234567890123456789", "-0", nil},
+ {"-0.00e-01234567890123456789", "-0", nil},
+ {"0e291", "0", nil}, // issue 15364
+ {"0e292", "0", nil}, // issue 15364
+ {"0e347", "0", nil}, // issue 15364
+ {"0e348", "0", nil}, // issue 15364
+ {"-0e291", "-0", nil},
+ {"-0e292", "-0", nil},
+ {"-0e347", "-0", nil},
+ {"-0e348", "-0", nil},
+
// NaNs
{"nan", "NaN", nil},
{"NaN", "NaN", nil},
diff --git a/src/strconv/extfloat.go b/src/strconv/extfloat.go
index 019b4eebdc..7033e96c39 100644
--- a/src/strconv/extfloat.go
+++ b/src/strconv/extfloat.go
@@ -311,9 +311,9 @@ func (f *extFloat) AssignDecimal(mantissa uint64, exp10 int, neg bool, trunc boo
var extrabits uint
if f.exp <= denormalExp {
// f.mant * 2^f.exp is smaller than 2^(flt.bias+1).
- extrabits = uint(63 - flt.mantbits + 1 + uint(denormalExp-f.exp))
+ extrabits = 63 - flt.mantbits + 1 + uint(denormalExp-f.exp)
} else {
- extrabits = uint(63 - flt.mantbits)
+ extrabits = 63 - flt.mantbits
}
halfway := uint64(1) << (extrabits - 1)
diff --git a/src/strings/reader.go b/src/strings/reader.go
index 248e55245c..74eed4d574 100644
--- a/src/strings/reader.go
+++ b/src/strings/reader.go
@@ -113,7 +113,7 @@ func (r *Reader) Seek(offset int64, whence int) (int64, error) {
case 0:
abs = offset
case 1:
- abs = int64(r.i) + offset
+ abs = r.i + offset
case 2:
abs = int64(len(r.s)) + offset
default:
@@ -145,6 +145,9 @@ func (r *Reader) WriteTo(w io.Writer) (n int64, err error) {
return
}
+// Reset resets the Reader to be reading from s.
+func (r *Reader) Reset(s string) { *r = Reader{s, 0, -1} }
+
// NewReader returns a new Reader reading from s.
// It is similar to bytes.NewBufferString but more efficient and read-only.
func NewReader(s string) *Reader { return &Reader{s, 0, -1} }
diff --git a/src/strings/reader_test.go b/src/strings/reader_test.go
index 5003a37be4..6e9d904b9d 100644
--- a/src/strings/reader_test.go
+++ b/src/strings/reader_test.go
@@ -9,7 +9,6 @@ import (
"fmt"
"io"
"io/ioutil"
- "os"
"strings"
"sync"
"testing"
@@ -25,15 +24,15 @@ func TestReader(t *testing.T) {
wantpos int64
seekerr string
}{
- {seek: os.SEEK_SET, off: 0, n: 20, want: "0123456789"},
- {seek: os.SEEK_SET, off: 1, n: 1, want: "1"},
- {seek: os.SEEK_CUR, off: 1, wantpos: 3, n: 2, want: "34"},
- {seek: os.SEEK_SET, off: -1, seekerr: "strings.Reader.Seek: negative position"},
- {seek: os.SEEK_SET, off: 1 << 33, wantpos: 1 << 33},
- {seek: os.SEEK_CUR, off: 1, wantpos: 1<<33 + 1},
- {seek: os.SEEK_SET, n: 5, want: "01234"},
- {seek: os.SEEK_CUR, n: 5, want: "56789"},
- {seek: os.SEEK_END, off: -1, n: 1, wantpos: 9, want: "9"},
+ {seek: io.SeekStart, off: 0, n: 20, want: "0123456789"},
+ {seek: io.SeekStart, off: 1, n: 1, want: "1"},
+ {seek: io.SeekCurrent, off: 1, wantpos: 3, n: 2, want: "34"},
+ {seek: io.SeekStart, off: -1, seekerr: "strings.Reader.Seek: negative position"},
+ {seek: io.SeekStart, off: 1 << 33, wantpos: 1 << 33},
+ {seek: io.SeekCurrent, off: 1, wantpos: 1<<33 + 1},
+ {seek: io.SeekStart, n: 5, want: "01234"},
+ {seek: io.SeekCurrent, n: 5, want: "56789"},
+ {seek: io.SeekEnd, off: -1, n: 1, wantpos: 9, want: "9"},
}
for i, tt := range tests {
@@ -64,7 +63,7 @@ func TestReader(t *testing.T) {
func TestReadAfterBigSeek(t *testing.T) {
r := strings.NewReader("0123456789")
- if _, err := r.Seek(1<<31+5, os.SEEK_SET); err != nil {
+ if _, err := r.Seek(1<<31+5, io.SeekStart); err != nil {
t.Fatal(err)
}
if n, err := r.Read(make([]byte, 10)); n != 0 || err != io.EOF {
@@ -170,3 +169,23 @@ func TestReaderLenSize(t *testing.T) {
t.Errorf("Size = %d; want 3", r.Size())
}
}
+
+func TestReaderReset(t *testing.T) {
+ r := strings.NewReader("世界")
+ if _, _, err := r.ReadRune(); err != nil {
+ t.Errorf("ReadRune: unexpected error: %v", err)
+ }
+
+ const want = "abcdef"
+ r.Reset(want)
+ if err := r.UnreadRune(); err == nil {
+ t.Errorf("UnreadRune: expected error, got nil")
+ }
+ buf, err := ioutil.ReadAll(r)
+ if err != nil {
+ t.Errorf("ReadAll: unexpected error: %v", err)
+ }
+ if got := string(buf); got != want {
+ t.Errorf("ReadAll: got %q, want %q", got, want)
+ }
+}
diff --git a/src/strings/strings.go b/src/strings/strings.go
index c24c77b9dd..919e8c8354 100644
--- a/src/strings/strings.go
+++ b/src/strings/strings.go
@@ -12,32 +12,25 @@ import (
"unicode/utf8"
)
-// explode splits s into an array of UTF-8 sequences, one per Unicode character (still strings) up to a maximum of n (n < 0 means no limit).
-// Invalid UTF-8 sequences become correct encodings of U+FFF8.
+// explode splits s into a slice of UTF-8 strings,
+// one string per Unicode character up to a maximum of n (n < 0 means no limit).
+// Invalid UTF-8 sequences become correct encodings of U+FFFD.
func explode(s string, n int) []string {
- if n == 0 {
- return nil
- }
l := utf8.RuneCountInString(s)
- if n <= 0 || n > l {
+ if n < 0 || n > l {
n = l
}
a := make([]string, n)
- var size int
- var ch rune
- i, cur := 0, 0
- for ; i+1 < n; i++ {
- ch, size = utf8.DecodeRuneInString(s[cur:])
+ for i := 0; i < n-1; i++ {
+ ch, size := utf8.DecodeRuneInString(s)
+ a[i] = s[:size]
+ s = s[size:]
if ch == utf8.RuneError {
a[i] = string(utf8.RuneError)
- } else {
- a[i] = s[cur : cur+size]
}
- cur += size
}
- // add the rest, if there is any
- if cur < len(s) {
- a[i] = s[cur:]
+ if n > 0 {
+ a[n-1] = s
}
return a
}
diff --git a/src/strings/strings_test.go b/src/strings/strings_test.go
index 0572adbdd9..1ed803bf85 100644
--- a/src/strings/strings_test.go
+++ b/src/strings/strings_test.go
@@ -256,31 +256,6 @@ func BenchmarkIndexByte(b *testing.B) {
}
}
-var explodetests = []struct {
- s string
- n int
- a []string
-}{
- {"", -1, []string{}},
- {abcd, 4, []string{"a", "b", "c", "d"}},
- {faces, 3, []string{"☺", "☻", "☹"}},
- {abcd, 2, []string{"a", "bcd"}},
-}
-
-func TestExplode(t *testing.T) {
- for _, tt := range explodetests {
- a := SplitN(tt.s, "", tt.n)
- if !eq(a, tt.a) {
- t.Errorf("explode(%q, %d) = %v; want %v", tt.s, tt.n, a, tt.a)
- continue
- }
- s := Join(a, "")
- if s != tt.s {
- t.Errorf(`Join(explode(%q, %d), "") = %q`, tt.s, tt.n, s)
- }
- }
-}
-
type SplitTest struct {
s string
sep string
@@ -289,19 +264,23 @@ type SplitTest struct {
}
var splittests = []SplitTest{
+ {"", "", -1, []string{}},
+ {abcd, "", 2, []string{"a", "bcd"}},
+ {abcd, "", 4, []string{"a", "b", "c", "d"}},
+ {abcd, "", -1, []string{"a", "b", "c", "d"}},
+ {faces, "", -1, []string{"☺", "☻", "☹"}},
+ {faces, "", 3, []string{"☺", "☻", "☹"}},
+ {faces, "", 17, []string{"☺", "☻", "☹"}},
+ {"☺�☹", "", -1, []string{"☺", "�", "☹"}},
{abcd, "a", 0, nil},
{abcd, "a", -1, []string{"", "bcd"}},
{abcd, "z", -1, []string{"abcd"}},
- {abcd, "", -1, []string{"a", "b", "c", "d"}},
{commas, ",", -1, []string{"1", "2", "3", "4"}},
{dots, "...", -1, []string{"1", ".2", ".3", ".4"}},
{faces, "☹", -1, []string{"☺☻", ""}},
{faces, "~", -1, []string{faces}},
- {faces, "", -1, []string{"☺", "☻", "☹"}},
{"1 2 3 4", " ", 3, []string{"1", "2", "3 4"}},
{"1 2", " ", 3, []string{"1", "2"}},
- {"123", "", 2, []string{"1", "23"}},
- {"123", "", 17, []string{"1", "2", "3"}},
}
func TestSplit(t *testing.T) {
diff --git a/src/sync/atomic/asm_s390x.s b/src/sync/atomic/asm_s390x.s
new file mode 100644
index 0000000000..b5389be38f
--- /dev/null
+++ b/src/sync/atomic/asm_s390x.s
@@ -0,0 +1,143 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+TEXT ·SwapInt32(SB),NOSPLIT,$0-20
+ BR ·SwapUint32(SB)
+
+TEXT ·SwapUint32(SB),NOSPLIT,$0-20
+ MOVD addr+0(FP), R3
+ MOVWZ new+8(FP), R4
+ MOVWZ (R3), R5
+repeat:
+ CS R5, R4, (R3) // if (R3)==R5 then (R3)=R4 else R5=(R3)
+ BNE repeat
+ MOVW R5, old+16(FP)
+ RET
+
+TEXT ·SwapInt64(SB),NOSPLIT,$0-24
+ BR ·SwapUint64(SB)
+
+TEXT ·SwapUint64(SB),NOSPLIT,$0-24
+ MOVD addr+0(FP), R3
+ MOVD new+8(FP), R4
+ MOVD (R3), R5
+repeat:
+ CSG R5, R4, (R3) // if (R3)==R5 then (R3)=R4 else R5=(R3)
+ BNE repeat
+ MOVD R5, old+16(FP)
+ RET
+
+TEXT ·SwapUintptr(SB),NOSPLIT,$0-24
+ BR ·SwapUint64(SB)
+
+TEXT ·CompareAndSwapInt32(SB),NOSPLIT,$0-17
+ BR ·CompareAndSwapUint32(SB)
+
+TEXT ·CompareAndSwapUint32(SB),NOSPLIT,$0-17
+ MOVD ptr+0(FP), R3
+ MOVWZ old+8(FP), R4
+ MOVWZ new+12(FP), R5
+ CS R4, R5, 0(R3) // if R4==(R3) then (R3)=R5 else R4=(R3)
+ BNE cas_fail
+ MOVB $1, ret+16(FP)
+ RET
+cas_fail:
+ MOVB $0, ret+16(FP)
+ RET
+
+TEXT ·CompareAndSwapUintptr(SB),NOSPLIT,$0-25
+ BR ·CompareAndSwapUint64(SB)
+
+TEXT ·CompareAndSwapInt64(SB),NOSPLIT,$0-25
+ BR ·CompareAndSwapUint64(SB)
+
+TEXT ·CompareAndSwapUint64(SB),NOSPLIT,$0-25
+ MOVD ptr+0(FP), R3
+ MOVD old+8(FP), R4
+ MOVD new+16(FP), R5
+ CSG R4, R5, 0(R3) // if R4==(R3) then (R3)=R5 else R4=(R3)
+ BNE cas64_fail
+ MOVB $1, ret+24(FP)
+ RET
+cas64_fail:
+ MOVB $0, ret+24(FP)
+ RET
+
+TEXT ·AddInt32(SB),NOSPLIT,$0-20
+ BR ·AddUint32(SB)
+
+TEXT ·AddUint32(SB),NOSPLIT,$0-20
+ MOVD ptr+0(FP), R4
+ MOVWZ delta+8(FP), R5
+ MOVWZ (R4), R3
+repeat:
+ ADD R3, R5, R6
+ CS R3, R6, (R4) // if R3==(R4) then (R4)=R6 else R3=(R4)
+ BNE repeat
+ MOVW R6, ret+16(FP)
+ RET
+
+TEXT ·AddUintptr(SB),NOSPLIT,$0-24
+ BR ·AddUint64(SB)
+
+TEXT ·AddInt64(SB),NOSPLIT,$0-24
+ BR ·AddUint64(SB)
+
+TEXT ·AddUint64(SB),NOSPLIT,$0-24
+ MOVD ptr+0(FP), R4
+ MOVD delta+8(FP), R5
+ MOVD (R4), R3
+repeat:
+ ADD R3, R5, R6
+ CSG R3, R6, (R4) // if R3==(R4) then (R4)=R6 else R3=(R4)
+ BNE repeat
+ MOVD R6, ret+16(FP)
+ RET
+
+TEXT ·LoadInt32(SB),NOSPLIT,$0-12
+ BR ·LoadUint32(SB)
+
+TEXT ·LoadUint32(SB),NOSPLIT,$0-12
+ MOVD addr+0(FP), R3
+ MOVW 0(R3), R4
+ MOVW R4, val+8(FP)
+ RET
+
+TEXT ·LoadInt64(SB),NOSPLIT,$0-16
+ BR ·LoadUint64(SB)
+
+TEXT ·LoadUint64(SB),NOSPLIT,$0-16
+ MOVD addr+0(FP), R3
+ MOVD 0(R3), R4
+ MOVD R4, val+8(FP)
+ RET
+
+TEXT ·LoadUintptr(SB),NOSPLIT,$0-16
+ BR ·LoadPointer(SB)
+
+TEXT ·LoadPointer(SB),NOSPLIT,$0-16
+ BR ·LoadUint64(SB)
+
+TEXT ·StoreInt32(SB),NOSPLIT,$0-12
+ BR ·StoreUint32(SB)
+
+TEXT ·StoreUint32(SB),NOSPLIT,$0-12
+ MOVD ptr+0(FP), R3
+ MOVW val+8(FP), R4
+ MOVW R4, 0(R3)
+ RET
+
+TEXT ·StoreInt64(SB),NOSPLIT,$0-16
+ BR ·StoreUint64(SB)
+
+TEXT ·StoreUint64(SB),NOSPLIT,$0-16
+ MOVD addr+0(FP), R3
+ MOVD val+8(FP), R4
+ MOVD R4, 0(R3)
+ RET
+
+TEXT ·StoreUintptr(SB),NOSPLIT,$0-16
+ BR ·StoreUint64(SB)
diff --git a/src/sync/pool.go b/src/sync/pool.go
index 4fb1a1af9d..2acf505f3c 100644
--- a/src/sync/pool.go
+++ b/src/sync/pool.go
@@ -179,8 +179,8 @@ func (p *Pool) pinSlow() *poolLocal {
// If GOMAXPROCS changes between GCs, we re-allocate the array and lose the old one.
size := runtime.GOMAXPROCS(0)
local := make([]poolLocal, size)
- atomic.StorePointer((*unsafe.Pointer)(&p.local), unsafe.Pointer(&local[0])) // store-release
- atomic.StoreUintptr(&p.localSize, uintptr(size)) // store-release
+ atomic.StorePointer(&p.local, unsafe.Pointer(&local[0])) // store-release
+ atomic.StoreUintptr(&p.localSize, uintptr(size)) // store-release
return &local[pid]
}
diff --git a/src/syscall/asm_linux_s390x.s b/src/syscall/asm_linux_s390x.s
new file mode 100644
index 0000000000..e22a92b966
--- /dev/null
+++ b/src/syscall/asm_linux_s390x.s
@@ -0,0 +1,156 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+//
+// System calls for s390x, Linux
+//
+
+// func Syscall(trap int64, a1, a2, a3 int64) (r1, r2, err int64)
+TEXT ·Syscall(SB),NOSPLIT,$0-56
+ BL runtime·entersyscall(SB)
+ MOVD a1+8(FP), R2
+ MOVD a2+16(FP), R3
+ MOVD a3+24(FP), R4
+ MOVD $0, R5
+ MOVD $0, R6
+ MOVD $0, R7
+ MOVD trap+0(FP), R1 // syscall entry
+ SYSCALL
+ MOVD $0xfffffffffffff001, R8
+ CMPUBLT R2, R8, ok
+ MOVD $-1, r1+32(FP)
+ MOVD $0, r2+40(FP)
+ NEG R2, R2
+ MOVD R2, err+48(FP) // errno
+ BL runtime·exitsyscall(SB)
+ RET
+ok:
+ MOVD R2, r1+32(FP)
+ MOVD R3, r2+40(FP)
+ MOVD $0, err+48(FP) // errno
+ BL runtime·exitsyscall(SB)
+ RET
+
+// func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
+TEXT ·Syscall6(SB),NOSPLIT,$0-80
+ BL runtime·entersyscall(SB)
+ MOVD a1+8(FP), R2
+ MOVD a2+16(FP), R3
+ MOVD a3+24(FP), R4
+ MOVD a4+32(FP), R5
+ MOVD a5+40(FP), R6
+ MOVD a6+48(FP), R7
+ MOVD trap+0(FP), R1 // syscall entry
+ SYSCALL
+ MOVD $0xfffffffffffff001, R8
+ CMPUBLT R2, R8, ok6
+ MOVD $-1, r1+56(FP)
+ MOVD $0, r2+64(FP)
+ NEG R2, R2
+ MOVD R2, err+72(FP) // errno
+ BL runtime·exitsyscall(SB)
+ RET
+ok6:
+ MOVD R2, r1+56(FP)
+ MOVD R3, r2+64(FP)
+ MOVD $0, err+72(FP) // errno
+ BL runtime·exitsyscall(SB)
+ RET
+
+// func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
+TEXT ·RawSyscall(SB),NOSPLIT,$0-56
+ MOVD a1+8(FP), R2
+ MOVD a2+16(FP), R3
+ MOVD a3+24(FP), R4
+ MOVD $0, R5
+ MOVD $0, R6
+ MOVD $0, R7
+ MOVD trap+0(FP), R1 // syscall entry
+ SYSCALL
+ MOVD $0xfffffffffffff001, R8
+ CMPUBLT R2, R8, ok1
+ MOVD $-1, r1+32(FP)
+ MOVD $0, r2+40(FP)
+ NEG R2, R2
+ MOVD R2, err+48(FP) // errno
+ RET
+ok1:
+ MOVD R2, r1+32(FP)
+ MOVD R3, r2+40(FP)
+ MOVD $0, err+48(FP) // errno
+ RET
+
+// func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
+TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
+ MOVD a1+8(FP), R2
+ MOVD a2+16(FP), R3
+ MOVD a3+24(FP), R4
+ MOVD a4+32(FP), R5
+ MOVD a5+40(FP), R6
+ MOVD a6+48(FP), R7
+ MOVD trap+0(FP), R1 // syscall entry
+ SYSCALL
+ MOVD $0xfffffffffffff001, R8
+ CMPUBLT R2, R8, ok2
+ MOVD $-1, r1+56(FP)
+ MOVD $0, r2+64(FP)
+ NEG R2, R2
+ MOVD R2, err+72(FP) // errno
+ RET
+ok2:
+ MOVD R2, r1+56(FP)
+ MOVD R3, r2+64(FP)
+ MOVD $0, err+72(FP) // errno
+ RET
+
+#define SYS_SOCKETCALL 102 /* from zsysnum_linux_s390x.go */
+
+// func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, err int)
+// Kernel interface gets call sub-number and pointer to a0.
+TEXT ·socketcall(SB),NOSPLIT,$0-72
+ BL runtime·entersyscall(SB)
+ MOVD $SYS_SOCKETCALL, R1 // syscall entry
+ MOVD call+0(FP), R2 // socket call number
+ MOVD $a0+8(FP), R3 // pointer to call arguments
+ MOVD $0, R4
+ MOVD $0, R5
+ MOVD $0, R6
+ MOVD $0, R7
+ SYSCALL
+ MOVD $0xfffffffffffff001, R8
+ CMPUBLT R2, R8, oksock
+ MOVD $-1, n+56(FP)
+ NEG R2, R2
+ MOVD R2, err+64(FP)
+ BL runtime·exitsyscall(SB)
+ RET
+oksock:
+ MOVD R2, n+56(FP)
+ MOVD $0, err+64(FP)
+ CALL runtime·exitsyscall(SB)
+ RET
+
+// func rawsocketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, err int)
+// Kernel interface gets call sub-number and pointer to a0.
+TEXT ·rawsocketcall(SB),NOSPLIT,$0-72
+ MOVD $SYS_SOCKETCALL, R1 // syscall entry
+ MOVD call+0(FP), R2 // socket call number
+ MOVD $a0+8(FP), R3 // pointer to call arguments
+ MOVD $0, R4
+ MOVD $0, R5
+ MOVD $0, R6
+ MOVD $0, R7
+ SYSCALL
+ MOVD $0xfffffffffffff001, R8
+ CMPUBLT R2, R8, oksock1
+ MOVD $-1, n+56(FP)
+ NEG R2, R2
+ MOVD R2, err+64(FP)
+ RET
+oksock1:
+ MOVD R2, n+56(FP)
+ MOVD $0, err+64(FP)
+ RET
diff --git a/src/syscall/mksyscall_windows.go b/src/syscall/mksyscall_windows.go
index 7786d1349e..a6cef6fca7 100644
--- a/src/syscall/mksyscall_windows.go
+++ b/src/syscall/mksyscall_windows.go
@@ -707,9 +707,9 @@ func (src *Source) Generate(w io.Writer) error {
}
if *sysRepo {
if packageName == "windows" {
- return "&LazyDLL{Name: " + arg + ", System: true}"
+ return "NewLazySystemDLL(" + arg + ")"
} else {
- return "&windows.LazyDLL{Name: " + arg + ", System: true}"
+ return "windows.NewLazySystemDLL(" + arg + ")"
}
} else {
return syscalldot() + "NewLazyDLL(" + arg + ")"
diff --git a/src/syscall/sockcmsg_unix.go b/src/syscall/sockcmsg_unix.go
index b7a7c83286..bc4caf54a2 100644
--- a/src/syscall/sockcmsg_unix.go
+++ b/src/syscall/sockcmsg_unix.go
@@ -62,7 +62,7 @@ func ParseSocketControlMessage(b []byte) ([]SocketControlMessage, error) {
func socketControlMessageHeaderAndData(b []byte) (*Cmsghdr, []byte, error) {
h := (*Cmsghdr)(unsafe.Pointer(&b[0]))
- if h.Len < SizeofCmsghdr || int(h.Len) > len(b) {
+ if h.Len < SizeofCmsghdr || uint64(h.Len) > uint64(len(b)) {
return nil, nil, EINVAL
}
return h, b[cmsgAlignOf(SizeofCmsghdr):h.Len], nil
diff --git a/src/syscall/types_linux.go b/src/syscall/types_linux.go
index 9bccfcabd8..28d0225cbf 100644
--- a/src/syscall/types_linux.go
+++ b/src/syscall/types_linux.go
@@ -117,6 +117,9 @@ struct my_epoll_event {
// alignment requirements of EABI
int32_t padFd;
#endif
+#ifdef __powerpc64__
+ int32_t _padFd;
+#endif
int32_t fd;
int32_t pad;
};
diff --git a/src/syscall/ztypes_linux_ppc64.go b/src/syscall/ztypes_linux_ppc64.go
index 33d1b7f3e5..915ca95190 100644
--- a/src/syscall/ztypes_linux_ppc64.go
+++ b/src/syscall/ztypes_linux_ppc64.go
@@ -574,9 +574,10 @@ type Ustat_t struct {
}
type EpollEvent struct {
- Events uint32
- Fd int32
- Pad int32
+ Events uint32
+ X_padFd int32
+ Fd int32
+ Pad int32
}
const (
diff --git a/src/syscall/ztypes_linux_ppc64le.go b/src/syscall/ztypes_linux_ppc64le.go
index 27ca004834..a1180553ec 100644
--- a/src/syscall/ztypes_linux_ppc64le.go
+++ b/src/syscall/ztypes_linux_ppc64le.go
@@ -574,9 +574,10 @@ type Ustat_t struct {
}
type EpollEvent struct {
- Events uint32
- Fd int32
- Pad int32
+ Events uint32
+ X_padFd int32
+ Fd int32
+ Pad int32
}
const (
diff --git a/src/testing/benchmark.go b/src/testing/benchmark.go
index c935bc5e06..5d58b85e78 100644
--- a/src/testing/benchmark.go
+++ b/src/testing/benchmark.go
@@ -208,7 +208,24 @@ func (b *B) run1() bool {
b.runN(1)
}()
<-b.signal
- return !b.hasSub
+ if b.failed {
+ fmt.Fprintf(b.w, "--- FAIL: %s\n%s", b.name, b.output)
+ return false
+ }
+ // Only print the output if we know we are not going to proceed.
+ // Otherwise it is printed in processBench.
+ if b.hasSub || b.finished {
+ tag := "BENCH"
+ if b.skipped {
+ tag = "SKIP"
+ }
+ if b.chatty && (len(b.output) > 0 || b.finished) {
+ b.trimOutput()
+ fmt.Fprintf(b.w, "--- %s: %s\n%s", tag, b.name, b.output)
+ }
+ return false
+ }
+ return true
}
// run executes the benchmark in a separate goroutine, including all of its
@@ -372,7 +389,11 @@ func runBenchmarksInternal(matchString func(pat, str string) (bool, error), benc
}
}
main := &B{
- common: common{name: "Main"},
+ common: common{
+ name: "Main",
+ w: os.Stdout,
+ chatty: *chatty,
+ },
benchFunc: func(b *B) {
for _, Benchmark := range bs {
b.Run(Benchmark.Name, Benchmark.F)
@@ -390,13 +411,15 @@ func (ctx *benchContext) processBench(b *B) {
for i, procs := range cpuList {
runtime.GOMAXPROCS(procs)
benchName := benchmarkName(b.name, procs)
- fmt.Printf("%-*s\t", ctx.maxLen, benchName)
+ fmt.Fprintf(b.w, "%-*s\t", ctx.maxLen, benchName)
// Recompute the running time for all but the first iteration.
if i > 0 {
b = &B{
common: common{
signal: make(chan bool),
name: b.name,
+ w: b.w,
+ chatty: b.chatty,
},
benchFunc: b.benchFunc,
benchTime: b.benchTime,
@@ -408,19 +431,19 @@ func (ctx *benchContext) processBench(b *B) {
// The output could be very long here, but probably isn't.
// We print it all, regardless, because we don't want to trim the reason
// the benchmark failed.
- fmt.Printf("--- FAIL: %s\n%s", benchName, b.output)
+ fmt.Fprintf(b.w, "--- FAIL: %s\n%s", benchName, b.output)
continue
}
results := r.String()
if *benchmarkMemory || b.showAllocResult {
results += "\t" + r.MemString()
}
- fmt.Println(results)
+ fmt.Fprintln(b.w, results)
// Unlike with tests, we ignore the -chatty flag and always print output for
// benchmarks since the output generation time will skew the results.
if len(b.output) > 0 {
b.trimOutput()
- fmt.Printf("--- BENCH: %s\n%s", benchName, b.output)
+ fmt.Fprintf(b.w, "--- BENCH: %s\n%s", benchName, b.output)
}
if p := runtime.GOMAXPROCS(-1); p != procs {
fmt.Fprintf(os.Stderr, "testing: %s left GOMAXPROCS set to %d\n", benchName, p)
@@ -453,6 +476,8 @@ func (b *B) Run(name string, f func(b *B)) bool {
name: benchName,
parent: &b.common,
level: b.level + 1,
+ w: b.w,
+ chatty: b.chatty,
},
benchFunc: f,
benchTime: b.benchTime,
@@ -597,9 +622,17 @@ func Benchmark(f func(b *B)) BenchmarkResult {
b := &B{
common: common{
signal: make(chan bool),
+ w: discard{},
},
benchFunc: f,
benchTime: *benchTime,
}
+ if !b.run1() {
+ return BenchmarkResult{}
+ }
return b.run()
}
+
+type discard struct{}
+
+func (discard) Write(b []byte) (n int, err error) { return len(b), nil }
diff --git a/src/testing/match.go b/src/testing/match.go
index d0c52142ba..7751035760 100644
--- a/src/testing/match.go
+++ b/src/testing/match.go
@@ -8,29 +8,40 @@ import (
"fmt"
"os"
"strconv"
+ "strings"
"sync"
)
// matcher sanitizes, uniques, and filters names of subtests and subbenchmarks.
type matcher struct {
- filter string
+ filter []string
matchFunc func(pat, str string) (bool, error)
mu sync.Mutex
subNames map[string]int64
}
-// TODO: fix test_main to avoid race and improve caching.
+// TODO: fix test_main to avoid race and improve caching, also allowing to
+// eliminate this Mutex.
var matchMutex sync.Mutex
-func newMatcher(matchString func(pat, str string) (bool, error), pattern, name string) *matcher {
- // Verify filters before doing any processing.
- if _, err := matchString(pattern, "non-empty"); err != nil {
- fmt.Fprintf(os.Stderr, "testing: invalid regexp for %s: %s\n", name, err)
- os.Exit(1)
+func newMatcher(matchString func(pat, str string) (bool, error), patterns, name string) *matcher {
+ var filter []string
+ if patterns != "" {
+ filter = splitRegexp(patterns)
+ for i, s := range filter {
+ filter[i] = rewrite(s)
+ }
+ // Verify filters before doing any processing.
+ for i, s := range filter {
+ if _, err := matchString(s, "non-empty"); err != nil {
+ fmt.Fprintf(os.Stderr, "testing: invalid regexp for element %d of %s (%q): %s\n", i, name, s, err)
+ os.Exit(1)
+ }
+ }
}
return &matcher{
- filter: pattern,
+ filter: filter,
matchFunc: matchString,
subNames: map[string]int64{},
}
@@ -49,14 +60,54 @@ func (m *matcher) fullName(c *common, subname string) (name string, ok bool) {
matchMutex.Lock()
defer matchMutex.Unlock()
- if c != nil && c.level == 0 {
- if matched, _ := m.matchFunc(m.filter, subname); !matched {
+ // We check the full array of paths each time to allow for the case that
+ // a pattern contains a '/'.
+ for i, s := range strings.Split(name, "/") {
+ if i >= len(m.filter) {
+ break
+ }
+ if ok, _ := m.matchFunc(m.filter[i], s); !ok {
return name, false
}
}
return name, true
}
+func splitRegexp(s string) []string {
+ a := make([]string, 0, strings.Count(s, "/"))
+ cs := 0
+ cp := 0
+ for i := 0; i < len(s); {
+ switch s[i] {
+ case '[':
+ cs++
+ case ']':
+ if cs--; cs < 0 { // An unmatched ']' is legal.
+ cs = 0
+ }
+ case '(':
+ if cs == 0 {
+ cp++
+ }
+ case ')':
+ if cs == 0 {
+ cp--
+ }
+ case '\\':
+ i++
+ case '/':
+ if cs == 0 && cp == 0 {
+ a = append(a, s[:i])
+ s = s[i+1:]
+ i = 0
+ continue
+ }
+ }
+ i++
+ }
+ return append(a, s)
+}
+
// unique creates a unique name for the given parent and subname by affixing it
// with one ore more counts, if necessary.
func (m *matcher) unique(parent, subname string) string {
diff --git a/src/testing/match_test.go b/src/testing/match_test.go
index 68f3e9e867..d19036c72d 100644
--- a/src/testing/match_test.go
+++ b/src/testing/match_test.go
@@ -5,6 +5,7 @@
package testing
import (
+ "reflect"
"regexp"
"unicode"
)
@@ -23,6 +24,123 @@ func TestIsSpace(t *T) {
}
}
+func TestSplitRegexp(t *T) {
+ res := func(s ...string) []string { return s }
+ testCases := []struct {
+ pattern string
+ result []string
+ }{
+ // Correct patterns
+ // If a regexp pattern is correct, all split regexps need to be correct
+ // as well.
+ {"", res("")},
+ {"/", res("", "")},
+ {"//", res("", "", "")},
+ {"A", res("A")},
+ {"A/B", res("A", "B")},
+ {"A/B/", res("A", "B", "")},
+ {"/A/B/", res("", "A", "B", "")},
+ {"[A]/(B)", res("[A]", "(B)")},
+ {"[/]/[/]", res("[/]", "[/]")},
+ {"[/]/[:/]", res("[/]", "[:/]")},
+ {"/]", res("", "]")},
+ {"]/", res("]", "")},
+ {"]/[/]", res("]", "[/]")},
+ {`([)/][(])`, res(`([)/][(])`)},
+ {"[(]/[)]", res("[(]", "[)]")},
+
+ // Faulty patterns
+ // Errors in original should produce at least one faulty regexp in results.
+ {")/", res(")/")},
+ {")/(/)", res(")/(", ")")},
+ {"a[/)b", res("a[/)b")},
+ {"(/]", res("(/]")},
+ {"(/", res("(/")},
+ {"[/]/[/", res("[/]", "[/")},
+ {`\p{/}`, res(`\p{`, "}")},
+ {`\p/`, res(`\p`, "")},
+ {`[[:/:]]`, res(`[[:/:]]`)},
+ }
+ for _, tc := range testCases {
+ a := splitRegexp(tc.pattern)
+ if !reflect.DeepEqual(a, tc.result) {
+ t.Errorf("splitRegexp(%q) = %#v; want %#v", tc.pattern, a, tc.result)
+ }
+
+ // If there is any error in the pattern, one of the returned subpatterns
+ // needs to have an error as well.
+ if _, err := regexp.Compile(tc.pattern); err != nil {
+ ok := true
+ for _, re := range a {
+ if _, err := regexp.Compile(re); err != nil {
+ ok = false
+ }
+ }
+ if ok {
+ t.Errorf("%s: expected error in any of %q", tc.pattern, a)
+ }
+ }
+ }
+}
+
+func TestMatcher(t *T) {
+ testCases := []struct {
+ pattern string
+ parent, sub string
+ ok bool
+ }{
+ // Behavior without subtests.
+ {"", "", "TestFoo", true},
+ {"TestFoo", "", "TestFoo", true},
+ {"TestFoo/", "", "TestFoo", true},
+ {"TestFoo/bar/baz", "", "TestFoo", true},
+ {"TestFoo", "", "TestBar", false},
+ {"TestFoo/", "", "TestBar", false},
+ {"TestFoo/bar/baz", "", "TestBar/bar/baz", false},
+
+ // with subtests
+ {"", "TestFoo", "x", true},
+ {"TestFoo", "TestFoo", "x", true},
+ {"TestFoo/", "TestFoo", "x", true},
+ {"TestFoo/bar/baz", "TestFoo", "bar", true},
+ // Subtest with a '/' in its name still allows for copy and pasted names
+ // to match.
+ {"TestFoo/bar/baz", "TestFoo", "bar/baz", true},
+ {"TestFoo/bar/baz", "TestFoo/bar", "baz", true},
+ {"TestFoo/bar/baz", "TestFoo", "x", false},
+ {"TestFoo", "TestBar", "x", false},
+ {"TestFoo/", "TestBar", "x", false},
+ {"TestFoo/bar/baz", "TestBar", "x/bar/baz", false},
+
+ // subtests only
+ {"", "TestFoo", "x", true},
+ {"/", "TestFoo", "x", true},
+ {"./", "TestFoo", "x", true},
+ {"./.", "TestFoo", "x", true},
+ {"/bar/baz", "TestFoo", "bar", true},
+ {"/bar/baz", "TestFoo", "bar/baz", true},
+ {"//baz", "TestFoo", "bar/baz", true},
+ {"//", "TestFoo", "bar/baz", true},
+ {"/bar/baz", "TestFoo/bar", "baz", true},
+ {"//foo", "TestFoo", "bar/baz", false},
+ {"/bar/baz", "TestFoo", "x", false},
+ {"/bar/baz", "TestBar", "x/bar/baz", false},
+ }
+
+ for _, tc := range testCases {
+ m := newMatcher(regexp.MatchString, tc.pattern, "-test.run")
+
+ parent := &common{name: tc.parent}
+ if tc.parent != "" {
+ parent.level = 1
+ }
+ if n, ok := m.fullName(parent, tc.sub); ok != tc.ok {
+ t.Errorf("pattern: %q, parent: %q, sub %q: got %v; want %v",
+ tc.pattern, tc.parent, tc.sub, ok, tc.ok, n)
+ }
+ }
+}
+
func TestNaming(t *T) {
m := newMatcher(regexp.MatchString, "", "")
diff --git a/src/testing/sub_test.go b/src/testing/sub_test.go
index 264d77c2cf..2804550737 100644
--- a/src/testing/sub_test.go
+++ b/src/testing/sub_test.go
@@ -5,8 +5,9 @@
package testing
import (
- "io/ioutil"
+ "bytes"
"regexp"
+ "strings"
"sync/atomic"
"time"
)
@@ -113,11 +114,17 @@ func TestTRun(t *T) {
desc string
ok bool
maxPar int
+ chatty bool
+ output string
f func(*T)
}{{
desc: "failnow skips future sequential and parallel tests at same level",
ok: false,
maxPar: 1,
+ output: `
+--- FAIL: failnow skips future sequential and parallel tests at same level (N.NNs)
+ --- FAIL: failnow skips future sequential and parallel tests at same level/#00 (N.NNs)
+ `,
f: func(t *T) {
ranSeq := false
ranPar := false
@@ -149,6 +156,11 @@ func TestTRun(t *T) {
desc: "failure in parallel test propagates upwards",
ok: false,
maxPar: 1,
+ output: `
+--- FAIL: failure in parallel test propagates upwards (N.NNs)
+ --- FAIL: failure in parallel test propagates upwards/#00 (N.NNs)
+ --- FAIL: failure in parallel test propagates upwards/#00/par (N.NNs)
+ `,
f: func(t *T) {
t.Run("", func(t *T) {
t.Parallel()
@@ -159,6 +171,44 @@ func TestTRun(t *T) {
})
},
}, {
+ desc: "skipping without message, chatty",
+ ok: true,
+ chatty: true,
+ output: `
+=== RUN skipping without message, chatty
+--- SKIP: skipping without message, chatty (N.NNs)`,
+ f: func(t *T) { t.SkipNow() },
+ }, {
+ desc: "chatty with recursion",
+ ok: true,
+ chatty: true,
+ output: `
+=== RUN chatty with recursion
+=== RUN chatty with recursion/#00
+=== RUN chatty with recursion/#00/#00
+--- PASS: chatty with recursion (N.NNs)
+ --- PASS: chatty with recursion/#00 (N.NNs)
+ --- PASS: chatty with recursion/#00/#00 (N.NNs)`,
+ f: func(t *T) {
+ t.Run("", func(t *T) {
+ t.Run("", func(t *T) {})
+ })
+ },
+ }, {
+ desc: "skipping without message, not chatty",
+ ok: true,
+ f: func(t *T) { t.SkipNow() },
+ }, {
+ desc: "skipping after error",
+ output: `
+--- FAIL: skipping after error (N.NNs)
+ sub_test.go:NNN: an error
+ sub_test.go:NNN: skipped`,
+ f: func(t *T) {
+ t.Error("an error")
+ t.Skip("skipped")
+ },
+ }, {
desc: "use Run to locally synchronize parallelism",
ok: true,
maxPar: 1,
@@ -177,57 +227,6 @@ func TestTRun(t *T) {
}
},
}, {
- desc: "run no more than *parallel tests concurrently",
- ok: true,
- maxPar: 4,
- f: func(t *T) {
- max := 0
- in := make(chan int)
- out := make(chan int)
- ctx := t.context
- t.Run("wait", func(t *T) {
- t.Run("controller", func(t *T) {
- // Verify sequential tests don't skew counts.
- t.Run("seq1", func(t *T) {})
- t.Run("seq2", func(t *T) {})
- t.Run("seq3", func(t *T) {})
- t.Parallel()
- for i := 0; i < 80; i++ {
- ctx.mu.Lock()
- if ctx.running > max {
- max = ctx.running
- }
- ctx.mu.Unlock()
- <-in
- // force a minimum to avoid a race, although it works
- // without it.
- if i >= ctx.maxParallel-2 { // max - this - 1
- out <- i
- }
- }
- close(out)
- })
- // Ensure we don't exceed the maximum even with nested parallelism.
- for i := 0; i < 2; i++ {
- t.Run("", func(t *T) {
- t.Parallel()
- for j := 0; j < 40; j++ {
- t.Run("", func(t *T) {
- t.Run("seq1", func(t *T) {})
- t.Run("seq2", func(t *T) {})
- t.Parallel()
- in <- j
- <-out
- })
- }
- })
- }
- })
- if max != ctx.maxParallel {
- realTest.Errorf("max: got %d; want: %d", max, ctx.maxParallel)
- }
- },
- }, {
desc: "alternate sequential and parallel",
// Sequential tests should partake in the counting of running threads.
// Otherwise, if one runs parallel subtests in sequential tests that are
@@ -301,14 +300,23 @@ func TestTRun(t *T) {
})
}
},
+ }, {
+ desc: "skip output",
+ ok: true,
+ maxPar: 4,
+ f: func(t *T) {
+ t.Skip()
+ },
}}
for _, tc := range testCases {
ctx := newTestContext(tc.maxPar, newMatcher(regexp.MatchString, "", ""))
+ buf := &bytes.Buffer{}
root := &T{
common: common{
signal: make(chan bool),
name: "Test",
- w: ioutil.Discard,
+ w: buf,
+ chatty: tc.chatty,
},
context: ctx,
}
@@ -324,6 +332,12 @@ func TestTRun(t *T) {
if ctx.running != 0 || ctx.numWaiting != 0 {
t.Errorf("%s:running and waiting non-zero: got %d and %d", tc.desc, ctx.running, ctx.numWaiting)
}
+ got := strings.TrimSpace(buf.String())
+ want := strings.TrimSpace(tc.output)
+ re := makeRegexp(want)
+ if ok, err := regexp.MatchString(re, got); !ok || err != nil {
+ t.Errorf("%s:ouput:\ngot:\n%s\nwant:\n%s", tc.desc, got, want)
+ }
}
}
@@ -336,6 +350,8 @@ func TestBRun(t *T) {
testCases := []struct {
desc string
failed bool
+ chatty bool
+ output string
f func(*B)
}{{
desc: "simulate sequential run of subbenchmarks.",
@@ -371,8 +387,43 @@ func TestBRun(t *T) {
}, {
desc: "failure carried over to root",
failed: true,
+ output: "--- FAIL: root",
f: func(b *B) { b.Fail() },
}, {
+ desc: "skipping without message, chatty",
+ chatty: true,
+ output: "--- SKIP: root",
+ f: func(b *B) { b.SkipNow() },
+ }, {
+ desc: "skipping with message, chatty",
+ chatty: true,
+ output: `
+--- SKIP: root
+ sub_test.go:NNN: skipping`,
+ f: func(b *B) { b.Skip("skipping") },
+ }, {
+ desc: "chatty with recursion",
+ chatty: true,
+ f: func(b *B) {
+ b.Run("", func(b *B) {
+ b.Run("", func(b *B) {})
+ })
+ },
+ }, {
+ desc: "skipping without message, not chatty",
+ f: func(b *B) { b.SkipNow() },
+ }, {
+ desc: "skipping after error",
+ failed: true,
+ output: `
+--- FAIL: root
+ sub_test.go:NNN: an error
+ sub_test.go:NNN: skipped`,
+ f: func(b *B) {
+ b.Error("an error")
+ b.Skip("skipped")
+ },
+ }, {
desc: "memory allocation",
f: func(b *B) {
const bufSize = 256
@@ -398,11 +449,15 @@ func TestBRun(t *T) {
}}
for _, tc := range testCases {
var ok bool
+ buf := &bytes.Buffer{}
// This is almost like the Benchmark function, except that we override
// the benchtime and catch the failure result of the subbenchmark.
root := &B{
common: common{
signal: make(chan bool),
+ name: "root",
+ w: buf,
+ chatty: tc.chatty,
},
benchFunc: func(b *B) { ok = b.Run("test", tc.f) }, // Use Run to catch failure.
benchTime: time.Microsecond,
@@ -418,5 +473,24 @@ func TestBRun(t *T) {
if root.result.N != 1 {
t.Errorf("%s: N for parent benchmark was %d; want 1", tc.desc, root.result.N)
}
+ got := strings.TrimSpace(buf.String())
+ want := strings.TrimSpace(tc.output)
+ re := makeRegexp(want)
+ if ok, err := regexp.MatchString(re, got); !ok || err != nil {
+ t.Errorf("%s:ouput:\ngot:\n%s\nwant:\n%s", tc.desc, got, want)
+ }
}
}
+
+func makeRegexp(s string) string {
+ s = strings.Replace(s, ":NNN:", `:\d\d\d:`, -1)
+ s = strings.Replace(s, "(N.NNs)", `\(\d*\.\d*s\)`, -1)
+ return s
+}
+
+func TestBenchmarkOutput(t *T) {
+ // Ensure Benchmark initialized common.w by invoking it with an error and
+ // normal case.
+ Benchmark(func(b *B) { b.Error("do not print this output") })
+ Benchmark(func(b *B) {})
+}
diff --git a/src/testing/testing.go b/src/testing/testing.go
index f9bb43b618..3a7a135a3c 100644
--- a/src/testing/testing.go
+++ b/src/testing/testing.go
@@ -199,6 +199,7 @@ type common struct {
mu sync.RWMutex // guards output and failed
output []byte // Output generated by test or benchmark.
w io.Writer // For flushToParent.
+ chatty bool // A copy of the chatty flag.
failed bool // Test or benchmark has failed.
skipped bool // Test of benchmark has been skipped.
finished bool
@@ -265,7 +266,6 @@ func (c *common) flushToParent(format string, args ...interface{}) {
defer p.mu.Unlock()
fmt.Fprintf(p.w, format, args...)
- fmt.Fprintln(p.w)
c.mu.Lock()
defer c.mu.Unlock()
@@ -562,13 +562,18 @@ func (t *T) Run(name string, f func(t *T)) bool {
name: testName,
parent: &t.common,
level: t.level + 1,
+ chatty: t.chatty,
},
context: t.context,
}
t.w = indenter{&t.common}
- if *chatty {
- fmt.Printf("=== RUN %s\n", t.name)
+ if t.chatty {
+ // Print directly to root's io.Writer so there is no delay.
+ root := t.parent
+ for ; root.parent != nil; root = root.parent {
+ }
+ fmt.Fprintf(root.w, "=== RUN %s\n", t.name)
}
// Instead of reducing the running count of this test before calling the
// tRunner and increasing it afterwards, we rely on tRunner keeping the
@@ -690,10 +695,10 @@ func (t *T) report() {
return
}
dstr := fmtDuration(t.duration)
- format := "--- %s: %s (%s)"
+ format := "--- %s: %s (%s)\n"
if t.Failed() {
t.flushToParent(format, "FAIL", t.name, dstr)
- } else if *chatty {
+ } else if t.chatty {
if t.Skipped() {
t.flushToParent(format, "SKIP", t.name, dstr)
} else {
@@ -716,6 +721,7 @@ func RunTests(matchString func(pat, str string) (bool, error), tests []InternalT
signal: make(chan bool),
barrier: make(chan bool),
w: os.Stdout,
+ chatty: *chatty,
},
context: ctx,
}
diff --git a/src/text/template/exec.go b/src/text/template/exec.go
index a169e62ab0..22881c6852 100644
--- a/src/text/template/exec.go
+++ b/src/text/template/exec.go
@@ -538,14 +538,14 @@ func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node,
return s.evalCall(dot, method, node, fieldName, args, final)
}
hasArgs := len(args) > 1 || final.IsValid()
- // It's not a method; must be a field of a struct or an element of a map. The receiver must not be nil.
- if isNil {
- s.errorf("nil pointer evaluating %s.%s", typ, fieldName)
- }
+ // It's not a method; must be a field of a struct or an element of a map.
switch receiver.Kind() {
case reflect.Struct:
tField, ok := receiver.Type().FieldByName(fieldName)
if ok {
+ if isNil {
+ s.errorf("nil pointer evaluating %s.%s", typ, fieldName)
+ }
field := receiver.FieldByIndex(tField.Index)
if tField.PkgPath != "" { // field is unexported
s.errorf("%s is an unexported field of struct type %s", fieldName, typ)
@@ -556,8 +556,10 @@ func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node,
}
return field
}
- s.errorf("%s is not a field of struct type %s", fieldName, typ)
case reflect.Map:
+ if isNil {
+ s.errorf("nil pointer evaluating %s.%s", typ, fieldName)
+ }
// If it's a map, attempt to use the field name as a key.
nameVal := reflect.ValueOf(fieldName)
if nameVal.Type().AssignableTo(receiver.Type().Key()) {
diff --git a/src/text/template/exec_test.go b/src/text/template/exec_test.go
index e507e917fe..bc2aa683ec 100644
--- a/src/text/template/exec_test.go
+++ b/src/text/template/exec_test.go
@@ -1280,3 +1280,20 @@ func TestBlock(t *testing.T) {
t.Errorf("got %q, want %q", got, want2)
}
}
+
+// Check that calling an invalid field on nil pointer prints
+// a field error instead of a distracting nil pointer error.
+// https://golang.org/issue/15125
+func TestMissingFieldOnNil(t *testing.T) {
+ tmpl := Must(New("tmpl").Parse("{{.MissingField}}"))
+ var d *T
+ err := tmpl.Execute(ioutil.Discard, d)
+ got := "<nil>"
+ if err != nil {
+ got = err.Error()
+ }
+ want := "can't evaluate field MissingField in type *template.T"
+ if !strings.HasSuffix(got, want) {
+ t.Errorf("got error %q, want %q", got, want)
+ }
+}
diff --git a/src/text/template/helper.go b/src/text/template/helper.go
index 787ca62e5f..9e0200c352 100644
--- a/src/text/template/helper.go
+++ b/src/text/template/helper.go
@@ -29,6 +29,11 @@ func Must(t *Template, err error) *Template {
// the named files. The returned template's name will have the base name and
// parsed contents of the first file. There must be at least one file.
// If an error occurs, parsing stops and the returned *Template is nil.
+//
+// When parsing multiple files with the same name in different directories,
+// the last one mentioned will be the one that results.
+// For instance, ParseFiles("a/foo", "b/foo") stores "b/foo" as the template
+// named "foo", while "a/foo" is unavailable.
func ParseFiles(filenames ...string) (*Template, error) {
return parseFiles(nil, filenames...)
}
@@ -41,6 +46,9 @@ func ParseFiles(filenames ...string) (*Template, error) {
// of the (base) names of the files. If it does not, depending on t's
// contents before calling ParseFiles, t.Execute may fail. In that
// case use t.ExecuteTemplate to execute a valid template.
+//
+// When parsing multiple files with the same name in different directories,
+// the last one mentioned will be the one that results.
func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
t.init()
return parseFiles(t, filenames...)
@@ -88,6 +96,9 @@ func parseFiles(t *Template, filenames ...string) (*Template, error) {
// returned template will have the (base) name and (parsed) contents of the
// first file matched by the pattern. ParseGlob is equivalent to calling
// ParseFiles with the list of files matched by the pattern.
+//
+// When parsing multiple files with the same name in different directories,
+// the last one mentioned will be the one that results.
func ParseGlob(pattern string) (*Template, error) {
return parseGlob(nil, pattern)
}
@@ -97,6 +108,9 @@ func ParseGlob(pattern string) (*Template, error) {
// processed by filepath.Glob and must match at least one file. ParseGlob is
// equivalent to calling t.ParseFiles with the list of files matched by the
// pattern.
+//
+// When parsing multiple files with the same name in different directories,
+// the last one mentioned will be the one that results.
func (t *Template) ParseGlob(pattern string) (*Template, error) {
t.init()
return parseGlob(t, pattern)
diff --git a/src/time/time.go b/src/time/time.go
index 4b9a0db730..d9dbd3449a 100644
--- a/src/time/time.go
+++ b/src/time/time.go
@@ -476,7 +476,7 @@ func (d Duration) String() string {
w--
switch {
case u == 0:
- return "0"
+ return "0s"
case u < uint64(Microsecond):
// print nanoseconds
prec = 0
@@ -606,7 +606,7 @@ func (d Duration) Hours() float64 {
// Add returns the time t+d.
func (t Time) Add(d Duration) Time {
t.sec += int64(d / 1e9)
- nsec := int32(t.nsec) + int32(d%1e9)
+ nsec := t.nsec + int32(d%1e9)
if nsec >= 1e9 {
t.sec++
nsec -= 1e9
@@ -623,7 +623,7 @@ func (t Time) Add(d Duration) Time {
// will be returned.
// To compute t-d for a duration d, use t.Add(-d).
func (t Time) Sub(u Time) Duration {
- d := Duration(t.sec-u.sec)*Second + Duration(int32(t.nsec)-int32(u.nsec))
+ d := Duration(t.sec-u.sec)*Second + Duration(t.nsec-u.nsec)
// Check for overflow or underflow.
switch {
case u.Add(d).Equal(t):
@@ -1125,7 +1125,7 @@ func (t Time) Round(d Duration) Time {
// but it's still here in case we change our minds.
func div(t Time, d Duration) (qmod2 int, r Duration) {
neg := false
- nsec := int32(t.nsec)
+ nsec := t.nsec
if t.sec < 0 {
// Operate on absolute value.
neg = true
@@ -1159,7 +1159,7 @@ func div(t Time, d Duration) (qmod2 int, r Duration) {
tmp := (sec >> 32) * 1e9
u1 := tmp >> 32
u0 := tmp << 32
- tmp = uint64(sec&0xFFFFFFFF) * 1e9
+ tmp = (sec & 0xFFFFFFFF) * 1e9
u0x, u0 := u0, u0+tmp
if u0 < u0x {
u1++
diff --git a/src/time/time_test.go b/src/time/time_test.go
index 5a5451b5b8..b7ebb37296 100644
--- a/src/time/time_test.go
+++ b/src/time/time_test.go
@@ -533,7 +533,7 @@ var durationTests = []struct {
str string
d Duration
}{
- {"0", 0},
+ {"0s", 0},
{"1ns", 1 * Nanosecond},
{"1.1µs", 1100 * Nanosecond},
{"2.2ms", 2200 * Microsecond},
diff --git a/src/time/zoneinfo_windows.go b/src/time/zoneinfo_windows.go
index bcb8ccd563..c753119d5d 100644
--- a/src/time/zoneinfo_windows.go
+++ b/src/time/zoneinfo_windows.go
@@ -83,7 +83,7 @@ func extractCAPS(desc string) string {
var short []rune
for _, c := range desc {
if 'A' <= c && c <= 'Z' {
- short = append(short, rune(c))
+ short = append(short, c)
}
}
return string(short)
diff --git a/src/unicode/letter.go b/src/unicode/letter.go
index 8443ee51a2..8aec920d22 100644
--- a/src/unicode/letter.go
+++ b/src/unicode/letter.go
@@ -217,7 +217,7 @@ func to(_case int, r rune, caseRange []CaseRange) rune {
m := lo + (hi-lo)/2
cr := caseRange[m]
if rune(cr.Lo) <= r && r <= rune(cr.Hi) {
- delta := rune(cr.Delta[_case])
+ delta := cr.Delta[_case]
if delta > MaxRune {
// In an Upper-Lower sequence, which always starts with
// an UpperCase letter, the real deltas always look like:
@@ -332,6 +332,10 @@ type foldPair struct {
// SimpleFold('1') = '1'
//
func SimpleFold(r rune) rune {
+ if int(r) < len(asciiFold) {
+ return rune(asciiFold[r])
+ }
+
// Consult caseOrbit table for special cases.
lo := 0
hi := len(caseOrbit)
diff --git a/src/unicode/maketables.go b/src/unicode/maketables.go
index 328c75ed63..f364515c90 100644
--- a/src/unicode/maketables.go
+++ b/src/unicode/maketables.go
@@ -1172,6 +1172,7 @@ func printCasefold() {
}
}
+ printAsciiFold()
printCaseOrbit()
// Tables of category and script folding exceptions: code points
@@ -1269,6 +1270,25 @@ var comment = map[string]string{
"// If there is no entry for a script name, there are no such points.\n",
}
+func printAsciiFold() {
+ printf("var asciiFold = [MaxASCII + 1]uint16{\n")
+ for i := rune(0); i <= unicode.MaxASCII; i++ {
+ c := chars[i]
+ f := c.caseOrbit
+ if f == 0 {
+ if c.lowerCase != i && c.lowerCase != 0 {
+ f = c.lowerCase
+ } else if c.upperCase != i && c.upperCase != 0 {
+ f = c.upperCase
+ } else {
+ f = i
+ }
+ }
+ printf("\t0x%04X,\n", f)
+ }
+ printf("}\n\n")
+}
+
func printCaseOrbit() {
if *test {
for j := range chars {
diff --git a/src/unicode/tables.go b/src/unicode/tables.go
index 8bb42062f9..c04d69a6ff 100644
--- a/src/unicode/tables.go
+++ b/src/unicode/tables.go
@@ -6834,6 +6834,137 @@ var properties = [MaxLatin1 + 1]uint8{
0xFF: pLl | pp, // 'ÿ'
}
+var asciiFold = [MaxASCII + 1]uint16{
+ 0x0000,
+ 0x0001,
+ 0x0002,
+ 0x0003,
+ 0x0004,
+ 0x0005,
+ 0x0006,
+ 0x0007,
+ 0x0008,
+ 0x0009,
+ 0x000A,
+ 0x000B,
+ 0x000C,
+ 0x000D,
+ 0x000E,
+ 0x000F,
+ 0x0010,
+ 0x0011,
+ 0x0012,
+ 0x0013,
+ 0x0014,
+ 0x0015,
+ 0x0016,
+ 0x0017,
+ 0x0018,
+ 0x0019,
+ 0x001A,
+ 0x001B,
+ 0x001C,
+ 0x001D,
+ 0x001E,
+ 0x001F,
+ 0x0020,
+ 0x0021,
+ 0x0022,
+ 0x0023,
+ 0x0024,
+ 0x0025,
+ 0x0026,
+ 0x0027,
+ 0x0028,
+ 0x0029,
+ 0x002A,
+ 0x002B,
+ 0x002C,
+ 0x002D,
+ 0x002E,
+ 0x002F,
+ 0x0030,
+ 0x0031,
+ 0x0032,
+ 0x0033,
+ 0x0034,
+ 0x0035,
+ 0x0036,
+ 0x0037,
+ 0x0038,
+ 0x0039,
+ 0x003A,
+ 0x003B,
+ 0x003C,
+ 0x003D,
+ 0x003E,
+ 0x003F,
+ 0x0040,
+ 0x0061,
+ 0x0062,
+ 0x0063,
+ 0x0064,
+ 0x0065,
+ 0x0066,
+ 0x0067,
+ 0x0068,
+ 0x0069,
+ 0x006A,
+ 0x006B,
+ 0x006C,
+ 0x006D,
+ 0x006E,
+ 0x006F,
+ 0x0070,
+ 0x0071,
+ 0x0072,
+ 0x0073,
+ 0x0074,
+ 0x0075,
+ 0x0076,
+ 0x0077,
+ 0x0078,
+ 0x0079,
+ 0x007A,
+ 0x005B,
+ 0x005C,
+ 0x005D,
+ 0x005E,
+ 0x005F,
+ 0x0060,
+ 0x0041,
+ 0x0042,
+ 0x0043,
+ 0x0044,
+ 0x0045,
+ 0x0046,
+ 0x0047,
+ 0x0048,
+ 0x0049,
+ 0x004A,
+ 0x212A,
+ 0x004C,
+ 0x004D,
+ 0x004E,
+ 0x004F,
+ 0x0050,
+ 0x0051,
+ 0x0052,
+ 0x017F,
+ 0x0054,
+ 0x0055,
+ 0x0056,
+ 0x0057,
+ 0x0058,
+ 0x0059,
+ 0x005A,
+ 0x007B,
+ 0x007C,
+ 0x007D,
+ 0x007E,
+ 0x007F,
+}
+
var caseOrbit = []foldPair{
{0x004B, 0x006B},
{0x0053, 0x0073},