aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAustin Clements <austin@google.com>2014-10-22 11:21:16 -0400
committerAustin Clements <austin@google.com>2014-10-22 11:21:16 -0400
commit32082501859c5c3c61913df5791cafbbb45c2519 (patch)
tree365cfa998cf0ef09335922c75b50009b315ca8e9
parent04d5796b2988a20175b8cb03eb7080cf4cca2179 (diff)
parent36ca636fab865cb8d5d01927d274ed54153c4e73 (diff)
downloadgo-32082501859c5c3c61913df5791cafbbb45c2519.tar.gz
go-32082501859c5c3c61913df5791cafbbb45c2519.zip
[dev.power64] build: merge default into dev.power64
LGTM=rsc R=rsc CC=golang-codereviews https://golang.org/cl/160200044
-rw-r--r--.hgtags3
-rw-r--r--AUTHORS9
-rw-r--r--CONTRIBUTORS11
-rw-r--r--api/except.txt1
-rw-r--r--api/next.txt24
-rw-r--r--doc/asm.html2
-rw-r--r--doc/cmd.html2
-rw-r--r--doc/devel/release.html7
-rw-r--r--doc/go1.3.html9
-rw-r--r--doc/go1.4.txt15
-rw-r--r--doc/go1compat.html15
-rw-r--r--doc/go_spec.html475
-rw-r--r--include/link.h1
-rw-r--r--lib/codereview/codereview.cfg1
-rw-r--r--lib/codereview/codereview.py12
-rw-r--r--misc/cgo/errors/issue7757.go14
-rw-r--r--misc/cgo/errors/issue8442.go17
-rwxr-xr-xmisc/cgo/errors/test.bash2
-rw-r--r--misc/cgo/test/backdoor/runtime.c14
-rw-r--r--misc/cgo/test/backdoor/thunk.s16
-rw-r--r--misc/cgo/test/buildid_linux.go77
-rw-r--r--misc/cgo/test/callback.go17
-rw-r--r--misc/cgo/test/cgo_linux_test.go5
-rw-r--r--misc/cgo/test/cgo_test.go2
-rw-r--r--misc/cgo/test/issue5242.go31
-rw-r--r--misc/cgo/test/issue5548.go9
-rw-r--r--misc/cgo/test/issue7695_test.go3
-rw-r--r--misc/cgo/test/issue8092.go36
-rw-r--r--misc/cgo/test/issue8428.go52
-rw-r--r--misc/cgo/testcdefs/cdefstest.go3
-rw-r--r--misc/cgo/testcdefs/main.c9
-rw-r--r--misc/makerelease/makerelease.go7
-rw-r--r--misc/nacl/README89
-rw-r--r--misc/nacl/testzip.proto5
-rwxr-xr-xsrc/androidtest.bash4
-rw-r--r--src/cmd/5c/cgen.c34
-rw-r--r--src/cmd/5c/gc.h4
-rw-r--r--src/cmd/5c/sgen.c2
-rw-r--r--src/cmd/5c/swt.c9
-rw-r--r--src/cmd/5c/txt.c40
-rw-r--r--src/cmd/5g/cgen.c74
-rw-r--r--src/cmd/5g/gsubr.c1
-rw-r--r--src/cmd/5g/reg.c1
-rw-r--r--src/cmd/5l/asm.c4
-rw-r--r--src/cmd/6a/lex.c34
-rw-r--r--src/cmd/6c/cgen.c34
-rw-r--r--src/cmd/6c/gc.h4
-rw-r--r--src/cmd/6c/sgen.c7
-rw-r--r--src/cmd/6c/swt.c9
-rw-r--r--src/cmd/6c/txt.c40
-rw-r--r--src/cmd/6g/cgen.c8
-rw-r--r--src/cmd/6g/gg.h1
-rw-r--r--src/cmd/6g/ggen.c2
-rw-r--r--src/cmd/6g/gsubr.c7
-rw-r--r--src/cmd/6g/peep.c5
-rw-r--r--src/cmd/6g/reg.c3
-rw-r--r--src/cmd/6l/asm.c25
-rw-r--r--src/cmd/8a/lex.c34
-rw-r--r--src/cmd/8c/cgen.c34
-rw-r--r--src/cmd/8c/gc.h4
-rw-r--r--src/cmd/8c/sgen.c2
-rw-r--r--src/cmd/8c/swt.c9
-rw-r--r--src/cmd/8c/txt.c40
-rw-r--r--src/cmd/8g/cgen.c81
-rw-r--r--src/cmd/8g/ggen.c2
-rw-r--r--src/cmd/8g/gsubr.c1
-rw-r--r--src/cmd/8g/peep.c5
-rw-r--r--src/cmd/8g/reg.c1
-rw-r--r--src/cmd/8l/asm.c8
-rw-r--r--src/cmd/9c/cgen.c30
-rw-r--r--src/cmd/9c/gc.h5
-rw-r--r--src/cmd/9c/sgen.c2
-rw-r--r--src/cmd/9c/swt.c9
-rw-r--r--src/cmd/9c/txt.c40
-rw-r--r--src/cmd/9g/cgen.c1
-rw-r--r--src/cmd/9g/gg.h1
-rw-r--r--src/cmd/9g/gsubr.c7
-rw-r--r--src/cmd/9l/asm.c4
-rw-r--r--src/cmd/addr2line/main.go159
-rw-r--r--src/cmd/api/goapi.go46
-rw-r--r--src/cmd/api/run.go4
-rw-r--r--src/cmd/cc/cc.h2
-rw-r--r--src/cmd/cc/cc.y1
-rw-r--r--src/cmd/cc/dcl.c16
-rw-r--r--src/cmd/cc/godefs.c81
-rw-r--r--src/cmd/cc/pgen.c45
-rw-r--r--src/cmd/cgo/gcc.go94
-rw-r--r--src/cmd/cgo/out.go202
-rw-r--r--src/cmd/dist/a.h4
-rw-r--r--src/cmd/dist/buf.c2
-rw-r--r--src/cmd/dist/build.c47
-rw-r--r--src/cmd/dist/buildruntime.c58
-rw-r--r--src/cmd/dist/goc2c.c833
-rw-r--r--src/cmd/dist/plan9.c6
-rw-r--r--src/cmd/dist/unix.c16
-rw-r--r--src/cmd/dist/windows.c4
-rw-r--r--src/cmd/fix/doc.go2
-rw-r--r--src/cmd/gc/builtin.c18
-rw-r--r--src/cmd/gc/bv.c5
-rw-r--r--src/cmd/gc/const.c8
-rw-r--r--src/cmd/gc/fmt.c2
-rw-r--r--src/cmd/gc/gen.c55
-rw-r--r--src/cmd/gc/go.h3
-rw-r--r--src/cmd/gc/mparith1.c2
-rw-r--r--src/cmd/gc/mparith3.c4
-rw-r--r--src/cmd/gc/order.c26
-rw-r--r--src/cmd/gc/plive.c7
-rw-r--r--src/cmd/gc/popt.c2
-rw-r--r--src/cmd/gc/racewalk.c6
-rw-r--r--src/cmd/gc/reflect.c40
-rw-r--r--src/cmd/gc/runtime.go19
-rw-r--r--src/cmd/gc/select.c6
-rw-r--r--src/cmd/gc/subr.c113
-rw-r--r--src/cmd/gc/typecheck.c15
-rw-r--r--src/cmd/gc/walk.c105
-rw-r--r--src/cmd/go/build.go24
-rw-r--r--src/cmd/go/doc.go97
-rw-r--r--src/cmd/go/generate.go355
-rw-r--r--src/cmd/go/generate_test.go48
-rw-r--r--src/cmd/go/get.go4
-rw-r--r--src/cmd/go/main.go1
-rw-r--r--src/cmd/go/pkg.go91
-rwxr-xr-xsrc/cmd/go/test.bash90
-rw-r--r--src/cmd/go/testdata/generate/test1.go13
-rw-r--r--src/cmd/go/testdata/generate/test2.go10
-rw-r--r--src/cmd/go/testdata/generate/test3.go9
-rw-r--r--src/cmd/go/testdata/importcom/bad.go3
-rw-r--r--src/cmd/go/testdata/importcom/conflict.go3
-rw-r--r--src/cmd/go/testdata/importcom/src/bad/bad.go1
-rw-r--r--src/cmd/go/testdata/importcom/src/conflict/a.go1
-rw-r--r--src/cmd/go/testdata/importcom/src/conflict/b.go1
-rw-r--r--src/cmd/go/testdata/importcom/src/works/x/x.go1
-rw-r--r--src/cmd/go/testdata/importcom/src/works/x/x1.go1
-rw-r--r--src/cmd/go/testdata/importcom/src/wrongplace/x.go1
-rw-r--r--src/cmd/go/testdata/importcom/works.go3
-rw-r--r--src/cmd/go/testdata/importcom/wrongplace.go3
-rw-r--r--src/cmd/go/testdata/testinternal/p.go3
-rw-r--r--src/cmd/go/testdata/testinternal2/p.go3
-rw-r--r--src/cmd/go/testdata/testinternal2/x/y/z/internal/w/w.go1
-rw-r--r--src/cmd/go/vcs_test.go5
-rw-r--r--src/cmd/gofmt/gofmt.go11
-rw-r--r--src/cmd/gofmt/gofmt_test.go123
-rw-r--r--src/cmd/gofmt/testdata/composites.golden2
-rw-r--r--src/cmd/gofmt/testdata/composites.input2
-rw-r--r--src/cmd/gofmt/testdata/crlf.golden1
-rw-r--r--src/cmd/gofmt/testdata/crlf.input1
-rw-r--r--src/cmd/gofmt/testdata/emptydecl.golden4
-rw-r--r--src/cmd/gofmt/testdata/emptydecl.input4
-rw-r--r--src/cmd/gofmt/testdata/ranges.golden2
-rw-r--r--src/cmd/gofmt/testdata/ranges.input2
-rw-r--r--src/cmd/gofmt/testdata/rewrite1.golden2
-rw-r--r--src/cmd/gofmt/testdata/rewrite1.input2
-rw-r--r--src/cmd/gofmt/testdata/rewrite2.golden2
-rw-r--r--src/cmd/gofmt/testdata/rewrite2.input2
-rw-r--r--src/cmd/gofmt/testdata/rewrite3.golden2
-rw-r--r--src/cmd/gofmt/testdata/rewrite3.input2
-rw-r--r--src/cmd/gofmt/testdata/rewrite4.golden2
-rw-r--r--src/cmd/gofmt/testdata/rewrite4.input2
-rw-r--r--src/cmd/gofmt/testdata/rewrite5.golden2
-rw-r--r--src/cmd/gofmt/testdata/rewrite5.input2
-rw-r--r--src/cmd/gofmt/testdata/rewrite6.golden2
-rw-r--r--src/cmd/gofmt/testdata/rewrite6.input2
-rw-r--r--src/cmd/gofmt/testdata/rewrite7.golden2
-rw-r--r--src/cmd/gofmt/testdata/rewrite7.input2
-rw-r--r--src/cmd/gofmt/testdata/rewrite8.golden2
-rw-r--r--src/cmd/gofmt/testdata/rewrite8.input2
-rw-r--r--src/cmd/gofmt/testdata/slices1.golden2
-rw-r--r--src/cmd/gofmt/testdata/slices1.input2
-rw-r--r--src/cmd/gofmt/testdata/slices2.golden2
-rw-r--r--src/cmd/gofmt/testdata/slices2.input2
-rw-r--r--src/cmd/gofmt/testdata/stdin1.golden2
-rw-r--r--src/cmd/gofmt/testdata/stdin1.golden.gofmt3
-rw-r--r--src/cmd/gofmt/testdata/stdin1.input2
-rw-r--r--src/cmd/gofmt/testdata/stdin1.input.gofmt3
-rw-r--r--src/cmd/gofmt/testdata/stdin2.golden2
-rw-r--r--src/cmd/gofmt/testdata/stdin2.golden.gofmt10
-rw-r--r--src/cmd/gofmt/testdata/stdin2.input2
-rw-r--r--src/cmd/gofmt/testdata/stdin2.input.gofmt11
-rw-r--r--src/cmd/gofmt/testdata/stdin3.golden1
-rw-r--r--src/cmd/gofmt/testdata/stdin3.golden.gofmt7
-rw-r--r--src/cmd/gofmt/testdata/stdin3.input1
-rw-r--r--src/cmd/gofmt/testdata/stdin3.input.gofmt7
-rw-r--r--src/cmd/gofmt/testdata/stdin4.golden2
-rw-r--r--src/cmd/gofmt/testdata/stdin4.golden.gofmt3
-rw-r--r--src/cmd/gofmt/testdata/stdin4.input2
-rw-r--r--src/cmd/gofmt/testdata/stdin4.input.gofmt3
-rw-r--r--src/cmd/gofmt/testdata/stdin5.golden3
-rw-r--r--src/cmd/gofmt/testdata/stdin5.input3
-rw-r--r--src/cmd/internal/objfile/elf.go (renamed from src/cmd/nm/elf.go)44
-rw-r--r--src/cmd/internal/objfile/goobj.go (renamed from src/cmd/nm/goobj.go)35
-rw-r--r--src/cmd/internal/objfile/macho.go (renamed from src/cmd/nm/macho.go)49
-rw-r--r--src/cmd/internal/objfile/objfile.go72
-rw-r--r--src/cmd/internal/objfile/pe.go170
-rw-r--r--src/cmd/internal/objfile/plan9obj.go124
-rw-r--r--src/cmd/ld/data.c122
-rw-r--r--src/cmd/ld/lib.c57
-rw-r--r--src/cmd/ld/lib.h5
-rw-r--r--src/cmd/ld/pcln.c4
-rw-r--r--src/cmd/ld/pobj.c2
-rw-r--r--src/cmd/ld/symtab.c46
-rw-r--r--src/cmd/link/auto.go40
-rw-r--r--src/cmd/link/layout.go6
-rw-r--r--src/cmd/link/pclntab.go4
-rw-r--r--src/cmd/link/pclntab_test.go8
-rw-r--r--src/cmd/link/runtime.go4
-rw-r--r--src/cmd/link/testdata/Makefile2
-rw-r--r--src/cmd/link/testdata/autosection.6bin890 -> 942 bytes
-rw-r--r--src/cmd/link/testdata/autosection.s24
-rw-r--r--src/cmd/link/testdata/autoweak.6bin427 -> 387 bytes
-rw-r--r--src/cmd/link/testdata/dead.6bin1066 -> 854 bytes
-rw-r--r--src/cmd/link/testdata/dead.s3
-rw-r--r--src/cmd/link/testdata/genpcln.go2
-rw-r--r--src/cmd/link/testdata/hello.6bin273 -> 233 bytes
-rw-r--r--src/cmd/link/testdata/layout.6bin431 -> 391 bytes
-rw-r--r--src/cmd/link/testdata/layout.s2
-rw-r--r--src/cmd/link/testdata/link.hello.darwin.amd6411
-rw-r--r--src/cmd/link/testdata/pclntab.6bin4611 -> 3809 bytes
-rw-r--r--src/cmd/link/testdata/pclntab.s2
-rw-r--r--src/cmd/nm/nm.go57
-rw-r--r--src/cmd/nm/nm_test.go4
-rw-r--r--src/cmd/nm/pe.go98
-rw-r--r--src/cmd/nm/plan9obj.go48
-rw-r--r--src/cmd/objdump/main.go38
-rw-r--r--src/cmd/yacc/Makefile12
-rw-r--r--src/cmd/yacc/doc.go5
-rw-r--r--src/cmd/yacc/testdata/expr/README20
-rw-r--r--src/cmd/yacc/testdata/expr/expr.y (renamed from src/cmd/yacc/expr.y)5
-rw-r--r--src/cmd/yacc/testdata/expr/main.go15
-rw-r--r--src/liblink/asm5.c6
-rw-r--r--src/liblink/asm6.c52
-rw-r--r--src/liblink/asm8.c2
-rw-r--r--src/liblink/obj5.c119
-rw-r--r--src/liblink/obj6.c100
-rw-r--r--src/liblink/obj8.c78
-rw-r--r--src/liblink/objfile.c18
-rw-r--r--src/make.bat2
-rw-r--r--src/pkg/archive/zip/writer.go6
-rw-r--r--src/pkg/archive/zip/writer_test.go19
-rw-r--r--src/pkg/bytes/bytes.go3
-rw-r--r--src/pkg/compress/bzip2/bzip2_test.go38
-rw-r--r--src/pkg/compress/bzip2/move_to_front.go79
-rw-r--r--src/pkg/compress/flate/fixedhuff.go2
-rw-r--r--src/pkg/compress/flate/gen.go55
-rw-r--r--src/pkg/compress/flate/inflate.go2
-rw-r--r--src/pkg/compress/gzip/gzip.go3
-rw-r--r--src/pkg/compress/zlib/writer.go3
-rw-r--r--src/pkg/crypto/aes/asm_amd64.s2
-rw-r--r--src/pkg/crypto/cipher/cfb_test.go77
-rw-r--r--src/pkg/crypto/crypto.go41
-rw-r--r--src/pkg/crypto/ecdsa/ecdsa.go24
-rw-r--r--src/pkg/crypto/md5/gen.go23
-rw-r--r--src/pkg/crypto/md5/md5.go2
-rw-r--r--src/pkg/crypto/md5/md5block.go2
-rw-r--r--src/pkg/crypto/md5/md5block_386.s2
-rw-r--r--src/pkg/crypto/md5/md5block_amd64.s2
-rw-r--r--src/pkg/crypto/md5/md5block_amd64p32.s2
-rw-r--r--src/pkg/crypto/md5/md5block_arm.s2
-rw-r--r--src/pkg/crypto/rand/rand_linux.go39
-rw-r--r--src/pkg/crypto/rand/rand_unix.go11
-rw-r--r--src/pkg/crypto/rc4/rc4_386.s2
-rw-r--r--src/pkg/crypto/rc4/rc4_amd64.s2
-rw-r--r--src/pkg/crypto/rc4/rc4_amd64p32.s2
-rw-r--r--src/pkg/crypto/rc4/rc4_arm.s2
-rw-r--r--src/pkg/crypto/rsa/pss.go15
-rw-r--r--src/pkg/crypto/rsa/rsa.go19
-rw-r--r--src/pkg/crypto/sha1/sha1block_386.s2
-rw-r--r--src/pkg/crypto/sha1/sha1block_amd64.s2
-rw-r--r--src/pkg/crypto/sha1/sha1block_amd64p32.s2
-rw-r--r--src/pkg/crypto/sha1/sha1block_arm.s2
-rw-r--r--src/pkg/crypto/sha256/sha256block_amd64.s2
-rw-r--r--src/pkg/crypto/sha512/sha512block_amd64.s2
-rw-r--r--src/pkg/crypto/tls/common.go15
-rw-r--r--src/pkg/crypto/tls/conn.go6
-rw-r--r--src/pkg/crypto/tls/handshake_client.go44
-rw-r--r--src/pkg/crypto/tls/handshake_client_test.go2
-rw-r--r--src/pkg/crypto/tls/handshake_server.go14
-rw-r--r--src/pkg/crypto/tls/tls_test.go45
-rw-r--r--src/pkg/crypto/x509/verify.go8
-rw-r--r--src/pkg/database/sql/convert_test.go37
-rw-r--r--src/pkg/database/sql/sql.go161
-rw-r--r--src/pkg/database/sql/sql_test.go49
-rw-r--r--src/pkg/debug/dwarf/type.go54
-rw-r--r--src/pkg/debug/elf/elf.go338
-rw-r--r--src/pkg/debug/elf/file.go50
-rw-r--r--src/pkg/debug/elf/file_test.go6
-rw-r--r--src/pkg/debug/elf/testdata/go-relocation-test-gcc482-aarch64.objbin0 -> 3392 bytes
-rw-r--r--src/pkg/debug/goobj/read.go6
-rw-r--r--src/pkg/debug/gosym/symtab.go2
-rw-r--r--src/pkg/debug/pe/file_test.go31
-rw-r--r--src/pkg/debug/pe/testdata/gcc-amd64-mingw-execbin37376 -> 273083 bytes
-rw-r--r--src/pkg/encoding/gob/timing_test.go62
-rw-r--r--src/pkg/encoding/json/encode.go8
-rw-r--r--src/pkg/encoding/json/encode_test.go26
-rw-r--r--src/pkg/encoding/xml/xml.go11
-rw-r--r--src/pkg/encoding/xml/xml_test.go31
-rw-r--r--src/pkg/flag/flag.go5
-rw-r--r--src/pkg/fmt/fmt_test.go29
-rw-r--r--src/pkg/fmt/print.go10
-rw-r--r--src/pkg/fmt/scan.go1
-rw-r--r--src/pkg/fmt/scan_test.go32
-rw-r--r--src/pkg/go/build/build.go168
-rw-r--r--src/pkg/go/build/build_test.go10
-rw-r--r--src/pkg/go/build/deps_test.go4
-rw-r--r--src/pkg/go/doc/headscan.go15
-rw-r--r--src/pkg/go/parser/error_test.go3
-rw-r--r--src/pkg/go/parser/parser.go51
-rw-r--r--src/pkg/go/parser/short_test.go1
-rw-r--r--src/pkg/go/token/position.go51
-rw-r--r--src/pkg/go/token/position_test.go101
-rw-r--r--src/pkg/hash/crc32/crc32_amd64.s2
-rw-r--r--src/pkg/hash/crc32/crc32_amd64p32.s2
-rw-r--r--src/pkg/html/template/error.go16
-rw-r--r--src/pkg/html/template/escape.go74
-rw-r--r--src/pkg/html/template/escape_test.go23
-rw-r--r--src/pkg/html/template/template.go40
-rw-r--r--src/pkg/html/template/transition.go12
-rw-r--r--src/pkg/image/color/palette/gen.go96
-rw-r--r--src/pkg/image/color/palette/generate.go8
-rw-r--r--src/pkg/image/color/palette/palette.go3
-rw-r--r--src/pkg/image/png/writer.go60
-rw-r--r--src/pkg/image/png/writer_test.go29
-rw-r--r--src/pkg/internal/syscall/getrandom_linux.go56
-rw-r--r--src/pkg/io/io.go8
-rw-r--r--src/pkg/math/abs_386.s2
-rw-r--r--src/pkg/math/abs_amd64.s2
-rw-r--r--src/pkg/math/abs_arm.s2
-rw-r--r--src/pkg/math/abs_power64x.s2
-rw-r--r--src/pkg/math/asin_386.s2
-rw-r--r--src/pkg/math/asin_amd64.s2
-rw-r--r--src/pkg/math/asin_arm.s2
-rw-r--r--src/pkg/math/atan2_386.s2
-rw-r--r--src/pkg/math/atan2_amd64.s2
-rw-r--r--src/pkg/math/atan2_arm.s2
-rw-r--r--src/pkg/math/atan_386.s2
-rw-r--r--src/pkg/math/atan_amd64.s2
-rw-r--r--src/pkg/math/atan_arm.s2
-rw-r--r--src/pkg/math/big/arith_386.s2
-rw-r--r--src/pkg/math/big/arith_amd64.s2
-rw-r--r--src/pkg/math/big/arith_amd64p32.s2
-rw-r--r--src/pkg/math/big/arith_arm.s2
-rw-r--r--src/pkg/math/big/arith_power64x.s2
-rw-r--r--src/pkg/math/dim_386.s2
-rw-r--r--src/pkg/math/dim_amd64.s2
-rw-r--r--src/pkg/math/dim_arm.s2
-rw-r--r--src/pkg/math/exp2_386.s2
-rw-r--r--src/pkg/math/exp2_amd64.s2
-rw-r--r--src/pkg/math/exp2_arm.s2
-rw-r--r--src/pkg/math/exp_386.s2
-rw-r--r--src/pkg/math/exp_amd64.s2
-rw-r--r--src/pkg/math/exp_arm.s2
-rw-r--r--src/pkg/math/expm1_386.s2
-rw-r--r--src/pkg/math/expm1_amd64.s2
-rw-r--r--src/pkg/math/expm1_arm.s2
-rw-r--r--src/pkg/math/floor_386.s2
-rw-r--r--src/pkg/math/floor_amd64.s2
-rw-r--r--src/pkg/math/floor_arm.s2
-rw-r--r--src/pkg/math/frexp_386.s2
-rw-r--r--src/pkg/math/frexp_amd64.s2
-rw-r--r--src/pkg/math/frexp_arm.s2
-rw-r--r--src/pkg/math/hypot_386.s2
-rw-r--r--src/pkg/math/hypot_amd64.s2
-rw-r--r--src/pkg/math/hypot_arm.s2
-rw-r--r--src/pkg/math/ldexp_386.s2
-rw-r--r--src/pkg/math/ldexp_amd64.s2
-rw-r--r--src/pkg/math/ldexp_arm.s2
-rw-r--r--src/pkg/math/log10_386.s2
-rw-r--r--src/pkg/math/log10_amd64.s2
-rw-r--r--src/pkg/math/log10_arm.s2
-rw-r--r--src/pkg/math/log1p_386.s2
-rw-r--r--src/pkg/math/log1p_amd64.s2
-rw-r--r--src/pkg/math/log1p_arm.s2
-rw-r--r--src/pkg/math/log_386.s2
-rw-r--r--src/pkg/math/log_amd64.s2
-rw-r--r--src/pkg/math/log_arm.s2
-rw-r--r--src/pkg/math/mod_386.s2
-rw-r--r--src/pkg/math/mod_amd64.s2
-rw-r--r--src/pkg/math/mod_arm.s2
-rw-r--r--src/pkg/math/modf_386.s2
-rw-r--r--src/pkg/math/modf_amd64.s2
-rw-r--r--src/pkg/math/modf_arm.s2
-rw-r--r--src/pkg/math/remainder_386.s2
-rw-r--r--src/pkg/math/remainder_amd64.s2
-rw-r--r--src/pkg/math/remainder_arm.s2
-rw-r--r--src/pkg/math/sin_386.s2
-rw-r--r--src/pkg/math/sin_amd64.s2
-rw-r--r--src/pkg/math/sin_arm.s2
-rw-r--r--src/pkg/math/sincos_386.s2
-rw-r--r--src/pkg/math/sincos_amd64.s2
-rw-r--r--src/pkg/math/sincos_arm.s2
-rw-r--r--src/pkg/math/sqrt_386.s2
-rw-r--r--src/pkg/math/sqrt_amd64.s2
-rw-r--r--src/pkg/math/sqrt_arm.s2
-rw-r--r--src/pkg/math/tan_386.s2
-rw-r--r--src/pkg/math/tan_amd64.s2
-rw-r--r--src/pkg/math/tan_arm.s2
-rw-r--r--src/pkg/mime/type.go84
-rw-r--r--src/pkg/mime/type_plan9.go2
-rw-r--r--src/pkg/mime/type_test.go41
-rw-r--r--src/pkg/mime/type_unix.go2
-rw-r--r--src/pkg/mime/type_windows.go2
-rw-r--r--src/pkg/net/dnsclient_unix.go95
-rw-r--r--src/pkg/net/dnsclient_unix_test.go31
-rw-r--r--src/pkg/net/dnsconfig_unix.go37
-rw-r--r--src/pkg/net/dnsconfig_unix_test.go91
-rw-r--r--src/pkg/net/http/export_test.go6
-rw-r--r--src/pkg/net/http/fs.go2
-rw-r--r--src/pkg/net/http/httptest/server_test.go23
-rw-r--r--src/pkg/net/http/httputil/reverseproxy.go16
-rw-r--r--src/pkg/net/http/readrequest_test.go41
-rw-r--r--src/pkg/net/http/request.go31
-rw-r--r--src/pkg/net/http/request_test.go70
-rw-r--r--src/pkg/net/http/requestwrite_test.go4
-rw-r--r--src/pkg/net/http/serve_test.go77
-rw-r--r--src/pkg/net/http/server.go9
-rw-r--r--src/pkg/net/http/transfer.go10
-rw-r--r--src/pkg/net/http/transport.go82
-rw-r--r--src/pkg/net/http/transport_test.go64
-rw-r--r--src/pkg/net/lookup_test.go164
-rw-r--r--src/pkg/net/lookup_windows.go79
-rw-r--r--src/pkg/net/lookup_windows_test.go243
-rw-r--r--src/pkg/net/net_windows_test.go2
-rw-r--r--src/pkg/net/sockopt_bsd.go2
-rw-r--r--src/pkg/net/testdata/domain-resolv.conf5
-rw-r--r--src/pkg/net/testdata/empty-resolv.conf1
-rw-r--r--src/pkg/net/testdata/resolv.conf6
-rw-r--r--src/pkg/net/testdata/search-resolv.conf5
-rw-r--r--src/pkg/net/url/url.go11
-rw-r--r--src/pkg/net/url/url_test.go56
-rw-r--r--src/pkg/os/file_windows.go6
-rw-r--r--src/pkg/os/getwd.go24
-rw-r--r--src/pkg/os/os_test.go9
-rw-r--r--src/pkg/os/os_windows_test.go54
-rw-r--r--src/pkg/os/signal/sig.s2
-rw-r--r--src/pkg/os/stat_windows.go6
-rw-r--r--src/pkg/path/filepath/path.go11
-rw-r--r--src/pkg/path/filepath/path_plan9.go4
-rw-r--r--src/pkg/path/filepath/path_test.go21
-rw-r--r--src/pkg/path/filepath/path_unix.go4
-rw-r--r--src/pkg/path/filepath/path_windows.go5
-rw-r--r--src/pkg/path/path.go10
-rw-r--r--src/pkg/reflect/all_test.go21
-rw-r--r--src/pkg/reflect/asm_386.s23
-rw-r--r--src/pkg/reflect/asm_amd64.s23
-rw-r--r--src/pkg/reflect/asm_amd64p32.s23
-rw-r--r--src/pkg/reflect/asm_arm.s23
-rw-r--r--src/pkg/reflect/asm_power64x.s23
-rw-r--r--src/pkg/reflect/type.go21
-rw-r--r--src/pkg/reflect/value.go94
-rw-r--r--src/pkg/regexp/onepass.go5
-rw-r--r--src/pkg/regexp/syntax/parse.go2
-rw-r--r--src/pkg/runtime/alg.go291
-rw-r--r--src/pkg/runtime/alg.goc423
-rw-r--r--src/pkg/runtime/arch_386.go12
-rw-r--r--src/pkg/runtime/arch_amd64.go12
-rw-r--r--src/pkg/runtime/arch_amd64p32.go12
-rw-r--r--src/pkg/runtime/arch_arm.go12
-rw-r--r--src/pkg/runtime/arch_arm.h2
-rw-r--r--src/pkg/runtime/asm_386.s329
-rw-r--r--src/pkg/runtime/asm_amd64.s335
-rw-r--r--src/pkg/runtime/asm_amd64p32.s294
-rw-r--r--src/pkg/runtime/asm_arm.s302
-rw-r--r--src/pkg/runtime/atomic.go51
-rw-r--r--src/pkg/runtime/atomic_386.c2
-rw-r--r--src/pkg/runtime/atomic_amd64x.c2
-rw-r--r--src/pkg/runtime/atomic_arm.c169
-rw-r--r--src/pkg/runtime/atomic_arm.go155
-rw-r--r--src/pkg/runtime/callback_windows.c77
-rw-r--r--src/pkg/runtime/cgo/asm_386.s2
-rw-r--r--src/pkg/runtime/cgo/asm_amd64.s2
-rw-r--r--src/pkg/runtime/cgo/asm_arm.s2
-rw-r--r--src/pkg/runtime/cgo/asm_nacl_amd64p32.s2
-rw-r--r--src/pkg/runtime/cgo/callbacks.c8
-rw-r--r--src/pkg/runtime/cgo/setenv.c2
-rw-r--r--src/pkg/runtime/cgocall.go (renamed from src/pkg/runtime/cgocall.c)306
-rw-r--r--src/pkg/runtime/cgocall.h1
-rw-r--r--src/pkg/runtime/chan.go644
-rw-r--r--src/pkg/runtime/chan.goc1183
-rw-r--r--src/pkg/runtime/chan.h21
-rw-r--r--src/pkg/runtime/chan_test.go52
-rw-r--r--src/pkg/runtime/cpuprof.go425
-rw-r--r--src/pkg/runtime/cpuprof.goc433
-rw-r--r--src/pkg/runtime/debug.go162
-rw-r--r--src/pkg/runtime/debug/garbage.go16
-rw-r--r--src/pkg/runtime/debug/stubs.go20
-rw-r--r--src/pkg/runtime/debug/stubs.s21
-rw-r--r--src/pkg/runtime/defs.c3
-rw-r--r--src/pkg/runtime/defs1_linux.go2
-rw-r--r--src/pkg/runtime/defs2_linux.go2
-rw-r--r--src/pkg/runtime/defs_arm_linux.go2
-rw-r--r--src/pkg/runtime/defs_darwin_386.h8
-rw-r--r--src/pkg/runtime/defs_darwin_amd64.h8
-rw-r--r--src/pkg/runtime/defs_dragonfly.go2
-rw-r--r--src/pkg/runtime/defs_dragonfly_386.h8
-rw-r--r--src/pkg/runtime/defs_dragonfly_amd64.h8
-rw-r--r--src/pkg/runtime/defs_freebsd.go2
-rw-r--r--src/pkg/runtime/defs_freebsd_386.h8
-rw-r--r--src/pkg/runtime/defs_freebsd_amd64.h8
-rw-r--r--src/pkg/runtime/defs_freebsd_arm.h8
-rw-r--r--src/pkg/runtime/defs_linux_386.h12
-rw-r--r--src/pkg/runtime/defs_linux_amd64.h12
-rw-r--r--src/pkg/runtime/defs_linux_arm.h12
-rw-r--r--src/pkg/runtime/defs_linux_power64.h6
-rw-r--r--src/pkg/runtime/defs_nacl_amd64p32.h2
-rw-r--r--src/pkg/runtime/defs_netbsd.go2
-rw-r--r--src/pkg/runtime/defs_netbsd_386.h10
-rw-r--r--src/pkg/runtime/defs_netbsd_amd64.h10
-rw-r--r--src/pkg/runtime/defs_netbsd_arm.h10
-rw-r--r--src/pkg/runtime/defs_openbsd.go6
-rw-r--r--src/pkg/runtime/defs_openbsd_386.h12
-rw-r--r--src/pkg/runtime/defs_openbsd_amd64.h12
-rw-r--r--src/pkg/runtime/defs_plan9_386.h5
-rw-r--r--src/pkg/runtime/defs_solaris.go2
-rw-r--r--src/pkg/runtime/defs_solaris_amd64.h8
-rw-r--r--src/pkg/runtime/env_plan9.c42
-rw-r--r--src/pkg/runtime/env_plan9.go52
-rw-r--r--src/pkg/runtime/env_posix.c62
-rw-r--r--src/pkg/runtime/env_posix.go52
-rw-r--r--src/pkg/runtime/error.go17
-rw-r--r--src/pkg/runtime/export_futex_test.go3
-rw-r--r--src/pkg/runtime/export_test.go133
-rw-r--r--src/pkg/runtime/extern.go86
-rw-r--r--src/pkg/runtime/funcdata.h14
-rw-r--r--src/pkg/runtime/gc_test.go8
-rw-r--r--src/pkg/runtime/gcinfo_test.go118
-rw-r--r--src/pkg/runtime/hash_test.go60
-rw-r--r--src/pkg/runtime/hashmap.go133
-rw-r--r--src/pkg/runtime/hashmap_fast.go48
-rw-r--r--src/pkg/runtime/heapdump.c139
-rw-r--r--src/pkg/runtime/iface.go439
-rw-r--r--src/pkg/runtime/iface.goc611
-rw-r--r--src/pkg/runtime/lfstack.c (renamed from src/pkg/runtime/lfstack.goc)16
-rw-r--r--src/pkg/runtime/lock_futex.c201
-rw-r--r--src/pkg/runtime/lock_futex.go205
-rw-r--r--src/pkg/runtime/lock_sema.c266
-rw-r--r--src/pkg/runtime/lock_sema.go270
-rw-r--r--src/pkg/runtime/malloc.c197
-rw-r--r--src/pkg/runtime/malloc.go572
-rw-r--r--src/pkg/runtime/malloc.h103
-rw-r--r--src/pkg/runtime/malloc1.go26
-rw-r--r--src/pkg/runtime/malloc_test.go26
-rw-r--r--src/pkg/runtime/mallocrand.go93
-rw-r--r--src/pkg/runtime/mallocrep.go72
-rw-r--r--src/pkg/runtime/mallocrep1.go144
-rw-r--r--src/pkg/runtime/mcache.c20
-rw-r--r--src/pkg/runtime/mcentral.c104
-rw-r--r--src/pkg/runtime/mem_darwin.c4
-rw-r--r--src/pkg/runtime/mem_dragonfly.c4
-rw-r--r--src/pkg/runtime/mem_freebsd.c4
-rw-r--r--src/pkg/runtime/mem_linux.c4
-rw-r--r--src/pkg/runtime/mem_nacl.c8
-rw-r--r--src/pkg/runtime/mem_netbsd.c4
-rw-r--r--src/pkg/runtime/mem_openbsd.c4
-rw-r--r--src/pkg/runtime/mem_plan9.c58
-rw-r--r--src/pkg/runtime/mem_solaris.c4
-rw-r--r--src/pkg/runtime/mem_windows.c20
-rw-r--r--src/pkg/runtime/memclr_386.s2
-rw-r--r--src/pkg/runtime/memclr_amd64.s2
-rw-r--r--src/pkg/runtime/memclr_arm.s2
-rw-r--r--src/pkg/runtime/memclr_plan9_386.s2
-rw-r--r--src/pkg/runtime/memclr_plan9_amd64.s6
-rw-r--r--src/pkg/runtime/memmove_386.s2
-rw-r--r--src/pkg/runtime/memmove_amd64.s2
-rw-r--r--src/pkg/runtime/memmove_arm.s2
-rw-r--r--src/pkg/runtime/memmove_nacl_amd64p32.s4
-rw-r--r--src/pkg/runtime/memmove_plan9_386.s4
-rw-r--r--src/pkg/runtime/memmove_plan9_amd64.s4
-rw-r--r--src/pkg/runtime/mgc0.c1014
-rw-r--r--src/pkg/runtime/mgc0.go43
-rw-r--r--src/pkg/runtime/mgc0.h17
-rw-r--r--src/pkg/runtime/mheap.c177
-rw-r--r--src/pkg/runtime/mprof.go660
-rw-r--r--src/pkg/runtime/mprof.goc489
-rw-r--r--src/pkg/runtime/mprof.h56
-rw-r--r--src/pkg/runtime/msize.c2
-rw-r--r--src/pkg/runtime/netpoll.go455
-rw-r--r--src/pkg/runtime/netpoll.goc467
-rw-r--r--src/pkg/runtime/netpoll_epoll.c103
-rw-r--r--src/pkg/runtime/netpoll_epoll.go97
-rw-r--r--src/pkg/runtime/netpoll_kqueue.c111
-rw-r--r--src/pkg/runtime/netpoll_kqueue.go101
-rw-r--r--src/pkg/runtime/netpoll_nacl.c37
-rw-r--r--src/pkg/runtime/netpoll_nacl.go26
-rw-r--r--src/pkg/runtime/netpoll_solaris.c14
-rw-r--r--src/pkg/runtime/netpoll_windows.c10
-rw-r--r--src/pkg/runtime/noasm_arm.go54
-rw-r--r--src/pkg/runtime/os_darwin.c109
-rw-r--r--src/pkg/runtime/os_darwin.go26
-rw-r--r--src/pkg/runtime/os_darwin.h19
-rw-r--r--src/pkg/runtime/os_dragonfly.c67
-rw-r--r--src/pkg/runtime/os_dragonfly.go20
-rw-r--r--src/pkg/runtime/os_dragonfly.h13
-rw-r--r--src/pkg/runtime/os_freebsd.c67
-rw-r--r--src/pkg/runtime/os_freebsd.go19
-rw-r--r--src/pkg/runtime/os_freebsd.h14
-rw-r--r--src/pkg/runtime/os_freebsd_arm.c2
-rw-r--r--src/pkg/runtime/os_linux.c37
-rw-r--r--src/pkg/runtime/os_linux.go19
-rw-r--r--src/pkg/runtime/os_linux.h19
-rw-r--r--src/pkg/runtime/os_linux_386.c2
-rw-r--r--src/pkg/runtime/os_linux_arm.c2
-rw-r--r--src/pkg/runtime/os_nacl.c72
-rw-r--r--src/pkg/runtime/os_nacl.go30
-rw-r--r--src/pkg/runtime/os_nacl_arm.c2
-rw-r--r--src/pkg/runtime/os_netbsd.c79
-rw-r--r--src/pkg/runtime/os_netbsd.go22
-rw-r--r--src/pkg/runtime/os_netbsd.h25
-rw-r--r--src/pkg/runtime/os_netbsd_arm.c2
-rw-r--r--src/pkg/runtime/os_openbsd.c53
-rw-r--r--src/pkg/runtime/os_openbsd.go19
-rw-r--r--src/pkg/runtime/os_openbsd.h17
-rw-r--r--src/pkg/runtime/os_plan9.c47
-rw-r--r--src/pkg/runtime/os_plan9.go34
-rw-r--r--src/pkg/runtime/os_plan9.h28
-rw-r--r--src/pkg/runtime/os_solaris.c111
-rw-r--r--src/pkg/runtime/os_solaris.go101
-rw-r--r--src/pkg/runtime/os_solaris.h40
-rw-r--r--src/pkg/runtime/os_windows.c208
-rw-r--r--src/pkg/runtime/os_windows.go33
-rw-r--r--src/pkg/runtime/os_windows.h16
-rw-r--r--src/pkg/runtime/os_windows_386.c4
-rw-r--r--src/pkg/runtime/os_windows_386.go11
-rw-r--r--src/pkg/runtime/os_windows_amd64.c4
-rw-r--r--src/pkg/runtime/os_windows_amd64.go11
-rw-r--r--src/pkg/runtime/panic.c453
-rw-r--r--src/pkg/runtime/panic.go216
-rw-r--r--src/pkg/runtime/panic1.go209
-rw-r--r--src/pkg/runtime/parfor.c47
-rw-r--r--src/pkg/runtime/pprof/pprof.go6
-rw-r--r--src/pkg/runtime/pprof/pprof_test.go101
-rw-r--r--src/pkg/runtime/print.c443
-rw-r--r--src/pkg/runtime/print.go109
-rw-r--r--src/pkg/runtime/print1.go341
-rw-r--r--src/pkg/runtime/proc.c1245
-rw-r--r--src/pkg/runtime/proc.go123
-rw-r--r--src/pkg/runtime/proc_test.go4
-rw-r--r--src/pkg/runtime/race.c121
-rw-r--r--src/pkg/runtime/race.go80
-rw-r--r--src/pkg/runtime/race/README2
-rw-r--r--src/pkg/runtime/race/race_darwin_amd64.sysobin249744 -> 278328 bytes
-rw-r--r--src/pkg/runtime/race/race_freebsd_amd64.sysobin261096 -> 294224 bytes
-rw-r--r--src/pkg/runtime/race/race_linux_amd64.sysobin265024 -> 298064 bytes
-rw-r--r--src/pkg/runtime/race/race_windows_amd64.sysobin247609 -> 292311 bytes
-rw-r--r--src/pkg/runtime/race/testdata/atomic_test.go6
-rw-r--r--src/pkg/runtime/race0.c124
-rw-r--r--src/pkg/runtime/race0.go23
-rw-r--r--src/pkg/runtime/race_amd64.s145
-rw-r--r--src/pkg/runtime/rdebug.go37
-rw-r--r--src/pkg/runtime/rdebug.goc27
-rw-r--r--src/pkg/runtime/rt0_android_arm.s2
-rw-r--r--src/pkg/runtime/rt0_darwin_386.s4
-rw-r--r--src/pkg/runtime/rt0_darwin_amd64.s4
-rw-r--r--src/pkg/runtime/rt0_dragonfly_386.s4
-rw-r--r--src/pkg/runtime/rt0_dragonfly_amd64.s4
-rw-r--r--src/pkg/runtime/rt0_freebsd_386.s4
-rw-r--r--src/pkg/runtime/rt0_freebsd_amd64.s4
-rw-r--r--src/pkg/runtime/rt0_freebsd_arm.s6
-rw-r--r--src/pkg/runtime/rt0_linux_386.s4
-rw-r--r--src/pkg/runtime/rt0_linux_amd64.s4
-rw-r--r--src/pkg/runtime/rt0_linux_arm.s4
-rw-r--r--src/pkg/runtime/rt0_nacl_386.s4
-rw-r--r--src/pkg/runtime/rt0_nacl_amd64p32.s4
-rw-r--r--src/pkg/runtime/rt0_nacl_arm.s4
-rw-r--r--src/pkg/runtime/rt0_netbsd_386.s4
-rw-r--r--src/pkg/runtime/rt0_netbsd_amd64.s4
-rw-r--r--src/pkg/runtime/rt0_netbsd_arm.s4
-rw-r--r--src/pkg/runtime/rt0_openbsd_386.s4
-rw-r--r--src/pkg/runtime/rt0_openbsd_amd64.s4
-rw-r--r--src/pkg/runtime/rt0_plan9_386.s4
-rw-r--r--src/pkg/runtime/rt0_plan9_amd64.s4
-rw-r--r--src/pkg/runtime/rt0_solaris_amd64.s4
-rw-r--r--src/pkg/runtime/rt0_windows_386.s4
-rw-r--r--src/pkg/runtime/rt0_windows_amd64.s4
-rw-r--r--src/pkg/runtime/runtime.c139
-rw-r--r--src/pkg/runtime/runtime.go37
-rw-r--r--src/pkg/runtime/runtime.h337
-rw-r--r--src/pkg/runtime/runtime1.goc128
-rw-r--r--src/pkg/runtime/select.go636
-rw-r--r--src/pkg/runtime/sema.go266
-rw-r--r--src/pkg/runtime/sema.goc294
-rw-r--r--src/pkg/runtime/signal.c25
-rw-r--r--src/pkg/runtime/signal_arm.c1
-rw-r--r--src/pkg/runtime/signal_nacl_amd64p32.h2
-rw-r--r--src/pkg/runtime/signal_unix.c2
-rw-r--r--src/pkg/runtime/signal_unix.go13
-rw-r--r--src/pkg/runtime/sigqueue.go173
-rw-r--r--src/pkg/runtime/sigqueue.goc166
-rw-r--r--src/pkg/runtime/slice.go22
-rw-r--r--src/pkg/runtime/softfloat_arm.c84
-rw-r--r--src/pkg/runtime/stack.c267
-rw-r--r--src/pkg/runtime/stack.go13
-rw-r--r--src/pkg/runtime/stack.h4
-rw-r--r--src/pkg/runtime/stack_test.go37
-rw-r--r--src/pkg/runtime/string.c3
-rw-r--r--src/pkg/runtime/string.go38
-rw-r--r--src/pkg/runtime/stubs.go266
-rw-r--r--src/pkg/runtime/stubs.goc81
-rw-r--r--src/pkg/runtime/symtab.go292
-rw-r--r--src/pkg/runtime/symtab.goc332
-rw-r--r--src/pkg/runtime/sys_darwin_386.s35
-rw-r--r--src/pkg/runtime/sys_darwin_amd64.s161
-rw-r--r--src/pkg/runtime/sys_dragonfly_386.s30
-rw-r--r--src/pkg/runtime/sys_dragonfly_amd64.s122
-rw-r--r--src/pkg/runtime/sys_freebsd_386.s28
-rw-r--r--src/pkg/runtime/sys_freebsd_amd64.s122
-rw-r--r--src/pkg/runtime/sys_freebsd_arm.s17
-rw-r--r--src/pkg/runtime/sys_linux_386.s155
-rw-r--r--src/pkg/runtime/sys_linux_amd64.s163
-rw-r--r--src/pkg/runtime/sys_linux_arm.s45
-rw-r--r--src/pkg/runtime/sys_nacl_386.s242
-rw-r--r--src/pkg/runtime/sys_nacl_amd64p32.s200
-rw-r--r--src/pkg/runtime/sys_nacl_arm.s190
-rw-r--r--src/pkg/runtime/sys_netbsd_386.s26
-rw-r--r--src/pkg/runtime/sys_netbsd_amd64.s120
-rw-r--r--src/pkg/runtime/sys_netbsd_arm.s21
-rw-r--r--src/pkg/runtime/sys_openbsd_386.s48
-rw-r--r--src/pkg/runtime/sys_openbsd_amd64.s124
-rw-r--r--src/pkg/runtime/sys_plan9_386.s111
-rw-r--r--src/pkg/runtime/sys_plan9_amd64.s96
-rw-r--r--src/pkg/runtime/sys_solaris_amd64.s29
-rw-r--r--src/pkg/runtime/sys_windows_386.s33
-rw-r--r--src/pkg/runtime/sys_windows_amd64.s40
-rw-r--r--src/pkg/runtime/syscall_solaris.c23
-rw-r--r--src/pkg/runtime/syscall_solaris.go322
-rw-r--r--src/pkg/runtime/syscall_solaris.goc374
-rw-r--r--src/pkg/runtime/syscall_windows.c166
-rw-r--r--src/pkg/runtime/syscall_windows.go88
-rw-r--r--src/pkg/runtime/syscall_windows.goc145
-rw-r--r--src/pkg/runtime/thunk.s156
-rw-r--r--src/pkg/runtime/thunk_solaris_amd64.s88
-rw-r--r--src/pkg/runtime/time.go266
-rw-r--r--src/pkg/runtime/time.goc343
-rw-r--r--src/pkg/runtime/tls_arm.s9
-rw-r--r--src/pkg/runtime/traceback.go639
-rw-r--r--src/pkg/runtime/traceback_windows.go63
-rw-r--r--src/pkg/runtime/traceback_x86.c436
-rw-r--r--src/pkg/runtime/type.h14
-rw-r--r--src/pkg/runtime/typekind.go44
-rw-r--r--src/pkg/runtime/typekind.h3
-rw-r--r--src/pkg/runtime/vlop_386.s10
-rw-r--r--src/pkg/runtime/vlop_arm.s41
-rw-r--r--src/pkg/runtime/vlrt.c (renamed from src/pkg/runtime/vlrt_386.c)507
-rw-r--r--src/pkg/runtime/vlrt.go258
-rw-r--r--src/pkg/runtime/vlrt_arm.c808
-rw-r--r--src/pkg/strconv/isprint.go198
-rw-r--r--src/pkg/strconv/makeisprint.go70
-rw-r--r--src/pkg/strconv/quote.go2
-rw-r--r--src/pkg/strings/strings.go63
-rw-r--r--src/pkg/strings/strings_test.go19
-rw-r--r--src/pkg/sync/atomic/asm_386.s2
-rw-r--r--src/pkg/sync/atomic/asm_amd64.s2
-rw-r--r--src/pkg/sync/atomic/asm_amd64p32.s2
-rw-r--r--src/pkg/sync/atomic/asm_arm.s2
-rw-r--r--src/pkg/sync/atomic/asm_freebsd_arm.s2
-rw-r--r--src/pkg/sync/atomic/asm_linux_arm.s26
-rw-r--r--src/pkg/sync/atomic/asm_nacl_arm.s2
-rw-r--r--src/pkg/sync/atomic/asm_netbsd_arm.s2
-rw-r--r--src/pkg/sync/atomic/doc.go2
-rw-r--r--src/pkg/sync/atomic/race.go276
-rw-r--r--src/pkg/sync/atomic/race.s8
-rw-r--r--src/pkg/sync/runtime.go8
-rw-r--r--src/pkg/sync/waitgroup.go11
-rw-r--r--src/pkg/syscall/asm_darwin_386.s2
-rw-r--r--src/pkg/syscall/asm_darwin_amd64.s2
-rw-r--r--src/pkg/syscall/asm_dragonfly_386.s2
-rw-r--r--src/pkg/syscall/asm_dragonfly_amd64.s2
-rw-r--r--src/pkg/syscall/asm_freebsd_386.s2
-rw-r--r--src/pkg/syscall/asm_freebsd_amd64.s2
-rw-r--r--src/pkg/syscall/asm_freebsd_arm.s2
-rw-r--r--src/pkg/syscall/asm_linux_386.s2
-rw-r--r--src/pkg/syscall/asm_linux_amd64.s2
-rw-r--r--src/pkg/syscall/asm_linux_arm.s5
-rw-r--r--src/pkg/syscall/asm_nacl_386.s2
-rw-r--r--src/pkg/syscall/asm_nacl_amd64p32.s2
-rw-r--r--src/pkg/syscall/asm_nacl_arm.s2
-rw-r--r--src/pkg/syscall/asm_netbsd_386.s2
-rw-r--r--src/pkg/syscall/asm_netbsd_amd64.s2
-rw-r--r--src/pkg/syscall/asm_netbsd_arm.s2
-rw-r--r--src/pkg/syscall/asm_openbsd_386.s2
-rw-r--r--src/pkg/syscall/asm_openbsd_amd64.s2
-rw-r--r--src/pkg/syscall/asm_plan9_386.s2
-rw-r--r--src/pkg/syscall/asm_plan9_amd64.s2
-rw-r--r--src/pkg/syscall/asm_solaris_amd64.s76
-rw-r--r--src/pkg/syscall/asm_windows.s13
-rw-r--r--src/pkg/syscall/asm_windows_386.s7
-rw-r--r--src/pkg/syscall/asm_windows_amd64.s7
-rw-r--r--src/pkg/syscall/exec_windows.go15
-rwxr-xr-xsrc/pkg/syscall/mkall.sh2
-rw-r--r--src/pkg/syscall/mkall_windows.bat10
-rw-r--r--src/pkg/syscall/net_nacl.go7
-rw-r--r--src/pkg/syscall/so_solaris.go2
-rw-r--r--src/pkg/syscall/syscall.go7
-rw-r--r--src/pkg/syscall/syscall_bsd.go35
-rw-r--r--src/pkg/syscall/syscall_dragonfly.go2
-rw-r--r--src/pkg/syscall/syscall_freebsd.go2
-rw-r--r--src/pkg/syscall/syscall_openbsd.go4
-rw-r--r--src/pkg/syscall/syscall_windows.go19
-rw-r--r--src/pkg/syscall/time_nacl_386.s2
-rw-r--r--src/pkg/syscall/time_nacl_amd64p32.s2
-rw-r--r--src/pkg/syscall/time_nacl_arm.s2
-rw-r--r--src/pkg/syscall/zsyscall_dragonfly_386.go20
-rw-r--r--src/pkg/syscall/zsyscall_dragonfly_amd64.go20
-rw-r--r--src/pkg/syscall/zsyscall_freebsd_386.go20
-rw-r--r--src/pkg/syscall/zsyscall_freebsd_amd64.go20
-rw-r--r--src/pkg/syscall/zsyscall_freebsd_arm.go20
-rw-r--r--src/pkg/syscall/zsyscall_openbsd_386.go20
-rw-r--r--src/pkg/syscall/zsyscall_openbsd_amd64.go20
-rw-r--r--src/pkg/syscall/zsyscall_windows.go (renamed from src/pkg/syscall/zsyscall_windows_386.go)9
-rw-r--r--src/pkg/syscall/zsyscall_windows_amd64.go1839
-rw-r--r--src/pkg/syscall/ztypes_windows.go12
-rw-r--r--src/pkg/text/scanner/scanner.go6
-rw-r--r--src/pkg/text/template/exec.go6
-rw-r--r--src/pkg/text/template/exec_test.go5
-rw-r--r--src/pkg/text/template/parse/node.go248
-rw-r--r--src/pkg/text/template/parse/parse.go62
-rw-r--r--src/pkg/text/template/parse/parse_test.go5
-rw-r--r--src/pkg/time/Makefile9
-rw-r--r--src/pkg/time/format.go2
-rw-r--r--src/pkg/time/format_test.go47
-rw-r--r--src/pkg/time/genzabbrs.go20
-rw-r--r--src/pkg/time/internal_test.go2
-rw-r--r--src/pkg/time/sleep.go9
-rw-r--r--src/pkg/time/zoneinfo_abbrs_windows.go3
-rw-r--r--src/pkg/time/zoneinfo_windows.go2
-rw-r--r--src/pkg/unicode/Makefile16
-rw-r--r--src/pkg/unicode/letter.go3
-rw-r--r--src/pkg/unicode/maketables.go178
-rw-r--r--src/pkg/unicode/script_test.go27
-rw-r--r--src/pkg/unicode/tables.go1248
-rwxr-xr-xsrc/run.bash7
-rw-r--r--test/fixedbugs/bug490.go16
-rw-r--r--test/fixedbugs/issue4388.go6
-rw-r--r--test/fixedbugs/issue5856.go2
-rw-r--r--test/fixedbugs/issue7760.go25
-rw-r--r--test/fixedbugs/issue8325.go31
-rw-r--r--test/fixedbugs/issue8336.go29
-rw-r--r--test/fixedbugs/issue8475.go25
-rw-r--r--test/fixedbugs/issue8612.go34
-rw-r--r--test/live.go53
-rw-r--r--test/map.go2
-rw-r--r--test/maplinear.go143
-rw-r--r--test/mapnan.go56
-rw-r--r--test/named1.go8
-rw-r--r--test/nosplit.go10
-rw-r--r--test/recover.go48
-rw-r--r--test/slice3.go18
-rw-r--r--test/slicecap.go90
846 files changed, 25395 insertions, 21058 deletions
diff --git a/.hgtags b/.hgtags
index aaa5234b52..51569d518f 100644
--- a/.hgtags
+++ b/.hgtags
@@ -131,4 +131,5 @@ f8b50ad4cac4d4c4ecf48324b4f512f65e82cc1c go1.3beta1
9d5451df4e53acc58a848005b7ec3a24c4b6050c go1.3rc1
3f66a43d5180052e2e1e38d979d1aa5ad05b21f9 go1.3rc2
9895f9e36435468d503eaa74ee217f28d5e28dd4 go1.3
-9895f9e36435468d503eaa74ee217f28d5e28dd4 release
+073fc578434bf3e1e22749b559d273c8da728ebb go1.3.1
+073fc578434bf3e1e22749b559d273c8da728ebb release
diff --git a/AUTHORS b/AUTHORS
index c97790c7fa..29aca3dbc4 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -16,6 +16,7 @@ Adrien Bustany <adrien-xx-google@bustany.org>
Akshat Kumar <seed@mail.nanosouffle.net>
Alan Shreve <alan@inconshreveable.com>
Albert Strasheim <fullung@gmail.com>
+Alberto Donizetti <alb.donizetti@gmail.com>
Alberto García Hierro <alberto@garciahierro.com> <alberto.garcia.hierro@gmail.com>
Aleksandar Dezelin <dezelin@gmail.com>
Alex A Skinner <alex@lx.lc>
@@ -35,6 +36,7 @@ Amrut Joshi <amrut.joshi@gmail.com>
Andrei Vieru <euvieru@gmail.com>
Andrew Balholm <andybalholm@gmail.com>
Andrew Bonventre <andybons@chromium.org>
+Andrew Bursavich <abursavich@gmail.com>
Andrew Harding <andrew@spacemonkey.com>
Andrew Lutomirski <andy@luto.us>
Andrew Pritchard <awpritchard@gmail.com>
@@ -118,6 +120,7 @@ David du Colombier <0intro@gmail.com>
David Forsythe <dforsythe@gmail.com>
David G. Andersen <dave.andersen@gmail.com>
David Jakob Fritz <david.jakob.fritz@gmail.com>
+David Leon Gil <coruus@gmail.com>
David Thomas <davidthomas426@gmail.com>
David Titarenco <david.titarenco@gmail.com>
Dean Prichard <dean.prichard@gmail.com>
@@ -152,6 +155,7 @@ Evan Shaw <chickencha@gmail.com>
Ewan Chou <coocood@gmail.com>
Fabrizio Milo <mistobaan@gmail.com>
Fan Hongjian <fan.howard@gmail.com>
+Fatih Arslan <fatih@arslan.io>
Fazlul Shahriar <fshahriar@gmail.com>
Felix Geisendörfer <haimuiba@gmail.com>
Firmansyah Adiputra <frm.adiputra@gmail.com>
@@ -178,6 +182,7 @@ Gustavo Niemeyer <gustavo@niemeyer.net>
Gwenael Treguier <gwenn.kahz@gmail.com>
Harley Laue <losinggeneration@gmail.com>
Hector Chu <hectorchu@gmail.com>
+Henning Schmiedehausen <henning@schmiedehausen.org>
Henrik Edwards <henrik.edwards@gmail.com>
Herbert Georg Fischer <herbert.fischer@gmail.com>
Hong Ruiqi <hongruiqi@gmail.com>
@@ -213,6 +218,8 @@ Jimmy Zelinskie <jimmyzelinskie@gmail.com>
Jingcheng Zhang <diogin@gmail.com>
Joakim Sernbrant <serbaut@gmail.com>
Joe Poirier <jdpoirier@gmail.com>
+Joe Shaw <joe@joeshaw.org>
+Joel Stemmer <stemmertech@gmail.com>
John Asmuth <jasmuth@gmail.com>
John C Barstow <jbowtie@amathaine.com>
John Graham-Cumming <jgc@jgc.org> <jgrahamc@gmail.com>
@@ -257,6 +264,7 @@ Luke Curley <qpingu@gmail.com>
Manuel Mendez <mmendez534@gmail.com>
Marc Weistroff <marc@weistroff.net>
Marco Hennings <marco.hennings@freiheit.com>
+Mark Theunissen <mark.theunissen@gmail.com>
Marko Juhani Silokunnas <marko.silokunnas@gmail.com>
Marko Tiikkaja <marko@joh.to>
Markover Inc. DBA Poptip
@@ -399,6 +407,7 @@ Taj Khattra <taj.khattra@gmail.com>
Tarmigan Casebolt <tarmigan@gmail.com>
Taru Karttunen <taruti@taruti.net>
Tetsuo Kiso <tetsuokiso9@gmail.com>
+Thiago Fransosi Farina <thiago.farina@gmail.com>
Thomas Alan Copeland <talan.copeland@gmail.com>
Thomas Kappler <tkappler@gmail.com>
Timo Savola <timo.savola@gmail.com>
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 1053057b5a..4d2aacafa9 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -42,6 +42,7 @@ Akshat Kumar <seed@mail.nanosouffle.net>
Alan Donovan <adonovan@google.com>
Alan Shreve <alan@inconshreveable.com>
Albert Strasheim <fullung@gmail.com>
+Alberto Donizetti <alb.donizetti@gmail.com>
Alberto García Hierro <alberto@garciahierro.com> <alberto.garcia.hierro@gmail.com>
Aleksandar Dezelin <dezelin@gmail.com>
Alex A Skinner <alex@lx.lc>
@@ -66,6 +67,7 @@ Andreas Jellinghaus <andreas@ionisiert.de> <anj@google.com>
Andrei Vieru <euvieru@gmail.com>
Andrew Balholm <andybalholm@gmail.com>
Andrew Bonventre <andybons@chromium.org>
+Andrew Bursavich <abursavich@gmail.com>
Andrew Gerrand <adg@golang.org>
Andrew Harding <andrew@spacemonkey.com>
Andrew Lutomirski <andy@luto.us>
@@ -180,6 +182,7 @@ David du Colombier <0intro@gmail.com>
David Forsythe <dforsythe@gmail.com>
David G. Andersen <dave.andersen@gmail.com>
David Jakob Fritz <david.jakob.fritz@gmail.com>
+David Leon Gil <coruus@gmail.com>
David McLeish <davemc@google.com>
David Presotto <presotto@gmail.com>
David Symonds <dsymonds@golang.org>
@@ -221,6 +224,7 @@ Evan Shaw <chickencha@gmail.com>
Ewan Chou <coocood@gmail.com>
Fabrizio Milo <mistobaan@gmail.com>
Fan Hongjian <fan.howard@gmail.com>
+Fatih Arslan <fatih@arslan.io>
Fazlul Shahriar <fshahriar@gmail.com>
Felix Geisendörfer <haimuiba@gmail.com>
Firmansyah Adiputra <frm.adiputra@gmail.com>
@@ -253,6 +257,7 @@ Gwenael Treguier <gwenn.kahz@gmail.com>
Han-Wen Nienhuys <hanwen@google.com>
Harley Laue <losinggeneration@gmail.com>
Hector Chu <hectorchu@gmail.com>
+Henning Schmiedehausen <henning@schmiedehausen.org>
Henrik Edwards <henrik.edwards@gmail.com>
Herbert Georg Fischer <herbert.fischer@gmail.com>
Hong Ruiqi <hongruiqi@gmail.com>
@@ -300,7 +305,9 @@ Jimmy Zelinskie <jimmyzelinskie@gmail.com>
Jingcheng Zhang <diogin@gmail.com>
Joakim Sernbrant <serbaut@gmail.com>
Joe Poirier <jdpoirier@gmail.com>
+Joe Shaw <joe@joeshaw.org>
Joel Sing <jsing@google.com>
+Joel Stemmer <stemmertech@gmail.com>
Johan Euphrosine <proppy@google.com>
John Asmuth <jasmuth@gmail.com>
John Beisley <huin@google.com>
@@ -368,6 +375,7 @@ Manuel Mendez <mmendez534@gmail.com>
Marc Weistroff <marc@weistroff.net>
Marcel van Lohuizen <mpvl@golang.org>
Marco Hennings <marco.hennings@freiheit.com>
+Mark Theunissen <mark.theunissen@gmail.com>
Mark Zavislak <zavislak@google.com>
Marko Juhani Silokunnas <marko.silokunnas@gmail.com>
Marko Mikulicic <mkm@google.com>
@@ -455,6 +463,7 @@ Paul Borman <borman@google.com>
Paul Chang <paulchang@google.com>
Paul Hammond <paul@paulhammond.org>
Paul Lalonde <paul.a.lalonde@gmail.com>
+Paul Nasrat <pnasrat@google.com>
Paul Sbarra <Sbarra.Paul@gmail.com>
Paul van Brouwershaven <paul@vanbrouwershaven.com>
Pavel Zinovkin <pavel.zinovkin@gmail.com>
@@ -486,6 +495,7 @@ Richard Crowley <r@rcrowley.org>
Richard Eric Gavaletz <gavaletz@gmail.com>
Richard Musiol <mail@richard-musiol.de> <neelance@gmail.com>
Rick Arnold <rickarnoldjr@gmail.com>
+Rick Hudson <rlh@golang.org>
Risto Jaakko Saarelma <rsaarelm@gmail.com>
Rob Pike <r@golang.org>
Robert Daniel Kortschak <dan.kortschak@adelaide.edu.au>
@@ -547,6 +557,7 @@ Taj Khattra <taj.khattra@gmail.com>
Tarmigan Casebolt <tarmigan@gmail.com>
Taru Karttunen <taruti@taruti.net>
Tetsuo Kiso <tetsuokiso9@gmail.com>
+Thiago Fransosi Farina <thiago.farina@gmail.com> <tfarina@chromium.org>
Thomas Alan Copeland <talan.copeland@gmail.com>
Thomas Habets <habets@google.com>
Thomas Kappler <tkappler@gmail.com>
diff --git a/api/except.txt b/api/except.txt
index 1a8296635d..6e40e1844e 100644
--- a/api/except.txt
+++ b/api/except.txt
@@ -327,3 +327,4 @@ pkg syscall (netbsd-arm), const SizeofIfData = 132
pkg syscall (netbsd-arm), type IfMsghdr struct, Pad_cgo_1 [4]uint8
pkg syscall (netbsd-arm-cgo), const SizeofIfData = 132
pkg syscall (netbsd-arm-cgo), type IfMsghdr struct, Pad_cgo_1 [4]uint8
+pkg unicode, const Version = "6.3.0"
diff --git a/api/next.txt b/api/next.txt
index 5e49b3f94f..e8570a6f2b 100644
--- a/api/next.txt
+++ b/api/next.txt
@@ -115,3 +115,27 @@ pkg debug/goobj, type Var struct, Kind int
pkg debug/goobj, type Var struct, Name string
pkg debug/goobj, type Var struct, Offset int
pkg debug/goobj, type Var struct, Type SymID
+pkg unicode, const Version = "7.0.0"
+pkg unicode, var Bassa_Vah *RangeTable
+pkg unicode, var Caucasian_Albanian *RangeTable
+pkg unicode, var Duployan *RangeTable
+pkg unicode, var Elbasan *RangeTable
+pkg unicode, var Grantha *RangeTable
+pkg unicode, var Khojki *RangeTable
+pkg unicode, var Khudawadi *RangeTable
+pkg unicode, var Linear_A *RangeTable
+pkg unicode, var Mahajani *RangeTable
+pkg unicode, var Manichaean *RangeTable
+pkg unicode, var Mende_Kikakui *RangeTable
+pkg unicode, var Modi *RangeTable
+pkg unicode, var Mro *RangeTable
+pkg unicode, var Nabataean *RangeTable
+pkg unicode, var Old_North_Arabian *RangeTable
+pkg unicode, var Old_Permic *RangeTable
+pkg unicode, var Pahawh_Hmong *RangeTable
+pkg unicode, var Palmyrene *RangeTable
+pkg unicode, var Pau_Cin_Hau *RangeTable
+pkg unicode, var Psalter_Pahlavi *RangeTable
+pkg unicode, var Siddham *RangeTable
+pkg unicode, var Tirhuta *RangeTable
+pkg unicode, var Warang_Citi *RangeTable
diff --git a/doc/asm.html b/doc/asm.html
index f4ef1e62f2..943347216e 100644
--- a/doc/asm.html
+++ b/doc/asm.html
@@ -253,7 +253,7 @@ There may be one or two arguments to the directives.
If there are two, the first is a bit mask of flags,
which can be written as numeric expressions, added or or-ed together,
or can be set symbolically for easier absorption by a human.
-Their values, defined in the file <code>src/cmd/ld/textflag.h</code>, are:
+Their values, defined in the standard <code>#include</code> file <code>textflag.h</code>, are:
</p>
<ul>
diff --git a/doc/cmd.html b/doc/cmd.html
index 725666f1de..132ea275fa 100644
--- a/doc/cmd.html
+++ b/doc/cmd.html
@@ -65,7 +65,7 @@ details.
<td><a href="//godoc.org/code.google.com/p/go.tools/cmd/cover/">cover</a></td>
<td>&nbsp;&nbsp;&nbsp;&nbsp;</td>
<td>Cover is a program for creating and analyzing the coverage profiles
-generated by <code>"go test -coverprofile"</code>.
+generated by <code>"go test -coverprofile"</code>.</td>
</tr>
<tr>
diff --git a/doc/devel/release.html b/doc/devel/release.html
index 0824463f4c..a8f5963ca7 100644
--- a/doc/devel/release.html
+++ b/doc/devel/release.html
@@ -20,6 +20,13 @@ Go 1.3 is a major release of Go.
Read the <a href="/doc/go1.3">Go 1.3 Release Notes</a> for more information.
</p>
+<h3 id="go1.3.minor">Minor revisions</h3>
+
+<p>
+go1.3.1 (released 2014/08/13) includes bug fixes to the compiler and the <code>runtime</code>, <code>net</code>, and <code>crypto/rsa</code> packages.
+See the <a href="//code.google.com/p/go/source/list?name=release-branch.go1.3&r=073fc578434bf3e1e22749b559d273c8da728ebb">change history</a> for details.
+</p>
+
<h2 id="go1.2">go1.2 (released 2013/12/01)</h2>
<p>
diff --git a/doc/go1.3.html b/doc/go1.3.html
index 0d2bda122d..042de1bc7b 100644
--- a/doc/go1.3.html
+++ b/doc/go1.3.html
@@ -521,6 +521,15 @@ field to specify an end-to-end timeout on requests made using the
client.
</li>
+<li>
+The <a href="/pkg/net/http/"><code>net/http</code></a> package's
+<a href="/pkg/net/http/#Request.ParseMultipartForm"><code>Request.ParseMultipartForm</code></a>
+method will now return an error if the body's <code>Content-Type</code>
+is not <code>mutipart/form-data</code>.
+Prior to Go 1.3 it would silently fail and return <code>nil</code>.
+Code that relies on the previous behavior should be updated.
+</li>
+
<li> In the <a href="/pkg/net/"><code>net</code></a> package,
the <a href="/pkg/net/#Dialer"><code>Dialer</code></a> struct now
has a <code>KeepAlive</code> option to specify a keep-alive period for the connection.
diff --git a/doc/go1.4.txt b/doc/go1.4.txt
index 87904ee7ea..c5da7b72b6 100644
--- a/doc/go1.4.txt
+++ b/doc/go1.4.txt
@@ -7,12 +7,27 @@ Please keep the list sorted (as in sort.Strings of the lines).
spec: permit for range x (CL 104680043)
+cmd/6l, liblink: use pc-relative addressing for all memory references, so that linking Go binaries at high addresses works (CL 125140043). This cuts the maximum size of a Go binary's text+data+bss from 4GB to 2GB.
+cmd/go: import comments (CL 124940043)
+cmd/go: implement "internal" (CL 120600043)
+cmd/go: implement "generate" (CL 125580044)
+
+asm: make textflag.h available outside of cmd/ld (CL 128050043)
+crypto/tls: add support for ALPN (RFC 7301) (CL 108710046)
+crypto/tls: support programmatic selection of server certificates (CL 107400043)
encoding/gob: remove unsafe (CL 102680045)
misc: deleted editor support; refer to https://code.google.com/p/go-wiki/wiki/IDEsAndTextEditorPlugins instead (CL 105470043)
+net/http: add Request.BasicAuth method (CL 76540043)
+net/http: add Transport.DialTLS hook (CL 137940043)
+net/http/httputil: add ReverseProxy.ErrorLog (CL 132750043)
os: implement symlink support for windows (CL 86160044)
+runtime: implement monotonic clocks on windows (CL 108700045)
runtime/race: freebsd is supported (CL 107270043)
syscall: Setuid, Setgid are disabled on linux platforms. On linux those syscalls operate on the calling thread, not the whole process. This does not match the semantics of other platforms, nor the expectations of the caller, so the operations have been disabled until issue 1435 is resolved (CL 106170043)
+syscal: now frozen (CL 129820043)
testing: add Coverage (CL 98150043)
text/scanner: add IsIdentRune field of Scanner. (CL 108030044)
time: use the micro symbol (µ (U+00B5)) to print microsecond duration (CL 105030046)
encoding/asn1: optional elements with a default value will now only be omitted if they have that value (CL 86960045).
+
+go.sys subrepo created: http://golang.org/s/go1.4-syscall
diff --git a/doc/go1compat.html b/doc/go1compat.html
index d10b9af294..8ceaf32f97 100644
--- a/doc/go1compat.html
+++ b/doc/go1compat.html
@@ -152,6 +152,21 @@ will be tagged as appropriate to identify versions that are compatible
with the Go 1 point releases.
</p>
+<h2 id="operating_systems">Operating systems</h2>
+
+<p>
+It is impossible to guarantee long-term compatibility with operating
+system interfaces, which are changed by outside parties.
+The <a href="/pkg/syscall/"><code>syscall</code></a> package
+is therefore outside the purview of the guarantees made here.
+As of Go version 1.4, the <code>syscall</code> package is frozen.
+Any evolution of the system call interface must be supported elsewhere,
+such as in the
+<a href="http://godoc.org/code.google.com/p/go.sys">go.sys</a> subrepository.
+For details and background, see
+<a href="https://golang.org/s/go1.4-syscall">this document</a>.
+</p>
+
<h2 id="tools">Tools</h2>
<p>
diff --git a/doc/go_spec.html b/doc/go_spec.html
index a32fa457c9..e8bb35f0b0 100644
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1,6 +1,6 @@
<!--{
"Title": "The Go Programming Language Specification",
- "Subtitle": "Version of August 5, 2014",
+ "Subtitle": "Version of August 28, 2014",
"Path": "/ref/spec"
}-->
@@ -479,7 +479,7 @@ Interpreted string literals are character sequences between double
quotes <code>&quot;&quot;</code>. The text between the quotes,
which may not contain newlines, forms the
value of the literal, with backslash escapes interpreted as they
-are in rune literals (except that <code>\'</code> is illegal and
+are in <a href="#Rune_literals">rune literals</a> (except that <code>\'</code> is illegal and
<code>\"</code> is legal), with the same restrictions.
The three-digit octal (<code>\</code><i>nnn</i>)
and two-digit hexadecimal (<code>\x</code><i>nn</i>) escapes represent individual
@@ -1034,7 +1034,7 @@ The value of an uninitialized pointer is <code>nil</code>.
<pre class="ebnf">
PointerType = "*" BaseType .
-BaseType = Type .
+BaseType = Type .
</pre>
<pre>
@@ -2118,9 +2118,9 @@ operand only on the left-hand side of an <a href="#Assignments">assignment</a>.
</p>
<pre class="ebnf">
-Operand = Literal | OperandName | MethodExpr | "(" Expression ")" .
-Literal = BasicLit | CompositeLit | FunctionLit .
-BasicLit = int_lit | float_lit | imaginary_lit | rune_lit | string_lit .
+Operand = Literal | OperandName | MethodExpr | "(" Expression ")" .
+Literal = BasicLit | CompositeLit | FunctionLit .
+BasicLit = int_lit | float_lit | imaginary_lit | rune_lit | string_lit .
OperandName = identifier | QualifiedIdent.
</pre>
@@ -2537,6 +2537,233 @@ p.M0() // ((*p).T0).M0()
</pre>
+<h3 id="Method_expressions">Method expressions</h3>
+
+<p>
+If <code>M</code> is in the <a href="#Method_sets">method set</a> of type <code>T</code>,
+<code>T.M</code> is a function that is callable as a regular function
+with the same arguments as <code>M</code> prefixed by an additional
+argument that is the receiver of the method.
+</p>
+
+<pre class="ebnf">
+MethodExpr = ReceiverType "." MethodName .
+ReceiverType = TypeName | "(" "*" TypeName ")" | "(" ReceiverType ")" .
+</pre>
+
+<p>
+Consider a struct type <code>T</code> with two methods,
+<code>Mv</code>, whose receiver is of type <code>T</code>, and
+<code>Mp</code>, whose receiver is of type <code>*T</code>.
+</p>
+
+<pre>
+type T struct {
+ a int
+}
+func (tv T) Mv(a int) int { return 0 } // value receiver
+func (tp *T) Mp(f float32) float32 { return 1 } // pointer receiver
+
+var t T
+</pre>
+
+<p>
+The expression
+</p>
+
+<pre>
+T.Mv
+</pre>
+
+<p>
+yields a function equivalent to <code>Mv</code> but
+with an explicit receiver as its first argument; it has signature
+</p>
+
+<pre>
+func(tv T, a int) int
+</pre>
+
+<p>
+That function may be called normally with an explicit receiver, so
+these five invocations are equivalent:
+</p>
+
+<pre>
+t.Mv(7)
+T.Mv(t, 7)
+(T).Mv(t, 7)
+f1 := T.Mv; f1(t, 7)
+f2 := (T).Mv; f2(t, 7)
+</pre>
+
+<p>
+Similarly, the expression
+</p>
+
+<pre>
+(*T).Mp
+</pre>
+
+<p>
+yields a function value representing <code>Mp</code> with signature
+</p>
+
+<pre>
+func(tp *T, f float32) float32
+</pre>
+
+<p>
+For a method with a value receiver, one can derive a function
+with an explicit pointer receiver, so
+</p>
+
+<pre>
+(*T).Mv
+</pre>
+
+<p>
+yields a function value representing <code>Mv</code> with signature
+</p>
+
+<pre>
+func(tv *T, a int) int
+</pre>
+
+<p>
+Such a function indirects through the receiver to create a value
+to pass as the receiver to the underlying method;
+the method does not overwrite the value whose address is passed in
+the function call.
+</p>
+
+<p>
+The final case, a value-receiver function for a pointer-receiver method,
+is illegal because pointer-receiver methods are not in the method set
+of the value type.
+</p>
+
+<p>
+Function values derived from methods are called with function call syntax;
+the receiver is provided as the first argument to the call.
+That is, given <code>f := T.Mv</code>, <code>f</code> is invoked
+as <code>f(t, 7)</code> not <code>t.f(7)</code>.
+To construct a function that binds the receiver, use a
+<a href="#Function_literals">function literal</a> or
+<a href="#Method_values">method value</a>.
+</p>
+
+<p>
+It is legal to derive a function value from a method of an interface type.
+The resulting function takes an explicit receiver of that interface type.
+</p>
+
+<h3 id="Method_values">Method values</h3>
+
+<p>
+If the expression <code>x</code> has static type <code>T</code> and
+<code>M</code> is in the <a href="#Method_sets">method set</a> of type <code>T</code>,
+<code>x.M</code> is called a <i>method value</i>.
+The method value <code>x.M</code> is a function value that is callable
+with the same arguments as a method call of <code>x.M</code>.
+The expression <code>x</code> is evaluated and saved during the evaluation of the
+method value; the saved copy is then used as the receiver in any calls,
+which may be executed later.
+</p>
+
+<p>
+The type <code>T</code> may be an interface or non-interface type.
+</p>
+
+<p>
+As in the discussion of <a href="#Method_expressions">method expressions</a> above,
+consider a struct type <code>T</code> with two methods,
+<code>Mv</code>, whose receiver is of type <code>T</code>, and
+<code>Mp</code>, whose receiver is of type <code>*T</code>.
+</p>
+
+<pre>
+type T struct {
+ a int
+}
+func (tv T) Mv(a int) int { return 0 } // value receiver
+func (tp *T) Mp(f float32) float32 { return 1 } // pointer receiver
+
+var t T
+var pt *T
+func makeT() T
+</pre>
+
+<p>
+The expression
+</p>
+
+<pre>
+t.Mv
+</pre>
+
+<p>
+yields a function value of type
+</p>
+
+<pre>
+func(int) int
+</pre>
+
+<p>
+These two invocations are equivalent:
+</p>
+
+<pre>
+t.Mv(7)
+f := t.Mv; f(7)
+</pre>
+
+<p>
+Similarly, the expression
+</p>
+
+<pre>
+pt.Mp
+</pre>
+
+<p>
+yields a function value of type
+</p>
+
+<pre>
+func(float32) float32
+</pre>
+
+<p>
+As with <a href="#Selectors">selectors</a>, a reference to a non-interface method with a value receiver
+using a pointer will automatically dereference that pointer: <code>pt.Mv</code> is equivalent to <code>(*pt).Mv</code>.
+</p>
+
+<p>
+As with <a href="#Calls">method calls</a>, a reference to a non-interface method with a pointer receiver
+using an addressable value will automatically take the address of that value: <code>t.Mp</code> is equivalent to <code>(&amp;t).Mp</code>.
+</p>
+
+<pre>
+f := t.Mv; f(7) // like t.Mv(7)
+f := pt.Mp; f(7) // like pt.Mp(7)
+f := pt.Mv; f(7) // like (*pt).Mv(7)
+f := t.Mp; f(7) // like (&amp;t).Mp(7)
+f := makeT().Mp // invalid: result of makeT() is not addressable
+</pre>
+
+<p>
+Although the examples above use non-interface types, it is also legal to create a method value
+from a value of interface type.
+</p>
+
+<pre>
+var i interface { M(int) } = myVal
+f := i.M; f(7) // like i.M(7)
+</pre>
+
+
<h3 id="Index_expressions">Index expressions</h3>
<p>
@@ -3371,7 +3598,7 @@ or an array indexing operation of an addressable array.
As an exception to the addressability requirement, <code>x</code> may also be a
(possibly parenthesized)
<a href="#Composite_literals">composite literal</a>.
-If the evaluation of <code>x</code> would cause a <a href="#Run_time_panics">run-time panic</a>,
+If the evaluation of <code>x</code> would cause a <a href="#Run_time_panics">run-time panic</a>,
then the evaluation of <code>&amp;x</code> does too.
</p>
@@ -3436,232 +3663,6 @@ channel is closed and empty.
</p>
-<h3 id="Method_expressions">Method expressions</h3>
-
-<p>
-If <code>M</code> is in the <a href="#Method_sets">method set</a> of type <code>T</code>,
-<code>T.M</code> is a function that is callable as a regular function
-with the same arguments as <code>M</code> prefixed by an additional
-argument that is the receiver of the method.
-</p>
-
-<pre class="ebnf">
-MethodExpr = ReceiverType "." MethodName .
-ReceiverType = TypeName | "(" "*" TypeName ")" | "(" ReceiverType ")" .
-</pre>
-
-<p>
-Consider a struct type <code>T</code> with two methods,
-<code>Mv</code>, whose receiver is of type <code>T</code>, and
-<code>Mp</code>, whose receiver is of type <code>*T</code>.
-</p>
-
-<pre>
-type T struct {
- a int
-}
-func (tv T) Mv(a int) int { return 0 } // value receiver
-func (tp *T) Mp(f float32) float32 { return 1 } // pointer receiver
-
-var t T
-</pre>
-
-<p>
-The expression
-</p>
-
-<pre>
-T.Mv
-</pre>
-
-<p>
-yields a function equivalent to <code>Mv</code> but
-with an explicit receiver as its first argument; it has signature
-</p>
-
-<pre>
-func(tv T, a int) int
-</pre>
-
-<p>
-That function may be called normally with an explicit receiver, so
-these five invocations are equivalent:
-</p>
-
-<pre>
-t.Mv(7)
-T.Mv(t, 7)
-(T).Mv(t, 7)
-f1 := T.Mv; f1(t, 7)
-f2 := (T).Mv; f2(t, 7)
-</pre>
-
-<p>
-Similarly, the expression
-</p>
-
-<pre>
-(*T).Mp
-</pre>
-
-<p>
-yields a function value representing <code>Mp</code> with signature
-</p>
-
-<pre>
-func(tp *T, f float32) float32
-</pre>
-
-<p>
-For a method with a value receiver, one can derive a function
-with an explicit pointer receiver, so
-</p>
-
-<pre>
-(*T).Mv
-</pre>
-
-<p>
-yields a function value representing <code>Mv</code> with signature
-</p>
-
-<pre>
-func(tv *T, a int) int
-</pre>
-
-<p>
-Such a function indirects through the receiver to create a value
-to pass as the receiver to the underlying method;
-the method does not overwrite the value whose address is passed in
-the function call.
-</p>
-
-<p>
-The final case, a value-receiver function for a pointer-receiver method,
-is illegal because pointer-receiver methods are not in the method set
-of the value type.
-</p>
-
-<p>
-Function values derived from methods are called with function call syntax;
-the receiver is provided as the first argument to the call.
-That is, given <code>f := T.Mv</code>, <code>f</code> is invoked
-as <code>f(t, 7)</code> not <code>t.f(7)</code>.
-To construct a function that binds the receiver, use a
-<a href="#Function_literals">function literal</a> or
-<a href="#Method_values">method value</a>.
-</p>
-
-<p>
-It is legal to derive a function value from a method of an interface type.
-The resulting function takes an explicit receiver of that interface type.
-</p>
-
-<h3 id="Method_values">Method values</h3>
-
-<p>
-If the expression <code>x</code> has static type <code>T</code> and
-<code>M</code> is in the <a href="#Method_sets">method set</a> of type <code>T</code>,
-<code>x.M</code> is called a <i>method value</i>.
-The method value <code>x.M</code> is a function value that is callable
-with the same arguments as a method call of <code>x.M</code>.
-The expression <code>x</code> is evaluated and saved during the evaluation of the
-method value; the saved copy is then used as the receiver in any calls,
-which may be executed later.
-</p>
-
-<p>
-The type <code>T</code> may be an interface or non-interface type.
-</p>
-
-<p>
-As in the discussion of <a href="#Method_expressions">method expressions</a> above,
-consider a struct type <code>T</code> with two methods,
-<code>Mv</code>, whose receiver is of type <code>T</code>, and
-<code>Mp</code>, whose receiver is of type <code>*T</code>.
-</p>
-
-<pre>
-type T struct {
- a int
-}
-func (tv T) Mv(a int) int { return 0 } // value receiver
-func (tp *T) Mp(f float32) float32 { return 1 } // pointer receiver
-
-var t T
-var pt *T
-func makeT() T
-</pre>
-
-<p>
-The expression
-</p>
-
-<pre>
-t.Mv
-</pre>
-
-<p>
-yields a function value of type
-</p>
-
-<pre>
-func(int) int
-</pre>
-
-<p>
-These two invocations are equivalent:
-</p>
-
-<pre>
-t.Mv(7)
-f := t.Mv; f(7)
-</pre>
-
-<p>
-Similarly, the expression
-</p>
-
-<pre>
-pt.Mp
-</pre>
-
-<p>
-yields a function value of type
-</p>
-
-<pre>
-func(float32) float32
-</pre>
-
-<p>
-As with <a href="#Selectors">selectors</a>, a reference to a non-interface method with a value receiver
-using a pointer will automatically dereference that pointer: <code>pt.Mv</code> is equivalent to <code>(*pt).Mv</code>.
-</p>
-
-<p>
-As with <a href="#Calls">method calls</a>, a reference to a non-interface method with a pointer receiver
-using an addressable value will automatically take the address of that value: <code>t.Mp</code> is equivalent to <code>(&amp;t).Mp</code>.
-</p>
-
-<pre>
-f := t.Mv; f(7) // like t.Mv(7)
-f := pt.Mp; f(7) // like pt.Mp(7)
-f := pt.Mv; f(7) // like (*pt).Mv(7)
-f := t.Mp; f(7) // like (&amp;t).Mp(7)
-f := makeT().Mp // invalid: result of makeT() is not addressable
-</pre>
-
-<p>
-Although the examples above use non-interface types, it is also legal to create a method value
-from a value of interface type.
-</p>
-
-<pre>
-var i interface { M(int) } = myVal
-f := i.M; f(7) // like i.M(7)
-</pre>
-
<h3 id="Conversions">Conversions</h3>
<p>
@@ -4051,7 +4052,7 @@ n := map[int]int{a: f()} // n may be {2: 3} or {3: 3}: evaluation order bet
<p>
At package level, initialization dependencies override the left-to-right rule
for individual initialization expressions, but not for operands within each
-expression:
+expression:
</p>
<pre>
@@ -5941,7 +5942,7 @@ variable or function.
<li>
A reference to a method <code>m</code> is a
<a href="#Method_values">method value</a> or
-<a href="#Method_expressions">method expression</a> of the form
+<a href="#Method_expressions">method expression</a> of the form
<code>t.m</code>, where the (static) type of <code>t</code> is
not an interface type, and the method <code>m</code> is in the
<a href="#Method_sets">method set</a> of <code>t</code>.
@@ -5950,7 +5951,7 @@ It is immaterial whether the resulting function value
</li>
<li>
-A variable, function, or method <code>x</code> depends on a variable
+A variable, function, or method <code>x</code> depends on a variable
<code>y</code> if <code>x</code>'s initialization expression or body
(for functions and methods) contains a reference to <code>y</code>
or to a function or method that depends on <code>y</code>.
@@ -6002,7 +6003,7 @@ func init() { … }
</pre>
<p>
-Multiple such functions may be defined, even within a single
+Multiple such functions may be defined, even within a single
source file. The <code>init</code> identifier is not
<a href="#Declarations_and_scope">declared</a> and thus
<code>init</code> functions cannot be referred to from anywhere
diff --git a/include/link.h b/include/link.h
index 298b048e7c..05d063f767 100644
--- a/include/link.h
+++ b/include/link.h
@@ -127,6 +127,7 @@ struct LSym
short type;
short version;
uchar dupok;
+ uchar cfunc;
uchar external;
uchar nosplit;
uchar reachable;
diff --git a/lib/codereview/codereview.cfg b/lib/codereview/codereview.cfg
index 2801ebf8d6..43dbf3ce3b 100644
--- a/lib/codereview/codereview.cfg
+++ b/lib/codereview/codereview.cfg
@@ -1 +1,2 @@
defaultcc: golang-codereviews@googlegroups.com
+contributors: http://go.googlecode.com/hg/CONTRIBUTORS
diff --git a/lib/codereview/codereview.py b/lib/codereview/codereview.py
index fdf11d1f48..263385b79f 100644
--- a/lib/codereview/codereview.py
+++ b/lib/codereview/codereview.py
@@ -3603,11 +3603,17 @@ class MercurialVCS(VersionControlSystem):
if use_hg_shell:
base_content = RunShell(["hg", "cat", "-r", base_rev, oldrelpath], silent_ok=True)
else:
- base_content = str(self.repo[base_rev][oldrelpath].data())
+ try:
+ base_content = str(self.repo[base_rev][oldrelpath].data())
+ except Exception:
+ pass
is_binary = "\0" in base_content # Mercurial's heuristic
if status != "R":
- new_content = open(relpath, "rb").read()
- is_binary = is_binary or "\0" in new_content
+ try:
+ new_content = open(relpath, "rb").read()
+ is_binary = is_binary or "\0" in new_content
+ except Exception:
+ pass
if is_binary and base_content and use_hg_shell:
# Fetch again without converting newlines
base_content = RunShell(["hg", "cat", "-r", base_rev, oldrelpath],
diff --git a/misc/cgo/errors/issue7757.go b/misc/cgo/errors/issue7757.go
new file mode 100644
index 0000000000..5eafd22e8a
--- /dev/null
+++ b/misc/cgo/errors/issue7757.go
@@ -0,0 +1,14 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+void foo() {}
+*/
+import "C"
+
+func main() {
+ C.foo = C.foo // ERROR HERE
+}
diff --git a/misc/cgo/errors/issue8442.go b/misc/cgo/errors/issue8442.go
new file mode 100644
index 0000000000..45daf8e59e
--- /dev/null
+++ b/misc/cgo/errors/issue8442.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 main
+
+// Issue 8442. Cgo output unhelpful error messages for
+// invalid C preambles.
+
+/*
+void issue8442foo(UNDEF*); // ERROR HERE
+*/
+import "C"
+
+func main() {
+ C.issue8442foo(nil)
+}
diff --git a/misc/cgo/errors/test.bash b/misc/cgo/errors/test.bash
index f0f60c8445..c96264389c 100755
--- a/misc/cgo/errors/test.bash
+++ b/misc/cgo/errors/test.bash
@@ -27,6 +27,8 @@ check() {
check err1.go
check err2.go
check err3.go
+check issue7757.go
+check issue8442.go
rm -rf errs _obj
exit 0
diff --git a/misc/cgo/test/backdoor/runtime.c b/misc/cgo/test/backdoor/runtime.c
index 7e6b448724..87ee44eb6f 100644
--- a/misc/cgo/test/backdoor/runtime.c
+++ b/misc/cgo/test/backdoor/runtime.c
@@ -10,20 +10,6 @@
typedef char bool;
-bool runtime·lockedOSThread(void);
-
-static void
-FLUSH(void*)
-{
-}
-
-void
-·LockedOSThread(bool b)
-{
- b = runtime·lockedOSThread();
- FLUSH(&b);
-}
-
// This is what a cgo-compiled stub declaration looks like.
void
·Issue7695(struct{void *y[8*sizeof(void*)];}p)
diff --git a/misc/cgo/test/backdoor/thunk.s b/misc/cgo/test/backdoor/thunk.s
new file mode 100644
index 0000000000..ae735c8a34
--- /dev/null
+++ b/misc/cgo/test/backdoor/thunk.s
@@ -0,0 +1,16 @@
+// 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.
+
+// Assembly to get into package runtime without using exported symbols.
+
+// +build amd64 amd64p32 arm 386
+
+#include "textflag.h"
+
+#ifdef GOARCH_arm
+#define JMP B
+#endif
+
+TEXT ·LockedOSThread(SB),NOSPLIT,$0-0
+ JMP runtime·lockedOSThread(SB)
diff --git a/misc/cgo/test/buildid_linux.go b/misc/cgo/test/buildid_linux.go
new file mode 100644
index 0000000000..a3a86edfca
--- /dev/null
+++ b/misc/cgo/test/buildid_linux.go
@@ -0,0 +1,77 @@
+// 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 cgotest
+
+// Test that we have no more than one build ID. In the past we used
+// to generate a separate build ID for each package using cgo, and the
+// linker concatenated them all. We don't want that--we only want
+// one.
+
+import (
+ "bytes"
+ "debug/elf"
+ "os"
+ "testing"
+)
+
+func testBuildID(t *testing.T) {
+ f, err := elf.Open("/proc/self/exe")
+ if err != nil {
+ if os.IsNotExist(err) {
+ t.Skip("no /proc/self/exe")
+ }
+ t.Fatalf("opening /proc/self/exe: ", err)
+ }
+ defer f.Close()
+
+ c := 0
+ for i, s := range f.Sections {
+ if s.Type != elf.SHT_NOTE {
+ continue
+ }
+
+ d, err := s.Data()
+ if err != nil {
+ t.Logf("reading data of note section %d: %v", i, err)
+ continue
+ }
+
+ for len(d) > 0 {
+
+ // ELF standards differ as to the sizes in
+ // note sections. Both the GNU linker and
+ // gold always generate 32-bit sizes, so that
+ // is what we assume here.
+
+ if len(d) < 12 {
+ t.Logf("note section %d too short (%d < 12)", i, len(d))
+ continue
+ }
+
+ namesz := f.ByteOrder.Uint32(d)
+ descsz := f.ByteOrder.Uint32(d[4:])
+ typ := f.ByteOrder.Uint32(d[8:])
+
+ an := (namesz + 3) &^ 3
+ ad := (descsz + 3) &^ 3
+
+ if int(12+an+ad) > len(d) {
+ t.Logf("note section %d too short for header (%d < 12 + align(%d,4) + align(%d,4))", i, len(d), namesz, descsz)
+ continue
+ }
+
+ // 3 == NT_GNU_BUILD_ID
+ if typ == 3 && namesz == 4 && bytes.Equal(d[12:16], []byte("GNU\000")) {
+ c++
+ }
+
+ d = d[12+an+ad:]
+ }
+ }
+
+ if c > 1 {
+ t.Errorf("found %d build ID notes", c)
+ }
+}
diff --git a/misc/cgo/test/callback.go b/misc/cgo/test/callback.go
index 82ed015bd8..281e79494e 100644
--- a/misc/cgo/test/callback.go
+++ b/misc/cgo/test/callback.go
@@ -13,12 +13,13 @@ void callPanic(void);
import "C"
import (
- "./backdoor"
"path"
"runtime"
"strings"
"testing"
"unsafe"
+
+ "./backdoor"
)
// nestedCall calls into C, back into Go, and finally to f.
@@ -152,11 +153,13 @@ func testCallbackCallers(t *testing.T) {
n := 0
name := []string{
"test.goCallback",
+ "runtime.call16",
"runtime.cgocallbackg1",
"runtime.cgocallbackg",
"runtime.cgocallback_gofunc",
- "runtime.asmcgocall",
- "runtime.cgocall",
+ "asmcgocall",
+ "runtime.asmcgocall_errno",
+ "runtime.cgocall_errno",
"test._Cfunc_callback",
"test.nestedCall",
"test.testCallbackCallers",
@@ -181,8 +184,12 @@ func testCallbackCallers(t *testing.T) {
if strings.HasPrefix(fname, "_") {
fname = path.Base(f.Name()[1:])
}
- if fname != name[i] {
- t.Errorf("expected function name %s, got %s", name[i], fname)
+ namei := ""
+ if i < len(name) {
+ namei = name[i]
+ }
+ if fname != namei {
+ t.Errorf("stk[%d] = %q, want %q", i, fname, namei)
}
}
}
diff --git a/misc/cgo/test/cgo_linux_test.go b/misc/cgo/test/cgo_linux_test.go
index 0a405c7a3b..4fe0db1b2b 100644
--- a/misc/cgo/test/cgo_linux_test.go
+++ b/misc/cgo/test/cgo_linux_test.go
@@ -6,5 +6,6 @@ package cgotest
import "testing"
-func TestSetgid(t *testing.T) { testSetgid(t) }
-func Test6997(t *testing.T) { test6997(t) }
+func TestSetgid(t *testing.T) { testSetgid(t) }
+func Test6997(t *testing.T) { test6997(t) }
+func TestBuildID(t *testing.T) { testBuildID(t) }
diff --git a/misc/cgo/test/cgo_test.go b/misc/cgo/test/cgo_test.go
index eb237725a4..3cc83060fc 100644
--- a/misc/cgo/test/cgo_test.go
+++ b/misc/cgo/test/cgo_test.go
@@ -53,5 +53,7 @@ func Test5986(t *testing.T) { test5986(t) }
func Test7665(t *testing.T) { test7665(t) }
func TestNaming(t *testing.T) { testNaming(t) }
func Test7560(t *testing.T) { test7560(t) }
+func Test5242(t *testing.T) { test5242(t) }
+func Test8092(t *testing.T) { test8092(t) }
func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) }
diff --git a/misc/cgo/test/issue5242.go b/misc/cgo/test/issue5242.go
new file mode 100644
index 0000000000..fe0a6321c1
--- /dev/null
+++ b/misc/cgo/test/issue5242.go
@@ -0,0 +1,31 @@
+// 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.
+
+// Issue 5242. Cgo incorrectly computed the alignment of structs
+// with no Go accessible fields as 0, and then panicked on
+// modulo-by-zero computations.
+
+package cgotest
+
+/*
+typedef struct {
+} foo;
+
+typedef struct {
+ int x : 1;
+} bar;
+
+int issue5242(foo f, bar b) {
+ return 5242;
+}
+*/
+import "C"
+
+import "testing"
+
+func test5242(t *testing.T) {
+ if got := C.issue5242(C.foo{}, C.bar{}); got != 5242 {
+ t.Errorf("got %v", got)
+ }
+}
diff --git a/misc/cgo/test/issue5548.go b/misc/cgo/test/issue5548.go
index b41f465623..c879f2ae91 100644
--- a/misc/cgo/test/issue5548.go
+++ b/misc/cgo/test/issue5548.go
@@ -14,13 +14,14 @@ import "C"
//export issue5548FromC
func issue5548FromC(s string, i int) int {
if len(s) == 4 && s == "test" && i == 42 {
- return 1
+ return 12345
}
- return 0
+ println("got", len(s), i)
+ return 9876
}
func test5548(t *testing.T) {
- if C.issue5548_in_c() == 0 {
- t.Fail()
+ if x := C.issue5548_in_c(); x != 12345 {
+ t.Errorf("issue5548_in_c = %d, want %d", x, 12345)
}
}
diff --git a/misc/cgo/test/issue7695_test.go b/misc/cgo/test/issue7695_test.go
index 4bd6f8e734..de2fc03d42 100644
--- a/misc/cgo/test/issue7695_test.go
+++ b/misc/cgo/test/issue7695_test.go
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build ignore
+// This test depends on running C code on Go stacks. Not allowed anymore.
+
// Demo of deferred C function with untrue prototype
// breaking stack copying. See golang.org/issue/7695.
diff --git a/misc/cgo/test/issue8092.go b/misc/cgo/test/issue8092.go
new file mode 100644
index 0000000000..8a14ce6d7a
--- /dev/null
+++ b/misc/cgo/test/issue8092.go
@@ -0,0 +1,36 @@
+// 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.
+
+// Issue 8092. Test that linker defined symbols (e.g., text, data) don't
+// conflict with C symbols.
+
+package cgotest
+
+/*
+char text[] = "text";
+char data[] = "data";
+char *ctext(void) { return text; }
+char *cdata(void) { return data; }
+*/
+import "C"
+
+import "testing"
+
+func test8092(t *testing.T) {
+ tests := []struct {
+ s string
+ a, b *C.char
+ }{
+ {"text", &C.text[0], C.ctext()},
+ {"data", &C.data[0], C.cdata()},
+ }
+ for _, test := range tests {
+ if test.a != test.b {
+ t.Errorf("%s: pointer mismatch: %v != %v", test.s, test.a, test.b)
+ }
+ if got := C.GoString(test.a); got != test.s {
+ t.Errorf("%s: points at %#v, want %#v", test.s, got, test.s)
+ }
+ }
+}
diff --git a/misc/cgo/test/issue8428.go b/misc/cgo/test/issue8428.go
new file mode 100644
index 0000000000..a3dc5755ce
--- /dev/null
+++ b/misc/cgo/test/issue8428.go
@@ -0,0 +1,52 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This test fails on older versions of OS X because they use older buggy
+// versions of Clang that emit ambiguous DWARF info. See issue 8611.
+// +build !darwin
+
+package cgotest
+
+// Issue 8428. Cgo inconsistently translated zero size arrays.
+
+/*
+struct issue8428one {
+ char b;
+ char rest[];
+};
+
+struct issue8428two {
+ void *p;
+ char b;
+ char rest[0];
+};
+
+struct issue8428three {
+ char w[1][2][3][0];
+ char x[2][3][0][1];
+ char y[3][0][1][2];
+ char z[0][1][2][3];
+};
+*/
+import "C"
+
+import "unsafe"
+
+var _ = C.struct_issue8428one{
+ b: C.char(0),
+ rest: [0]C.char{},
+}
+
+var _ = C.struct_issue8428two{
+ p: unsafe.Pointer(nil),
+ b: C.char(0),
+ rest: [0]C.char{},
+}
+
+var _ = C.struct_issue8428three{
+ w: [1][2][3][0]C.char{},
+ x: [2][3][0][1]C.char{},
+ y: [3][0][1][2]C.char{},
+ z: [0][1][2][3]C.char{},
+}
diff --git a/misc/cgo/testcdefs/cdefstest.go b/misc/cgo/testcdefs/cdefstest.go
index 0804083a03..5e613c79e7 100644
--- a/misc/cgo/testcdefs/cdefstest.go
+++ b/misc/cgo/testcdefs/cdefstest.go
@@ -56,4 +56,5 @@ typedef struct timespec {
import "C"
type CdefsTest C.struct_cdefsTest
-type PackedTest C.struct_packedTest
+
+//type PackedTest C.struct_packedTest
diff --git a/misc/cgo/testcdefs/main.c b/misc/cgo/testcdefs/main.c
index c13a804306..594a431677 100644
--- a/misc/cgo/testcdefs/main.c
+++ b/misc/cgo/testcdefs/main.c
@@ -17,6 +17,8 @@ struct CdefsOrig {
int8 **array5[20][20];
};
+// Packed structs are no longer supported for -cdefs.
+/*
typedef struct PackedOrig PackedOrig;
#pragma pack on
struct PackedOrig {
@@ -25,14 +27,15 @@ struct PackedOrig {
int64 third;
};
#pragma pack off
+*/
void
main·test(int32 ret)
{
CdefsOrig o;
CdefsTest t;
- PackedOrig po;
- PackedTest pt;
+ // PackedOrig po;
+ // PackedTest pt;
ret = 0;
if(sizeof(t.array1) != sizeof(o.array1) || offsetof(CdefsTest, array1[0]) != offsetof(CdefsOrig, array1[0])) {
@@ -55,6 +58,7 @@ main·test(int32 ret)
runtime·printf("array5: size, offset = %d, %d, want %d, %d\n", sizeof(t.array5), offsetof(CdefsTest, array5[0][0]), sizeof(o.array5), offsetof(CdefsOrig, array5[0][0]));
ret = 1;
}
+/*
if(sizeof(pt.first) != sizeof(po.first) || offsetof(PackedTest, first) != offsetof(PackedOrig, first)) {
runtime·printf("first: size, offset = %d, %d, want %d, %d\n", sizeof(pt.first), offsetof(PackedTest, first), sizeof(po.first), offsetof(PackedOrig, first));
ret = 1;
@@ -67,5 +71,6 @@ main·test(int32 ret)
runtime·printf("third: size, offset = %d, %d, want %d, %d\n", sizeof(pt.third), offsetof(PackedTest, third), sizeof(po.third), offsetof(PackedOrig, third));
ret = 1;
}
+*/
FLUSH(&ret); // flush return value
}
diff --git a/misc/makerelease/makerelease.go b/misc/makerelease/makerelease.go
index f67cd5414c..369ab5cb0b 100644
--- a/misc/makerelease/makerelease.go
+++ b/misc/makerelease/makerelease.go
@@ -30,7 +30,7 @@ import (
"strings"
"code.google.com/p/goauth2/oauth"
- "code.google.com/p/google-api-go-client/storage/v1beta2"
+ storage "code.google.com/p/google-api-go-client/storage/v1beta2"
)
var (
@@ -154,6 +154,7 @@ func main() {
log.Fatalln("setupOAuthClient:", err)
}
}
+ ok := true
for _, targ := range flag.Args() {
var b Build
if m := fileRe.FindStringSubmatch(targ); m != nil {
@@ -205,8 +206,12 @@ func main() {
}
if err := b.Do(); err != nil {
log.Printf("%s: %v", targ, err)
+ ok = false
}
}
+ if !ok {
+ os.Exit(1)
+ }
}
type Build struct {
diff --git a/misc/nacl/README b/misc/nacl/README
index 710587dfce..2044b356b6 100644
--- a/misc/nacl/README
+++ b/misc/nacl/README
@@ -1,12 +1,14 @@
Native Client
=============
-This document outlines the basics of building and developing the Go runtime and programs in the Native Client (NaCl) environment.
+This document outlines the basics of building and developing the Go runtime and
+programs in the Native Client (NaCl) environment.
Go 1.3 supports three architectures
* nacl/386 which is standard 386.
- * nacl/amd64p32 which is a 64 bit architecture, where the address space is limited to a 4gb window.
+ * nacl/amd64p32 which is a 64 bit architecture, where the address space is
+ limited to a 4gb window.
* nacl/arm which is 32-bit ARMv7A architecture with 1GB address space.
For background it is recommended that you read http://golang.org/s/go13nacl.
@@ -14,34 +16,48 @@ For background it is recommended that you read http://golang.org/s/go13nacl.
Prerequisites
-------------
-Native Client programs are executed inside a sandbox, the NaCl runtime. This runtime must be installed before you can use NaCl programs.
+Native Client programs are executed inside a sandbox, the NaCl runtime. This
+runtime must be installed before you can use NaCl programs.
-The NaCl distribution comes with an installer which ensures you have access to the latest version of the runtime. The version tracks the Chrome numbering scheme.
+The NaCl distribution comes with an installer which ensures you have access to
+the latest version of the runtime. The version tracks the Chrome numbering
+scheme.
# Download NaCl
-Download nacl_sdk.zip file from https://developers.google.com/native-client/dev/sdk/download, and unpack it. I chose /opt/nacl_sdk
+Download nacl_sdk.zip file from
+ https://developers.google.com/native-client/dev/sdk/download
+and unpack it. I chose /opt/nacl_sdk.
# Update
-The zip file contains a small skeleton that can be used to download the correct sdk. These are released every 6-8 weeks, in line with Chrome releases.
+The zip file contains a small skeleton that can be used to download the correct
+sdk. These are released every 6-8 weeks, in line with Chrome releases.
% cd /opt/nacl_sdk
% ./naclsdk update
-At this time pepper_33 is the stable version. If naclsdk downloads a later version, please adjust accordingly. As of June 2014, only the canary sdk provides support for nacl/arm.
+At this time pepper_34 is the stable version. If naclsdk downloads a later
+version, please adjust accordingly. As of June 2014, only the canary sdk
+provides support for nacl/arm.
-The cmd/go helper scripts expect that the runtime loaders, sel_ldr_{x86_{32,64},arm} and nacl_helper_bootstrap_arm are in your path. I find it easiest to make a symlink from the NaCl distribution to my $GOPATH/bin directory.
+The cmd/go helper scripts expect that the loaders sel_ldr_{x86_{32,64},arm} and
+nacl_helper_bootstrap_arm are in your path. I find it easiest to make a symlink
+from the NaCl distribution to my $GOPATH/bin directory.
- % ln -nfs /opt/nacl_sdk/pepper_33/tools/sel_ldr_x86_32 $GOPATH/bin/sel_ldr_x86_32
- % ln -nfs /opt/nacl_sdk/pepper_33/tools/sel_ldr_x86_64 $GOPATH/bin/sel_ldr_x86_64
+ % ln -nfs /opt/nacl_sdk/pepper_34/tools/sel_ldr_x86_32 $GOPATH/bin/sel_ldr_x86_32
+ % ln -nfs /opt/nacl_sdk/pepper_34/tools/sel_ldr_x86_64 $GOPATH/bin/sel_ldr_x86_64
% ln -nfs /opt/nacl_sdk/pepper_canary/tools/sel_ldr_arm $GOPATH/bin/sel_ldr_arm
- % ln -nfs /opt/nacl_sdk/pepper_canary/tools/nacl_helper_bootstrap_arm $GOPATH/bin/nacl_helper_bootstrap_arm # only required for NaCl/ARM.
+
+Additionally, for NaCl/ARM only:
+
+ % ln -nfs /opt/nacl_sdk/pepper_canary/tools/nacl_helper_bootstrap_arm $GOPATH/bin/nacl_helper_bootstrap_arm
Support scripts
---------------
-Symlink the two scripts in this directory into your $PATH, just as you did with NaCl sdk above.
+Symlink the two scripts in this directory into your $PATH, just as you did with
+NaCl sdk above.
% ln -nfs $GOROOT/go/misc/nacl/go_nacl_amd64p32_exec $GOPATH/bin/go_nacl_amd64p32_exec
% ln -nfs $GOROOT/go/misc/nacl/go_nacl_386_exec $GOPATH/bin/go_nacl_386_exec
@@ -50,18 +66,57 @@ Symlink the two scripts in this directory into your $PATH, just as you did with
Building and testing
--------------------
-Building for NaCl is similar to cross compiling for other platforms. However, as it is not possible to ever build in a `native` NaCl environment, the cmd/go tool has been enhanced to allow the full build, all.bash, to be executed, rather than just the compile stage, make.bash.
+Building for NaCl is similar to cross compiling for other platforms. However,
+as it is not possible to ever build in a `native` NaCl environment, the cmd/go
+tool has been enhanced to allow the full build, all.bash, to be executed,
+rather than just the compile stage, make.bash.
-The cmd/go tool knows that if GOOS is set to `nacl` it should not try to execute any binaries itself. Instead it passes their execution to a support script which sets up a Native Client environment and invokes the NaCl sandbox.
+The cmd/go tool knows that if GOOS is set to `nacl` it should not try to
+execute any binaries itself. Instead it passes their execution to a support
+script which sets up a Native Client environment and invokes the NaCl sandbox.
-The script's name has a special format, go_$GOOS_$GOARCH_exec, so cmd/go can find it.
+The script's name has a special format, go_$GOOS_$GOARCH_exec, so cmd/go can
+find it.
-In short, if the support scripts are in place, the cmd/go tool can be used as per normal.
+In short, if the support scripts are in place, the cmd/go tool can be used as
+per normal.
# Build and test Go for NaCl
-NaCl does not permit direct file system access. Instead, package syscall provides a simulated file system served by in-memory data. The script nacltest.bash is the NaCl equivalent of all.bash. It builds NaCl with an in-memory file system containing files needed for tests, and then it runs the tests.
+NaCl does not permit direct file system access. Instead, package syscall
+provides a simulated file system served by in-memory data. The script
+nacltest.bash is the NaCl equivalent of all.bash. It builds NaCl with an
+in-memory file system containing files needed for tests, and then it runs the
+tests.
% cd go/src
% env GOARCH=amd64p32 ./nacltest.bash
+Debugging
+---------
+
+Assuming that you have built nacl/amd64p32 binary ./mybin and can run as:
+
+ % sel_ldr_x86_64 -l /dev/null -S -e ./mybin
+
+Create the nacl manifest file mybin.manifest with the following contents:
+
+ { "program": { "x86-64": { "url": "mybin" } } }
+
+url is the path to the binary relative to the manifest file.
+Then, run the program as:
+
+ % sel_ldr_x86_64 -g -l /dev/null -S -e ./mybin
+
+The -g flag instructs the loader to stop at startup. Then, in another console:
+
+ % /opt/nacl_sdk/pepper_34/toolchain/linux_x86_glibc/bin/x86_64-nacl-gdb
+ % nacl-manifest mybin.manifest
+ % target remote :4014
+
+If you see that the program is stopped in _rt0_amd64p32_nacl, then symbols are
+loaded successfully and you can type 'c' to start the program.
+Next time you can automate it as:
+
+ % /opt/nacl_sdk/pepper_34/toolchain/linux_x86_glibc/bin/x86_64-nacl-gdb \
+ -ex 'nacl-manifest mybin.manifest' -ex 'target remote :4014'
diff --git a/misc/nacl/testzip.proto b/misc/nacl/testzip.proto
index 57c2e1b08a..89e1e1193a 100644
--- a/misc/nacl/testzip.proto
+++ b/misc/nacl/testzip.proto
@@ -10,7 +10,12 @@ usr src=../misc/nacl/testdata
go src=..
src
cmd
+ internal
+ objfile
+ objfile.go
gofmt
+ gofmt.go
+ gofmt_test.go
testdata
+
link
diff --git a/src/androidtest.bash b/src/androidtest.bash
index 9bf7773b23..43a93a141b 100755
--- a/src/androidtest.bash
+++ b/src/androidtest.bash
@@ -47,7 +47,9 @@ ln -s $GOROOT/src/cmd $FAKE_GOROOT/src/cmd
ln -s $GOROOT/src/pkg $FAKE_GOROOT/src/pkg
ln -s $GOROOT/test $FAKE_GOROOT/test
ln -s $GOROOT/lib $FAKE_GOROOT/lib
-adb sync data
+echo '# Syncing test files to android device'
+time adb sync data &> /dev/null
+echo ''
rm -rf "$ANDROID_PRODUCT_OUT"
# Run standard build and tests.
diff --git a/src/cmd/5c/cgen.c b/src/cmd/5c/cgen.c
index 08ed36055a..5ea8eea073 100644
--- a/src/cmd/5c/cgen.c
+++ b/src/cmd/5c/cgen.c
@@ -46,7 +46,7 @@ _cgen(Node *n, Node *nn, int inrel)
}
if(n == Z || n->type == T)
return;
- if(typesuv[n->type->etype]) {
+ if(typesuv[n->type->etype] && (n->op != OFUNC || nn != Z)) {
sugen(n, nn, n->type->width);
return;
}
@@ -75,7 +75,7 @@ _cgen(Node *n, Node *nn, int inrel)
if(r != Z && r->complex >= FNX)
switch(o) {
default:
- regret(&nod, r);
+ regret(&nod, r, 0, 0);
cgen(r, &nod);
regsalloc(&nod1, r);
@@ -107,7 +107,7 @@ _cgen(Node *n, Node *nn, int inrel)
if(l->addable >= INDEXED && l->complex < FNX) {
if(nn != Z || r->addable < INDEXED) {
if(r->complex >= FNX && nn == Z)
- regret(&nod, r);
+ regret(&nod, r, 0, 0);
else
regalloc(&nod, r, nn);
cgen(r, &nod);
@@ -348,7 +348,7 @@ _cgen(Node *n, Node *nn, int inrel)
if(l->op != OIND)
diag(n, "bad function call");
- regret(&nod, l->left);
+ regret(&nod, l->left, 0, 0);
cgen(l->left, &nod);
regsalloc(&nod1, l->left);
gopcode(OAS, &nod, Z, &nod1);
@@ -377,11 +377,11 @@ _cgen(Node *n, Node *nn, int inrel)
if(REGARG >= 0)
if(o != reg[REGARG])
reg[REGARG]--;
- if(nn != Z) {
- regret(&nod, n);
- gopcode(OAS, &nod, Z, nn);
+ regret(&nod, n, l->type, 1);
+ if(nn != Z)
+ gmove(&nod, nn);
+ if(nod.op == OREGISTER)
regfree(&nod);
- }
break;
case OIND:
@@ -823,7 +823,7 @@ boolgen(Node *n, int true, Node *nn)
if(true)
o = comrel[relindex(o)];
if(l->complex >= FNX && r->complex >= FNX) {
- regret(&nod, r);
+ regret(&nod, r, 0, 0);
cgenrel(r, &nod);
regsalloc(&nod1, r);
gopcode(OAS, &nod, Z, &nod1);
@@ -957,7 +957,7 @@ sugen(Node *n, Node *nn, int32 w)
if(nn != Z && side(nn)) {
nod1 = *n;
nod1.type = typ(TIND, n->type);
- regret(&nod2, &nod1);
+ regret(&nod2, &nod1, 0, 0);
lcgen(nn, &nod2);
regsalloc(&nod0, &nod1);
gopcode(OAS, &nod2, Z, &nod0);
@@ -1036,6 +1036,20 @@ sugen(Node *n, Node *nn, int32 w)
break;
case OFUNC:
+ if(!hasdotdotdot(n->left->type)) {
+ cgen(n, Z);
+ if(nn != Z) {
+ curarg -= n->type->width;
+ regret(&nod1, n, n->left->type, 1);
+ if(nn->complex >= FNX) {
+ regsalloc(&nod2, n);
+ cgen(&nod1, &nod2);
+ nod1 = nod2;
+ }
+ cgen(&nod1, nn);
+ }
+ break;
+ }
if(nn == Z) {
sugen(n, nodrat, w);
break;
diff --git a/src/cmd/5c/gc.h b/src/cmd/5c/gc.h
index 166900c3a9..7417b7dbe3 100644
--- a/src/cmd/5c/gc.h
+++ b/src/cmd/5c/gc.h
@@ -210,7 +210,7 @@ void usedset(Node*, int);
void xcom(Node*);
int bcomplex(Node*, Node*);
Prog* gtext(Sym*, int32);
-vlong argsize(void);
+vlong argsize(int);
/*
* cgen.c
@@ -236,7 +236,7 @@ Node* nodconst(int32);
Node* nod32const(vlong);
Node* nodfconst(double);
void nodreg(Node*, Node*, int);
-void regret(Node*, Node*);
+void regret(Node*, Node*, Type*, int);
int tmpreg(void);
void regalloc(Node*, Node*, Node*);
void regfree(Node*);
diff --git a/src/cmd/5c/sgen.c b/src/cmd/5c/sgen.c
index efcc0437bb..a36612caa5 100644
--- a/src/cmd/5c/sgen.c
+++ b/src/cmd/5c/sgen.c
@@ -36,7 +36,7 @@ gtext(Sym *s, int32 stkoff)
{
int32 a;
- a = argsize();
+ a = argsize(1);
if((textflag & NOSPLIT) != 0 && stkoff >= 128)
yyerror("stack frame too large for NOSPLIT function");
diff --git a/src/cmd/5c/swt.c b/src/cmd/5c/swt.c
index d24a5df9b0..f39963b8f2 100644
--- a/src/cmd/5c/swt.c
+++ b/src/cmd/5c/swt.c
@@ -374,10 +374,11 @@ align(int32 i, Type *t, int op, int32 *maxalign)
{
int32 o;
Type *v;
- int w;
+ int w, packw;
o = i;
w = 1;
+ packw = 0;
switch(op) {
default:
diag(Z, "unknown align opcode %d", op);
@@ -388,7 +389,7 @@ align(int32 i, Type *t, int op, int32 *maxalign)
if(w < 1)
w = 1;
if(packflg)
- w = packflg;
+ packw = packflg;
break;
case Ael1: /* initial align of struct element */
@@ -404,7 +405,7 @@ align(int32 i, Type *t, int op, int32 *maxalign)
if(w < 1 || w > SZ_LONG)
fatal(Z, "align");
if(packflg)
- w = packflg;
+ packw = packflg;
break;
case Ael2: /* width of a struct element */
@@ -440,6 +441,8 @@ align(int32 i, Type *t, int op, int32 *maxalign)
w = SZ_LONG; /* because of a pun in cc/dcl.c:contig() */
break;
}
+ if(packw != 0 && xround(o, w) != xround(o, packw))
+ diag(Z, "#pragma pack changes offset of %T", t);
o = xround(o, w);
if(maxalign != nil && *maxalign < w)
*maxalign = w;
diff --git a/src/cmd/5c/txt.c b/src/cmd/5c/txt.c
index a753510caa..af40220ccb 100644
--- a/src/cmd/5c/txt.c
+++ b/src/cmd/5c/txt.c
@@ -274,15 +274,43 @@ nodreg(Node *n, Node *nn, int reg)
}
void
-regret(Node *n, Node *nn)
+regret(Node *n, Node *nn, Type *t, int mode)
{
int r;
- r = REGRET;
- if(typefd[nn->type->etype])
- r = FREGRET+NREG;
- nodreg(n, nn, r);
- reg[r]++;
+ if(mode == 0 || hasdotdotdot(t) || nn->type->width == 0) {
+ r = REGRET;
+ if(typefd[nn->type->etype])
+ r = FREGRET+NREG;
+ nodreg(n, nn, r);
+ reg[r]++;
+ return;
+ }
+
+ if(mode == 1) {
+ // fetch returned value after call.
+ // already called gargs, so curarg is set.
+ curarg = (curarg+3) & ~3;
+ regaalloc(n, nn);
+ return;
+ }
+
+ if(mode == 2) {
+ // store value to be returned.
+ // must compute arg offset.
+ if(t->etype != TFUNC)
+ fatal(Z, "bad regret func %T", t);
+ *n = *nn;
+ n->op = ONAME;
+ n->class = CPARAM;
+ n->sym = slookup(".ret");
+ n->complex = nodret->complex;
+ n->xoffset = argsize(0);
+ n->addable = 20;
+ return;
+ }
+
+ fatal(Z, "bad regret");
}
int
diff --git a/src/cmd/5g/cgen.c b/src/cmd/5g/cgen.c
index a42e67bafb..5fae7a564c 100644
--- a/src/cmd/5g/cgen.c
+++ b/src/cmd/5g/cgen.c
@@ -254,7 +254,6 @@ cgen(Node *n, Node *res)
case OOR:
case OXOR:
case OADD:
- case OADDPTR:
case OMUL:
a = optoas(n->op, nl->type);
goto sbop;
@@ -1635,7 +1634,10 @@ int
componentgen(Node *nr, Node *nl)
{
Node nodl, nodr, tmp;
+ Type *t;
int freel, freer;
+ vlong fldcount;
+ vlong loffset, roffset;
freel = 0;
freer = 0;
@@ -1645,8 +1647,33 @@ componentgen(Node *nr, Node *nl)
goto no;
case TARRAY:
- if(!isslice(nl->type))
+ t = nl->type;
+
+ // Slices are ok.
+ if(isslice(t))
+ break;
+ // Small arrays are ok.
+ if(t->bound > 0 && t->bound <= 3 && !isfat(t->type))
+ break;
+
+ goto no;
+
+ case TSTRUCT:
+ // Small structs with non-fat types are ok.
+ // Zero-sized structs are treated separately elsewhere.
+ fldcount = 0;
+ for(t=nl->type->type; t; t=t->down) {
+ if(isfat(t->type))
+ goto no;
+ if(t->etype != TFIELD)
+ fatal("componentgen: not a TFIELD: %lT", t);
+ fldcount++;
+ }
+ if(fldcount == 0 || fldcount > 4)
goto no;
+
+ break;
+
case TSTRING:
case TINTER:
break;
@@ -1674,6 +1701,7 @@ componentgen(Node *nr, Node *nl)
freer = 1;
}
+
// nl and nr are 'cadable' which basically means they are names (variables) now.
// If they are the same variable, don't generate any code, because the
// VARDEF we generate will mark the old value as dead incorrectly.
@@ -1683,8 +1711,25 @@ componentgen(Node *nr, Node *nl)
switch(nl->type->etype) {
case TARRAY:
+ // componentgen for arrays.
if(nl->op == ONAME)
gvardef(nl);
+ t = nl->type;
+ if(!isslice(t)) {
+ nodl.type = t->type;
+ nodr.type = nodl.type;
+ for(fldcount=0; fldcount < t->bound; fldcount++) {
+ if(nr == N)
+ clearslim(&nodl);
+ else
+ gmove(&nodr, &nodl);
+ nodl.xoffset += t->type->width;
+ nodr.xoffset += t->type->width;
+ }
+ goto yes;
+ }
+
+ // componentgen for slices.
nodl.xoffset += Array_array;
nodl.type = ptrto(nl->type->type);
@@ -1759,6 +1804,31 @@ componentgen(Node *nr, Node *nl)
gmove(&nodr, &nodl);
goto yes;
+
+ case TSTRUCT:
+ if(nl->op == ONAME)
+ gvardef(nl);
+ loffset = nodl.xoffset;
+ roffset = nodr.xoffset;
+ // funarg structs may not begin at offset zero.
+ if(nl->type->etype == TSTRUCT && nl->type->funarg && nl->type->type)
+ loffset -= nl->type->type->width;
+ if(nr != N && nr->type->etype == TSTRUCT && nr->type->funarg && nr->type->type)
+ roffset -= nr->type->type->width;
+
+ for(t=nl->type->type; t; t=t->down) {
+ nodl.xoffset = loffset + t->width;
+ nodl.type = t->type;
+
+ if(nr == N)
+ clearslim(&nodl);
+ else {
+ nodr.xoffset = roffset + t->width;
+ nodr.type = nodl.type;
+ gmove(&nodr, &nodl);
+ }
+ }
+ goto yes;
}
no:
diff --git a/src/cmd/5g/gsubr.c b/src/cmd/5g/gsubr.c
index 1241a23ea6..5a70fcddaf 100644
--- a/src/cmd/5g/gsubr.c
+++ b/src/cmd/5g/gsubr.c
@@ -1599,7 +1599,6 @@ optoas(int op, Type *t)
case CASE(OADD, TINT32):
case CASE(OADD, TUINT32):
case CASE(OADD, TPTR32):
- case CASE(OADDPTR, TPTR32):
a = AADD;
break;
diff --git a/src/cmd/5g/reg.c b/src/cmd/5g/reg.c
index d259a232e6..8e49a2d9c8 100644
--- a/src/cmd/5g/reg.c
+++ b/src/cmd/5g/reg.c
@@ -1315,6 +1315,7 @@ void
addreg(Adr *a, int rn)
{
a->sym = nil;
+ a->node = nil;
a->name = D_NONE;
a->type = D_REG;
a->reg = rn;
diff --git a/src/cmd/5l/asm.c b/src/cmd/5l/asm.c
index 02b4c78f11..9c1c04e2d4 100644
--- a/src/cmd/5l/asm.c
+++ b/src/cmd/5l/asm.c
@@ -599,10 +599,10 @@ asmb(void)
if(iself)
goto ElfSym;
case Hplan9:
- symo = HEADR+segtext.len+segdata.filelen;
+ symo = segdata.fileoff+segdata.filelen;
break;
ElfSym:
- symo = rnd(HEADR+segtext.filelen, INITRND)+rnd(HEADR+segrodata.filelen, INITRND)+segdata.filelen;
+ symo = segdata.fileoff+segdata.filelen;
symo = rnd(symo, INITRND);
break;
}
diff --git a/src/cmd/6a/lex.c b/src/cmd/6a/lex.c
index 167e6b6c17..b50e1622e2 100644
--- a/src/cmd/6a/lex.c
+++ b/src/cmd/6a/lex.c
@@ -435,49 +435,49 @@ struct
"IRETQ", LTYPE0, AIRETQ,
"IRETW", LTYPE0, AIRETW,
- "JOS", LTYPER, AJOS,
+ "JOS", LTYPER, AJOS, /* overflow set (OF = 1) */
"JO", LTYPER, AJOS, /* alternate */
- "JOC", LTYPER, AJOC,
+ "JOC", LTYPER, AJOC, /* overflow clear (OF = 0) */
"JNO", LTYPER, AJOC, /* alternate */
- "JCS", LTYPER, AJCS,
+ "JCS", LTYPER, AJCS, /* carry set (CF = 1) */
"JB", LTYPER, AJCS, /* alternate */
"JC", LTYPER, AJCS, /* alternate */
"JNAE", LTYPER, AJCS, /* alternate */
"JLO", LTYPER, AJCS, /* alternate */
- "JCC", LTYPER, AJCC,
+ "JCC", LTYPER, AJCC, /* carry clear (CF = 0) */
"JAE", LTYPER, AJCC, /* alternate */
"JNB", LTYPER, AJCC, /* alternate */
"JNC", LTYPER, AJCC, /* alternate */
"JHS", LTYPER, AJCC, /* alternate */
- "JEQ", LTYPER, AJEQ,
+ "JEQ", LTYPER, AJEQ, /* equal (ZF = 1) */
"JE", LTYPER, AJEQ, /* alternate */
"JZ", LTYPER, AJEQ, /* alternate */
- "JNE", LTYPER, AJNE,
+ "JNE", LTYPER, AJNE, /* not equal (ZF = 0) */
"JNZ", LTYPER, AJNE, /* alternate */
- "JLS", LTYPER, AJLS,
+ "JLS", LTYPER, AJLS, /* lower or same (unsigned) (CF = 1 || ZF = 1) */
"JBE", LTYPER, AJLS, /* alternate */
"JNA", LTYPER, AJLS, /* alternate */
- "JHI", LTYPER, AJHI,
+ "JHI", LTYPER, AJHI, /* higher (unsigned) (CF = 0 && ZF = 0) */
"JA", LTYPER, AJHI, /* alternate */
"JNBE", LTYPER, AJHI, /* alternate */
- "JMI", LTYPER, AJMI,
+ "JMI", LTYPER, AJMI, /* negative (minus) (SF = 1) */
"JS", LTYPER, AJMI, /* alternate */
- "JPL", LTYPER, AJPL,
+ "JPL", LTYPER, AJPL, /* non-negative (plus) (SF = 0) */
"JNS", LTYPER, AJPL, /* alternate */
- "JPS", LTYPER, AJPS,
+ "JPS", LTYPER, AJPS, /* parity set (PF = 1) */
"JP", LTYPER, AJPS, /* alternate */
"JPE", LTYPER, AJPS, /* alternate */
- "JPC", LTYPER, AJPC,
+ "JPC", LTYPER, AJPC, /* parity clear (PF = 0) */
"JNP", LTYPER, AJPC, /* alternate */
"JPO", LTYPER, AJPC, /* alternate */
- "JLT", LTYPER, AJLT,
+ "JLT", LTYPER, AJLT, /* less than (signed) (SF != OF) */
"JL", LTYPER, AJLT, /* alternate */
"JNGE", LTYPER, AJLT, /* alternate */
- "JGE", LTYPER, AJGE,
+ "JGE", LTYPER, AJGE, /* greater than or equal (signed) (SF = OF) */
"JNL", LTYPER, AJGE, /* alternate */
- "JLE", LTYPER, AJLE,
+ "JLE", LTYPER, AJLE, /* less than or equal (signed) (ZF = 1 || SF != OF) */
"JNG", LTYPER, AJLE, /* alternate */
- "JGT", LTYPER, AJGT,
+ "JGT", LTYPER, AJGT, /* greater than (signed) (ZF = 0 && SF = OF) */
"JG", LTYPER, AJGT, /* alternate */
"JNLE", LTYPER, AJGT, /* alternate */
"JCXZL", LTYPER, AJCXZL,
@@ -612,7 +612,7 @@ struct
"SCASL", LTYPE0, ASCASL,
"SCASQ", LTYPE0, ASCASQ,
"SCASW", LTYPE0, ASCASW,
- "SETCC", LTYPE1, ASETCC,
+ "SETCC", LTYPE1, ASETCC, /* see JCC etc above for condition codes */
"SETCS", LTYPE1, ASETCS,
"SETEQ", LTYPE1, ASETEQ,
"SETGE", LTYPE1, ASETGE,
diff --git a/src/cmd/6c/cgen.c b/src/cmd/6c/cgen.c
index bdef76ff08..b66c6add3e 100644
--- a/src/cmd/6c/cgen.c
+++ b/src/cmd/6c/cgen.c
@@ -51,7 +51,7 @@ cgen(Node *n, Node *nn)
}
if(n == Z || n->type == T)
return;
- if(typesu[n->type->etype]) {
+ if(typesu[n->type->etype] && (n->op != OFUNC || nn != Z)) {
sugen(n, nn, n->type->width);
return;
}
@@ -88,7 +88,7 @@ cgen(Node *n, Node *nn)
if(cond(o) && typesu[l->type->etype])
break;
- regret(&nod, r);
+ regret(&nod, r, 0, 0);
cgen(r, &nod);
regsalloc(&nod1, r);
@@ -135,7 +135,7 @@ cgen(Node *n, Node *nn)
if(!hardleft) {
if(nn != Z || r->addable < INDEXED || hardconst(r)) {
if(r->complex >= FNX && nn == Z)
- regret(&nod, r);
+ regret(&nod, r, 0, 0);
else
regalloc(&nod, r, nn);
cgen(r, &nod);
@@ -929,7 +929,7 @@ cgen(Node *n, Node *nn)
if(l->op != OIND)
diag(n, "bad function call");
- regret(&nod, l->left);
+ regret(&nod, l->left, 0, 0);
cgen(l->left, &nod);
regsalloc(&nod1, l->left);
gmove(&nod, &nod1);
@@ -956,11 +956,13 @@ cgen(Node *n, Node *nn)
gpcdata(PCDATA_ArgSize, -1);
if(REGARG >= 0 && reg[REGARG])
reg[REGARG]--;
- if(nn != Z) {
- regret(&nod, n);
+ regret(&nod, n, l->type, 1); // update maxarg if nothing else
+ gpcdata(PCDATA_ArgSize, curarg);
+ gpcdata(PCDATA_ArgSize, -1);
+ if(nn != Z)
gmove(&nod, nn);
+ if(nod.op == OREGISTER)
regfree(&nod);
- }
break;
case OIND:
@@ -1382,7 +1384,7 @@ boolgen(Node *n, int true, Node *nn)
if(true)
o = comrel[relindex(o)];
if(l->complex >= FNX && r->complex >= FNX) {
- regret(&nod, r);
+ regret(&nod, r, 0, 0);
cgen(r, &nod);
regsalloc(&nod1, r);
gmove(&nod, &nod1);
@@ -1535,7 +1537,7 @@ sugen(Node *n, Node *nn, int32 w)
if(nn != Z && side(nn)) {
nod1 = *n;
nod1.type = typ(TIND, n->type);
- regret(&nod2, &nod1);
+ regret(&nod2, &nod1, 0, 0);
lcgen(nn, &nod2);
regsalloc(&nod0, &nod1);
cgen(&nod2, &nod0);
@@ -1617,6 +1619,20 @@ sugen(Node *n, Node *nn, int32 w)
break;
case OFUNC:
+ if(!hasdotdotdot(n->left->type)) {
+ cgen(n, Z);
+ if(nn != Z) {
+ curarg -= n->type->width;
+ regret(&nod1, n, n->left->type, 1);
+ if(nn->complex >= FNX) {
+ regsalloc(&nod2, n);
+ cgen(&nod1, &nod2);
+ nod1 = nod2;
+ }
+ cgen(&nod1, nn);
+ }
+ break;
+ }
if(nn == Z) {
sugen(n, nodrat, w);
break;
diff --git a/src/cmd/6c/gc.h b/src/cmd/6c/gc.h
index bc4e36ccd8..aa9d95d21d 100644
--- a/src/cmd/6c/gc.h
+++ b/src/cmd/6c/gc.h
@@ -210,7 +210,7 @@ void xcom(Node*);
void indx(Node*);
int bcomplex(Node*, Node*);
Prog* gtext(Sym*, int32);
-vlong argsize(void);
+vlong argsize(int);
/*
* cgen.c
@@ -239,7 +239,7 @@ Node* nodfconst(double);
Node* nodgconst(vlong, Type*);
int nodreg(Node*, Node*, int);
int isreg(Node*, int);
-void regret(Node*, Node*);
+void regret(Node*, Node*, Type*, int);
void regalloc(Node*, Node*, Node*);
void regfree(Node*);
void regialloc(Node*, Node*, Node*);
diff --git a/src/cmd/6c/sgen.c b/src/cmd/6c/sgen.c
index ba1c1f652d..d99510185e 100644
--- a/src/cmd/6c/sgen.c
+++ b/src/cmd/6c/sgen.c
@@ -36,7 +36,7 @@ gtext(Sym *s, int32 stkoff)
{
vlong v;
- v = ((uvlong)argsize() << 32) | (stkoff & 0xffffffff);
+ v = ((uvlong)argsize(1) << 32) | (stkoff & 0xffffffff);
if((textflag & NOSPLIT) && stkoff >= 128)
yyerror("stack frame too large for NOSPLIT function");
@@ -124,10 +124,7 @@ xcom(Node *n)
break;
case ONAME:
- if(flag_largemodel)
- n->addable = 9;
- else
- n->addable = 10;
+ n->addable = 9;
if(n->class == CPARAM || n->class == CAUTO)
n->addable = 11;
break;
diff --git a/src/cmd/6c/swt.c b/src/cmd/6c/swt.c
index d7713648de..6e918eb109 100644
--- a/src/cmd/6c/swt.c
+++ b/src/cmd/6c/swt.c
@@ -250,10 +250,11 @@ align(int32 i, Type *t, int op, int32 *maxalign)
{
int32 o;
Type *v;
- int w;
+ int w, packw;
o = i;
w = 1;
+ packw = 0;
switch(op) {
default:
diag(Z, "unknown align opcode %d", op);
@@ -264,7 +265,7 @@ align(int32 i, Type *t, int op, int32 *maxalign)
if(w < 1)
w = 1;
if(packflg)
- w = packflg;
+ packw = packflg;
break;
case Ael1: /* initial align of struct element */
@@ -277,7 +278,7 @@ align(int32 i, Type *t, int op, int32 *maxalign)
if(w < 1 || w > SZ_VLONG)
fatal(Z, "align");
if(packflg)
- w = packflg;
+ packw = packflg;
break;
case Ael2: /* width of a struct element */
@@ -331,6 +332,8 @@ align(int32 i, Type *t, int op, int32 *maxalign)
o = align(o, t, Ael2, nil);
break;
}
+ if(packw != 0 && xround(o, w) != xround(o, packw))
+ diag(Z, "#pragma pack changes offset of %T", t);
o = xround(o, w);
if(maxalign && *maxalign < w)
*maxalign = w;
diff --git a/src/cmd/6c/txt.c b/src/cmd/6c/txt.c
index 4d07436c3f..3bdbf410ea 100644
--- a/src/cmd/6c/txt.c
+++ b/src/cmd/6c/txt.c
@@ -351,15 +351,43 @@ nodreg(Node *n, Node *nn, int r)
}
void
-regret(Node *n, Node *nn)
+regret(Node *n, Node *nn, Type *t, int mode)
{
int r;
+
+ if(mode == 0 || hasdotdotdot(t) || nn->type->width == 0) {
+ r = REGRET;
+ if(typefd[nn->type->etype])
+ r = FREGRET;
+ nodreg(n, nn, r);
+ reg[r]++;
+ return;
+ }
+
+ if(mode == 1) {
+ // fetch returned value after call.
+ // already called gargs, so curarg is set.
+ curarg = (curarg+7) & ~7;
+ regaalloc(n, nn);
+ return;
+ }
- r = REGRET;
- if(typefd[nn->type->etype])
- r = FREGRET;
- nodreg(n, nn, r);
- reg[r]++;
+ if(mode == 2) {
+ // store value to be returned.
+ // must compute arg offset.
+ if(t->etype != TFUNC)
+ fatal(Z, "bad regret func %T", t);
+ *n = *nn;
+ n->op = ONAME;
+ n->class = CPARAM;
+ n->sym = slookup(".ret");
+ n->complex = nodret->complex;
+ n->addable = 20;
+ n->xoffset = argsize(0);
+ return;
+ }
+
+ fatal(Z, "bad regret");
}
void
diff --git a/src/cmd/6g/cgen.c b/src/cmd/6g/cgen.c
index 4dd505b086..592e81542c 100644
--- a/src/cmd/6g/cgen.c
+++ b/src/cmd/6g/cgen.c
@@ -247,7 +247,6 @@ cgen(Node *n, Node *res)
case OOR:
case OXOR:
case OADD:
- case OADDPTR:
case OMUL:
a = optoas(n->op, nl->type);
if(a == AIMULB) {
@@ -752,12 +751,7 @@ agenr(Node *n, Node *a, Node *res)
regalloc(&n3, types[tptr], res);
p1 = gins(ALEAQ, N, &n3);
datastring(nl->val.u.sval->s, nl->val.u.sval->len, &p1->from);
- if(flag_largemodel) {
- gins(AADDQ, &n2, &n3);
- } else {
- p1->from.scale = 1;
- p1->from.index = n2.val.u.reg;
- }
+ gins(AADDQ, &n2, &n3);
goto indexdone;
}
diff --git a/src/cmd/6g/gg.h b/src/cmd/6g/gg.h
index a5da17d61f..27f6c01fee 100644
--- a/src/cmd/6g/gg.h
+++ b/src/cmd/6g/gg.h
@@ -51,7 +51,6 @@ void allocparams(void);
void checklabels(void);
void ginscall(Node*, int);
int gen_as_init(Node*);
-void clearslim(Node*);
/*
* cgen.c
diff --git a/src/cmd/6g/ggen.c b/src/cmd/6g/ggen.c
index c385798f2e..9665d831b3 100644
--- a/src/cmd/6g/ggen.c
+++ b/src/cmd/6g/ggen.c
@@ -943,7 +943,7 @@ cgen_hmul(Node *nl, Node *nr, Node *res)
if(t->width == 1) {
// byte multiply behaves differently.
nodreg(&ax, t, D_AH);
- nodreg(&dx, t, D_DL);
+ nodreg(&dx, t, D_DX);
gmove(&ax, &dx);
}
nodreg(&dx, t, D_DX);
diff --git a/src/cmd/6g/gsubr.c b/src/cmd/6g/gsubr.c
index f3464b7e1c..a451d7d624 100644
--- a/src/cmd/6g/gsubr.c
+++ b/src/cmd/6g/gsubr.c
@@ -598,11 +598,8 @@ ismem(Node *n)
case ONAME:
case OPARAM:
case OCLOSUREVAR:
- return 1;
case OADDR:
- if(flag_largemodel)
- return 1;
- break;
+ return 1;
}
return 0;
}
@@ -1540,14 +1537,12 @@ optoas(int op, Type *t)
case CASE(OADD, TINT32):
case CASE(OADD, TUINT32):
case CASE(OADD, TPTR32):
- case CASE(OADDPTR, TPTR32):
a = AADDL;
break;
case CASE(OADD, TINT64):
case CASE(OADD, TUINT64):
case CASE(OADD, TPTR64):
- case CASE(OADDPTR, TPTR64):
a = AADDQ;
break;
diff --git a/src/cmd/6g/peep.c b/src/cmd/6g/peep.c
index 0f27204434..24617836fe 100644
--- a/src/cmd/6g/peep.c
+++ b/src/cmd/6g/peep.c
@@ -838,6 +838,11 @@ copyu(Prog *p, Adr *v, Adr *s)
static int
copyas(Adr *a, Adr *v)
{
+ if(D_AL <= a->type && a->type <= D_R15B)
+ fatal("use of byte register");
+ if(D_AL <= v->type && v->type <= D_R15B)
+ fatal("use of byte register");
+
if(a->type != v->type)
return 0;
if(regtyp(v))
diff --git a/src/cmd/6g/reg.c b/src/cmd/6g/reg.c
index 02945fb401..1f757e1972 100644
--- a/src/cmd/6g/reg.c
+++ b/src/cmd/6g/reg.c
@@ -844,7 +844,7 @@ prop(Reg *r, Bits ref, Bits cal)
if(v == v1 || ((cal.b[j/32]>>(j&31))&1) == 0) {
for(; v1 != nil; v1 = v1->nextinnode) {
j = v1 - var;
- cal.b[j/32] |= 1<<(j&31);
+ cal.b[j/32] |= 1UL<<(j&31);
}
}
}
@@ -1191,6 +1191,7 @@ void
addreg(Adr *a, int rn)
{
a->sym = nil;
+ a->node = nil;
a->offset = 0;
a->type = rn;
diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c
index e251e32ca9..18b5aa3119 100644
--- a/src/cmd/6l/asm.c
+++ b/src/cmd/6l/asm.c
@@ -290,7 +290,6 @@ elfreloc1(Reloc *r, vlong sectoff)
break;
case R_CALL:
- case R_PCREL:
if(r->siz == 4) {
if(r->xsym->type == SDYNIMPORT)
VPUT(R_X86_64_GOTPCREL | (uint64)elfsym<<32);
@@ -299,7 +298,14 @@ elfreloc1(Reloc *r, vlong sectoff)
} else
return -1;
break;
-
+
+ case R_PCREL:
+ if(r->siz == 4) {
+ VPUT(R_X86_64_PC32 | (uint64)elfsym<<32);
+ } else
+ return -1;
+ break;
+
case R_TLS:
if(r->siz == 4) {
if(flag_shared)
@@ -323,7 +329,7 @@ machoreloc1(Reloc *r, vlong sectoff)
rs = r->xsym;
- if(rs->type == SHOSTOBJ) {
+ if(rs->type == SHOSTOBJ || r->type == R_PCREL) {
if(rs->dynid < 0) {
diag("reloc %d to non-macho symbol %s type=%d", r->type, rs->name, rs->type);
return -1;
@@ -345,10 +351,13 @@ machoreloc1(Reloc *r, vlong sectoff)
v |= MACHO_X86_64_RELOC_UNSIGNED<<28;
break;
case R_CALL:
- case R_PCREL:
v |= 1<<24; // pc-relative bit
v |= MACHO_X86_64_RELOC_BRANCH<<28;
break;
+ case R_PCREL:
+ // NOTE: Only works with 'external' relocation. Forced above.
+ v |= 1<<24; // pc-relative bit
+ v |= MACHO_X86_64_RELOC_SIGNED<<28;
}
switch(r->siz) {
@@ -689,10 +698,10 @@ asmb(void)
case Hplan9:
case Helf:
debug['s'] = 1;
- symo = HEADR+segtext.len+segdata.filelen;
+ symo = segdata.fileoff+segdata.filelen;
break;
case Hdarwin:
- symo = rnd(HEADR+segtext.len, INITRND)+rnd(segdata.filelen, INITRND)+machlink;
+ symo = segdata.fileoff+rnd(segdata.filelen, INITRND)+machlink;
break;
case Hlinux:
case Hfreebsd:
@@ -701,11 +710,11 @@ asmb(void)
case Hdragonfly:
case Hsolaris:
case Hnacl:
- symo = rnd(HEADR+segtext.len, INITRND)+rnd(segrodata.len, INITRND)+segdata.filelen;
+ symo = segdata.fileoff+segdata.filelen;
symo = rnd(symo, INITRND);
break;
case Hwindows:
- symo = rnd(HEADR+segtext.filelen, PEFILEALIGN)+segdata.filelen;
+ symo = segdata.fileoff+segdata.filelen;
symo = rnd(symo, PEFILEALIGN);
break;
}
diff --git a/src/cmd/8a/lex.c b/src/cmd/8a/lex.c
index 32c099b75a..807e48cb50 100644
--- a/src/cmd/8a/lex.c
+++ b/src/cmd/8a/lex.c
@@ -352,49 +352,49 @@ struct
"IRETL", LTYPE0, AIRETL,
"IRETW", LTYPE0, AIRETW,
- "JOS", LTYPER, AJOS,
+ "JOS", LTYPER, AJOS, /* overflow set (OF = 1) */
"JO", LTYPER, AJOS, /* alternate */
- "JOC", LTYPER, AJOC,
+ "JOC", LTYPER, AJOC, /* overflow clear (OF = 0) */
"JNO", LTYPER, AJOC, /* alternate */
- "JCS", LTYPER, AJCS,
+ "JCS", LTYPER, AJCS, /* carry set (CF = 1) */
"JB", LTYPER, AJCS, /* alternate */
"JC", LTYPER, AJCS, /* alternate */
"JNAE", LTYPER, AJCS, /* alternate */
"JLO", LTYPER, AJCS, /* alternate */
- "JCC", LTYPER, AJCC,
+ "JCC", LTYPER, AJCC, /* carry clear (CF = 0) */
"JAE", LTYPER, AJCC, /* alternate */
"JNB", LTYPER, AJCC, /* alternate */
"JNC", LTYPER, AJCC, /* alternate */
"JHS", LTYPER, AJCC, /* alternate */
- "JEQ", LTYPER, AJEQ,
+ "JEQ", LTYPER, AJEQ, /* equal (ZF = 1) */
"JE", LTYPER, AJEQ, /* alternate */
"JZ", LTYPER, AJEQ, /* alternate */
- "JNE", LTYPER, AJNE,
+ "JNE", LTYPER, AJNE, /* not equal (ZF = 0) */
"JNZ", LTYPER, AJNE, /* alternate */
- "JLS", LTYPER, AJLS,
+ "JLS", LTYPER, AJLS, /* lower or same (unsigned) (CF = 1 || ZF = 1) */
"JBE", LTYPER, AJLS, /* alternate */
"JNA", LTYPER, AJLS, /* alternate */
- "JHI", LTYPER, AJHI,
+ "JHI", LTYPER, AJHI, /* higher (unsigned) (CF = 0 && ZF = 0) */
"JA", LTYPER, AJHI, /* alternate */
"JNBE", LTYPER, AJHI, /* alternate */
- "JMI", LTYPER, AJMI,
+ "JMI", LTYPER, AJMI, /* negative (minus) (SF = 1) */
"JS", LTYPER, AJMI, /* alternate */
- "JPL", LTYPER, AJPL,
+ "JPL", LTYPER, AJPL, /* non-negative (plus) (SF = 0) */
"JNS", LTYPER, AJPL, /* alternate */
- "JPS", LTYPER, AJPS,
+ "JPS", LTYPER, AJPS, /* parity set (PF = 1) */
"JP", LTYPER, AJPS, /* alternate */
"JPE", LTYPER, AJPS, /* alternate */
- "JPC", LTYPER, AJPC,
+ "JPC", LTYPER, AJPC, /* parity clear (PF = 0) */
"JNP", LTYPER, AJPC, /* alternate */
"JPO", LTYPER, AJPC, /* alternate */
- "JLT", LTYPER, AJLT,
+ "JLT", LTYPER, AJLT, /* less than (signed) (SF != OF) */
"JL", LTYPER, AJLT, /* alternate */
"JNGE", LTYPER, AJLT, /* alternate */
- "JGE", LTYPER, AJGE,
+ "JGE", LTYPER, AJGE, /* greater than or equal (signed) (SF = OF) */
"JNL", LTYPER, AJGE, /* alternate */
- "JLE", LTYPER, AJLE,
+ "JLE", LTYPER, AJLE, /* less than or equal (signed) (ZF = 1 || SF != OF) */
"JNG", LTYPER, AJLE, /* alternate */
- "JGT", LTYPER, AJGT,
+ "JGT", LTYPER, AJGT, /* greater than (signed) (ZF = 0 && SF = OF) */
"JG", LTYPER, AJGT, /* alternate */
"JNLE", LTYPER, AJGT, /* alternate */
@@ -493,7 +493,7 @@ struct
"SCASB", LTYPE0, ASCASB,
"SCASL", LTYPE0, ASCASL,
"SCASW", LTYPE0, ASCASW,
- "SETCC", LTYPE1, ASETCC,
+ "SETCC", LTYPE1, ASETCC, /* see JCC etc above for condition codes */
"SETCS", LTYPE1, ASETCS,
"SETEQ", LTYPE1, ASETEQ,
"SETGE", LTYPE1, ASETGE,
diff --git a/src/cmd/8c/cgen.c b/src/cmd/8c/cgen.c
index f541022456..8ac8e36009 100644
--- a/src/cmd/8c/cgen.c
+++ b/src/cmd/8c/cgen.c
@@ -49,7 +49,7 @@ cgen(Node *n, Node *nn)
}
if(n == Z || n->type == T)
return;
- if(typesuv[n->type->etype]) {
+ if(typesuv[n->type->etype] && (n->op != OFUNC || nn != Z)) {
sugen(n, nn, n->type->width);
return;
}
@@ -86,7 +86,7 @@ cgen(Node *n, Node *nn)
if(cond(o) && typesuv[l->type->etype])
break;
- regret(&nod, r);
+ regret(&nod, r, 0, 0);
cgen(r, &nod);
regsalloc(&nod1, r);
@@ -147,7 +147,7 @@ cgen(Node *n, Node *nn)
if(!hardleft) {
if(nn != Z || r->addable < INDEXED) {
if(r->complex >= FNX && nn == Z)
- regret(&nod, r);
+ regret(&nod, r, 0, 0);
else
regalloc(&nod, r, nn);
cgen(r, &nod);
@@ -922,7 +922,7 @@ cgen(Node *n, Node *nn)
if(l->op != OIND)
diag(n, "bad function call");
- regret(&nod, l->left);
+ regret(&nod, l->left, 0, 0);
cgen(l->left, &nod);
regsalloc(&nod1, l->left);
gmove(&nod, &nod1);
@@ -949,12 +949,12 @@ cgen(Node *n, Node *nn)
gpcdata(PCDATA_ArgSize, -1);
if(REGARG >= 0 && reg[REGARG])
reg[REGARG]--;
- if(nn != Z) {
- regret(&nod, n);
+ regret(&nod, n, l->type, 1); // update maxarg if nothing else
+ if(nn != Z)
gmove(&nod, nn);
+ if(nod.op == OREGISTER)
regfree(&nod);
- } else
- if(typefd[n->type->etype])
+ if(nn == Z && hasdotdotdot(l->type) && typefd[n->type->etype])
gins(AFMOVDP, &fregnode0, &fregnode0);
break;
@@ -1374,7 +1374,7 @@ boolgen(Node *n, int true, Node *nn)
if(true)
o = comrel[relindex(o)];
if(l->complex >= FNX && r->complex >= FNX) {
- regret(&nod, r);
+ regret(&nod, r, 0, 0);
cgen(r, &nod);
regsalloc(&nod1, r);
gmove(&nod, &nod1);
@@ -1567,7 +1567,7 @@ sugen(Node *n, Node *nn, int32 w)
if(nn != Z && side(nn)) {
nod1 = *n;
nod1.type = typ(TIND, n->type);
- regret(&nod2, &nod1);
+ regret(&nod2, &nod1, 0, 0);
lcgen(nn, &nod2);
regsalloc(&nod0, &nod1);
cgen(&nod2, &nod0);
@@ -1649,6 +1649,20 @@ sugen(Node *n, Node *nn, int32 w)
break;
case OFUNC:
+ if(!hasdotdotdot(n->left->type)) {
+ cgen(n, Z);
+ if(nn != Z) {
+ curarg -= n->type->width;
+ regret(&nod1, n, n->left->type, 1);
+ if(nn->complex >= FNX) {
+ regsalloc(&nod2, n);
+ cgen(&nod1, &nod2);
+ nod1 = nod2;
+ }
+ cgen(&nod1, nn);
+ }
+ break;
+ }
if(nn == Z) {
sugen(n, nodrat, w);
break;
diff --git a/src/cmd/8c/gc.h b/src/cmd/8c/gc.h
index 9c4613f562..aa3888d733 100644
--- a/src/cmd/8c/gc.h
+++ b/src/cmd/8c/gc.h
@@ -210,7 +210,7 @@ void xcom(Node*);
void indx(Node*);
int bcomplex(Node*, Node*);
Prog* gtext(Sym*, int32);
-vlong argsize(void);
+vlong argsize(int);
/*
* cgen.c
@@ -244,7 +244,7 @@ Node* nodconst(int32);
Node* nodfconst(double);
int nodreg(Node*, Node*, int);
int isreg(Node*, int);
-void regret(Node*, Node*);
+void regret(Node*, Node*, Type*, int);
void regalloc(Node*, Node*, Node*);
void regfree(Node*);
void regialloc(Node*, Node*, Node*);
diff --git a/src/cmd/8c/sgen.c b/src/cmd/8c/sgen.c
index 069bbc1fc3..d647010ef7 100644
--- a/src/cmd/8c/sgen.c
+++ b/src/cmd/8c/sgen.c
@@ -35,7 +35,7 @@ gtext(Sym *s, int32 stkoff)
{
int32 a;
- a = argsize();
+ a = argsize(1);
if((textflag & NOSPLIT) != 0 && stkoff >= 128)
yyerror("stack frame too large for NOSPLIT function");
diff --git a/src/cmd/8c/swt.c b/src/cmd/8c/swt.c
index ae36f84eac..d960519e3b 100644
--- a/src/cmd/8c/swt.c
+++ b/src/cmd/8c/swt.c
@@ -255,10 +255,11 @@ align(int32 i, Type *t, int op, int32 *maxalign)
{
int32 o;
Type *v;
- int w;
+ int w, packw;
o = i;
w = 1;
+ packw = 0;
switch(op) {
default:
diag(Z, "unknown align opcode %d", op);
@@ -269,7 +270,7 @@ align(int32 i, Type *t, int op, int32 *maxalign)
if(w < 1)
w = 1;
if(packflg)
- w = packflg;
+ packw = packflg;
break;
case Ael1: /* initial align of struct element */
@@ -285,7 +286,7 @@ align(int32 i, Type *t, int op, int32 *maxalign)
if(w < 1 || w > SZ_LONG)
fatal(Z, "align");
if(packflg)
- w = packflg;
+ packw = packflg;
break;
case Ael2: /* width of a struct element */
@@ -320,6 +321,8 @@ align(int32 i, Type *t, int op, int32 *maxalign)
o = align(o, t, Ael2, nil);
break;
}
+ if(packw != 0 && xround(o, w) != xround(o, packw))
+ diag(Z, "#pragma pack changes offset of %T", t);
o = xround(o, w);
if(maxalign && *maxalign < w)
*maxalign = w;
diff --git a/src/cmd/8c/txt.c b/src/cmd/8c/txt.c
index 25082de05d..7f87a0a0d0 100644
--- a/src/cmd/8c/txt.c
+++ b/src/cmd/8c/txt.c
@@ -311,15 +311,43 @@ nodreg(Node *n, Node *nn, int r)
}
void
-regret(Node *n, Node *nn)
+regret(Node *n, Node *nn, Type *t, int mode)
{
int r;
- r = REGRET;
- if(typefd[nn->type->etype])
- r = FREGRET;
- nodreg(n, nn, r);
- reg[r]++;
+ if(mode == 0 || hasdotdotdot(t) || nn->type->width == 0) {
+ r = REGRET;
+ if(typefd[nn->type->etype])
+ r = FREGRET;
+ nodreg(n, nn, r);
+ reg[r]++;
+ return;
+ }
+
+ if(mode == 1) {
+ // fetch returned value after call.
+ // already called gargs, so curarg is set.
+ curarg = (curarg+3) & ~3;
+ regaalloc(n, nn);
+ return;
+ }
+
+ if(mode == 2) {
+ // store value to be returned.
+ // must compute arg offset.
+ if(t->etype != TFUNC)
+ fatal(Z, "bad regret func %T", t);
+ *n = *nn;
+ n->op = ONAME;
+ n->class = CPARAM;
+ n->sym = slookup(".retx");
+ n->complex = 0;
+ n->addable = 20;
+ n->xoffset = argsize(0);
+ return;
+ }
+
+ fatal(Z, "bad regret");
}
void
diff --git a/src/cmd/8g/cgen.c b/src/cmd/8g/cgen.c
index 5988a4328c..f3093bc26e 100644
--- a/src/cmd/8g/cgen.c
+++ b/src/cmd/8g/cgen.c
@@ -242,7 +242,6 @@ cgen(Node *n, Node *res)
case OOR:
case OXOR:
case OADD:
- case OADDPTR:
case OMUL:
a = optoas(n->op, nl->type);
if(a == AIMULB) {
@@ -1367,7 +1366,10 @@ int
componentgen(Node *nr, Node *nl)
{
Node nodl, nodr;
+ Type *t;
int freel, freer;
+ vlong fldcount;
+ vlong loffset, roffset;
freel = 0;
freer = 0;
@@ -1377,8 +1379,33 @@ componentgen(Node *nr, Node *nl)
goto no;
case TARRAY:
- if(!isslice(nl->type))
+ t = nl->type;
+
+ // Slices are ok.
+ if(isslice(t))
+ break;
+ // Small arrays are ok.
+ if(t->bound > 0 && t->bound <= 3 && !isfat(t->type))
+ break;
+
+ goto no;
+
+ case TSTRUCT:
+ // Small structs with non-fat types are ok.
+ // Zero-sized structs are treated separately elsewhere.
+ fldcount = 0;
+ for(t=nl->type->type; t; t=t->down) {
+ if(isfat(t->type))
+ goto no;
+ if(t->etype != TFIELD)
+ fatal("componentgen: not a TFIELD: %lT", t);
+ fldcount++;
+ }
+ if(fldcount == 0 || fldcount > 4)
goto no;
+
+ break;
+
case TSTRING:
case TINTER:
break;
@@ -1399,7 +1426,7 @@ componentgen(Node *nr, Node *nl)
freer = 1;
}
}
-
+
// nl and nr are 'cadable' which basically means they are names (variables) now.
// If they are the same variable, don't generate any code, because the
// VARDEF we generate will mark the old value as dead incorrectly.
@@ -1409,8 +1436,25 @@ componentgen(Node *nr, Node *nl)
switch(nl->type->etype) {
case TARRAY:
+ // componentgen for arrays.
if(nl->op == ONAME)
gvardef(nl);
+ t = nl->type;
+ if(!isslice(t)) {
+ nodl.type = t->type;
+ nodr.type = nodl.type;
+ for(fldcount=0; fldcount < t->bound; fldcount++) {
+ if(nr == N)
+ clearslim(&nodl);
+ else
+ gmove(&nodr, &nodl);
+ nodl.xoffset += t->type->width;
+ nodr.xoffset += t->type->width;
+ }
+ goto yes;
+ }
+
+ // componentgen for slices.
nodl.xoffset += Array_array;
nodl.type = ptrto(nl->type->type);
@@ -1422,7 +1466,7 @@ componentgen(Node *nr, Node *nl)
gmove(&nodr, &nodl);
nodl.xoffset += Array_nel-Array_array;
- nodl.type = types[TUINT32];
+ nodl.type = types[simtype[TUINT]];
if(nr != N) {
nodr.xoffset += Array_nel-Array_array;
@@ -1432,7 +1476,7 @@ componentgen(Node *nr, Node *nl)
gmove(&nodr, &nodl);
nodl.xoffset += Array_cap-Array_nel;
- nodl.type = types[TUINT32];
+ nodl.type = types[simtype[TUINT]];
if(nr != N) {
nodr.xoffset += Array_cap-Array_nel;
@@ -1457,7 +1501,7 @@ componentgen(Node *nr, Node *nl)
gmove(&nodr, &nodl);
nodl.xoffset += Array_nel-Array_array;
- nodl.type = types[TUINT32];
+ nodl.type = types[simtype[TUINT]];
if(nr != N) {
nodr.xoffset += Array_nel-Array_array;
@@ -1492,6 +1536,31 @@ componentgen(Node *nr, Node *nl)
gmove(&nodr, &nodl);
goto yes;
+
+ case TSTRUCT:
+ if(nl->op == ONAME)
+ gvardef(nl);
+ loffset = nodl.xoffset;
+ roffset = nodr.xoffset;
+ // funarg structs may not begin at offset zero.
+ if(nl->type->etype == TSTRUCT && nl->type->funarg && nl->type->type)
+ loffset -= nl->type->type->width;
+ if(nr != N && nr->type->etype == TSTRUCT && nr->type->funarg && nr->type->type)
+ roffset -= nr->type->type->width;
+
+ for(t=nl->type->type; t; t=t->down) {
+ nodl.xoffset = loffset + t->width;
+ nodl.type = t->type;
+
+ if(nr == N)
+ clearslim(&nodl);
+ else {
+ nodr.xoffset = roffset + t->width;
+ nodr.type = nodl.type;
+ gmove(&nodr, &nodl);
+ }
+ }
+ goto yes;
}
no:
diff --git a/src/cmd/8g/ggen.c b/src/cmd/8g/ggen.c
index 2285a04e61..5e31404806 100644
--- a/src/cmd/8g/ggen.c
+++ b/src/cmd/8g/ggen.c
@@ -991,7 +991,7 @@ cgen_hmul(Node *nl, Node *nr, Node *res)
if(t->width == 1) {
// byte multiply behaves differently.
nodreg(&ax, t, D_AH);
- nodreg(&dx, t, D_DL);
+ nodreg(&dx, t, D_DX);
gmove(&ax, &dx);
}
nodreg(&dx, t, D_DX);
diff --git a/src/cmd/8g/gsubr.c b/src/cmd/8g/gsubr.c
index 66d5b8d696..9ee418cb78 100644
--- a/src/cmd/8g/gsubr.c
+++ b/src/cmd/8g/gsubr.c
@@ -430,7 +430,6 @@ optoas(int op, Type *t)
case CASE(OADD, TINT32):
case CASE(OADD, TUINT32):
case CASE(OADD, TPTR32):
- case CASE(OADDPTR, TPTR32):
a = AADDL;
break;
diff --git a/src/cmd/8g/peep.c b/src/cmd/8g/peep.c
index d88987f954..91a91d20db 100644
--- a/src/cmd/8g/peep.c
+++ b/src/cmd/8g/peep.c
@@ -636,6 +636,11 @@ copyu(Prog *p, Adr *v, Adr *s)
static int
copyas(Adr *a, Adr *v)
{
+ if(D_AL <= a->type && a->type <= D_BL)
+ fatal("use of byte register");
+ if(D_AL <= v->type && v->type <= D_BL)
+ fatal("use of byte register");
+
if(a->type != v->type)
return 0;
if(regtyp(v))
diff --git a/src/cmd/8g/reg.c b/src/cmd/8g/reg.c
index a69d124db8..302b273a1b 100644
--- a/src/cmd/8g/reg.c
+++ b/src/cmd/8g/reg.c
@@ -1168,6 +1168,7 @@ void
addreg(Adr *a, int rn)
{
a->sym = nil;
+ a->node = nil;
a->offset = 0;
a->type = rn;
diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c
index 114a3eb5d7..c135dce709 100644
--- a/src/cmd/8l/asm.c
+++ b/src/cmd/8l/asm.c
@@ -619,17 +619,17 @@ asmb(void)
if(iself)
goto Elfsym;
case Hplan9:
- symo = HEADR+segtext.filelen+segdata.filelen;
+ symo = segdata.fileoff+segdata.filelen;
break;
case Hdarwin:
- symo = rnd(HEADR+segtext.filelen, INITRND)+rnd(segdata.filelen, INITRND)+machlink;
+ symo = segdata.fileoff+rnd(segdata.filelen, INITRND)+machlink;
break;
Elfsym:
- symo = rnd(HEADR+segtext.filelen, INITRND)+rnd(HEADR+segrodata.filelen, INITRND)+segdata.filelen;
+ symo = segdata.fileoff+segdata.filelen;
symo = rnd(symo, INITRND);
break;
case Hwindows:
- symo = rnd(HEADR+segtext.filelen, PEFILEALIGN)+segdata.filelen;
+ symo = segdata.fileoff+segdata.filelen;
symo = rnd(symo, PEFILEALIGN);
break;
}
diff --git a/src/cmd/9c/cgen.c b/src/cmd/9c/cgen.c
index 0d7a402945..aeedc60c06 100644
--- a/src/cmd/9c/cgen.c
+++ b/src/cmd/9c/cgen.c
@@ -45,7 +45,7 @@ cgen(Node *n, Node *nn)
}
if(n == Z || n->type == T)
return;
- if(typesu[n->type->etype]) {
+ if(typesu[n->type->etype] && (n->op != OFUNC || nn != Z)) {
sugen(n, nn, n->type->width);
return;
}
@@ -74,7 +74,7 @@ cgen(Node *n, Node *nn)
if(r != Z && r->complex >= FNX)
switch(o) {
default:
- regret(&nod, r);
+ regret(&nod, r, 0, 0);
cgen(r, &nod);
regsalloc(&nod1, r);
@@ -324,7 +324,7 @@ cgen(Node *n, Node *nn)
if(l->op != OIND)
diag(n, "bad function call");
- regret(&nod, l->left);
+ regret(&nod, l->left, 0, 0);
cgen(l->left, &nod);
regsalloc(&nod1, l->left);
gopcode(OAS, &nod, Z, &nod1);
@@ -353,11 +353,13 @@ cgen(Node *n, Node *nn)
if(REGARG>=0)
if(o != reg[REGARG])
reg[REGARG]--;
- if(nn != Z) {
- regret(&nod, n);
+ regret(&nod, n, l->type, 1); // update maxarg if nothing else
+ gpcdata(PCDATA_ArgSize, curarg);
+ gpcdata(PCDATA_ArgSize, -1);
+ if(nn != Z)
gopcode(OAS, &nod, Z, nn);
+ if(nod.op == OREGISTER)
regfree(&nod);
- }
break;
case OIND:
@@ -759,7 +761,7 @@ boolgen(Node *n, int true, Node *nn)
if(true)
o = comrel[relindex(o)];
if(l->complex >= FNX && r->complex >= FNX) {
- regret(&nod, r);
+ regret(&nod, r, 0, 0);
cgen(r, &nod);
regsalloc(&nod1, r);
gopcode(OAS, &nod, Z, &nod1);
@@ -966,6 +968,20 @@ sugen(Node *n, Node *nn, int32 w)
break;
case OFUNC:
+ if(!hasdotdotdot(n->left->type)) {
+ cgen(n, Z);
+ if(nn != Z) {
+ curarg -= n->type->width;
+ regret(&nod1, n, n->left->type, 1);
+ if(nn->complex >= FNX) {
+ regsalloc(&nod2, n);
+ cgen(&nod1, &nod2);
+ nod1 = nod2;
+ }
+ cgen(&nod1, nn);
+ }
+ break;
+ }
if(nn == Z) {
sugen(n, nodrat, w);
break;
diff --git a/src/cmd/9c/gc.h b/src/cmd/9c/gc.h
index 82204902b7..fbe5099fe8 100644
--- a/src/cmd/9c/gc.h
+++ b/src/cmd/9c/gc.h
@@ -141,7 +141,6 @@ extern int hintabsize;
EXTERN int32 maxargsafe;
EXTERN Multab multab[20];
EXTERN int mnstring;
-EXTERN int retok;
EXTERN Node* nodrat;
EXTERN Node* nodret;
EXTERN Node* nodsafe;
@@ -212,7 +211,7 @@ void noretval(int);
void xcom(Node*);
int bcomplex(Node*, Node*);
Prog* gtext(Sym*, int32);
-vlong argsize(void);
+vlong argsize(int);
/*
* cgen.c
@@ -238,7 +237,7 @@ Node* nod32const(vlong);
Node* nodfconst(double);
Node* nodgconst(vlong v, Type *t);
void nodreg(Node*, Node*, int);
-void regret(Node*, Node*);
+void regret(Node*, Node*, Type*, int);
void regalloc(Node*, Node*, Node*);
void regfree(Node*);
void regialloc(Node*, Node*, Node*);
diff --git a/src/cmd/9c/sgen.c b/src/cmd/9c/sgen.c
index 25f0438e58..b03c17267d 100644
--- a/src/cmd/9c/sgen.c
+++ b/src/cmd/9c/sgen.c
@@ -34,7 +34,7 @@ gtext(Sym *s, int32 stkoff)
{
vlong v;
- v = ((uvlong)argsize() << 32) | (stkoff & 0xffffffff);
+ v = ((uvlong)argsize(1) << 32) | (stkoff & 0xffffffff);
if((textflag & NOSPLIT) && stkoff >= 128)
yyerror("stack frame too large for NOSPLIT function");
diff --git a/src/cmd/9c/swt.c b/src/cmd/9c/swt.c
index 83058e7ab9..a63db60b27 100644
--- a/src/cmd/9c/swt.c
+++ b/src/cmd/9c/swt.c
@@ -324,10 +324,11 @@ align(int32 i, Type *t, int op, int32 *maxalign)
{
int32 o;
Type *v;
- int w;
+ int w, packw;
o = i;
w = 1;
+ packw = 0;
switch(op) {
default:
diag(Z, "unknown align opcode %d", op);
@@ -338,7 +339,7 @@ align(int32 i, Type *t, int op, int32 *maxalign)
if(w < 1)
w = 1;
if(packflg)
- w = packflg;
+ packw = packflg;
break;
case Ael1: /* initial allign of struct element */
@@ -351,7 +352,7 @@ align(int32 i, Type *t, int op, int32 *maxalign)
if(w < 1 || w > SZ_VLONG)
fatal(Z, "align");
if(packflg)
- w = packflg;
+ packw = packflg;
break;
case Ael2: /* width of a struct element */
@@ -386,6 +387,8 @@ align(int32 i, Type *t, int op, int32 *maxalign)
o = align(o, t, Ael2, nil);
break;
}
+ if(packw != 0 && xround(o, w) != xround(o, packw))
+ diag(Z, "#pragma pack changes offset of %T", t);
o = xround(o, w);
if(maxalign && *maxalign < w)
*maxalign = w;
diff --git a/src/cmd/9c/txt.c b/src/cmd/9c/txt.c
index 84aeb4d8a9..e46aba84e7 100644
--- a/src/cmd/9c/txt.c
+++ b/src/cmd/9c/txt.c
@@ -319,15 +319,43 @@ nodreg(Node *n, Node *nn, int reg)
}
void
-regret(Node *n, Node *nn)
+regret(Node *n, Node *nn, Type *t, int mode)
{
int r;
+
+ if(mode == 0 || hasdotdotdot(t) || nn->type->width == 0) {
+ r = REGRET;
+ if(typefd[nn->type->etype])
+ r = FREGRET+NREG;
+ nodreg(n, nn, r);
+ reg[r]++;
+ return;
+ }
+
+ if(mode == 1) {
+ // fetch returned value after call.
+ // already called gargs, so curarg is set.
+ curarg = (curarg+7) & ~7;
+ regaalloc(n, nn);
+ return;
+ }
- r = REGRET;
- if(typefd[nn->type->etype])
- r = FREGRET+NREG;
- nodreg(n, nn, r);
- reg[r]++;
+ if(mode == 2) {
+ // store value to be returned.
+ // must compute arg offset.
+ if(t->etype != TFUNC)
+ fatal(Z, "bad regret func %T", t);
+ *n = *nn;
+ n->op = ONAME;
+ n->class = CPARAM;
+ n->sym = slookup(".ret");
+ n->complex = nodret->complex;
+ n->addable = 20;
+ n->xoffset = argsize(0);
+ return;
+ }
+
+ fatal(Z, "bad regret");
}
void
diff --git a/src/cmd/9g/cgen.c b/src/cmd/9g/cgen.c
index 86a56975ad..e389360016 100644
--- a/src/cmd/9g/cgen.c
+++ b/src/cmd/9g/cgen.c
@@ -260,7 +260,6 @@ cgen(Node *n, Node *res)
case OOR:
case OXOR:
case OADD:
- case OADDPTR:
case OMUL:
a = optoas(n->op, nl->type);
goto sbop;
diff --git a/src/cmd/9g/gg.h b/src/cmd/9g/gg.h
index 6aa00117f8..2eb516b403 100644
--- a/src/cmd/9g/gg.h
+++ b/src/cmd/9g/gg.h
@@ -49,7 +49,6 @@ void allocparams(void);
void checklabels(void);
void ginscall(Node*, int);
int gen_as_init(Node*);
-void clearslim(Node*);
/*
* cgen.c
diff --git a/src/cmd/9g/gsubr.c b/src/cmd/9g/gsubr.c
index dfdff05879..b194cfd9ee 100644
--- a/src/cmd/9g/gsubr.c
+++ b/src/cmd/9g/gsubr.c
@@ -614,11 +614,8 @@ ismem(Node *n)
case ONAME:
case OPARAM:
case OCLOSUREVAR:
- return 1;
case OADDR:
- //if(flag_largemodel)
- return 1;
- break;
+ return 1;
}
return 0;
}
@@ -1474,11 +1471,9 @@ optoas(int op, Type *t)
case CASE(OADD, TINT32):
case CASE(OADD, TUINT32):
case CASE(OADD, TPTR32):
- case CASE(OADDPTR, TPTR32):
case CASE(OADD, TINT64):
case CASE(OADD, TUINT64):
case CASE(OADD, TPTR64):
- case CASE(OADDPTR, TPTR64):
a = AADD;
break;
diff --git a/src/cmd/9l/asm.c b/src/cmd/9l/asm.c
index 5aeea1b6b0..b8ca777c35 100644
--- a/src/cmd/9l/asm.c
+++ b/src/cmd/9l/asm.c
@@ -245,10 +245,10 @@ asmb(void)
if(iself)
goto ElfSym;
case Hplan9:
- symo = HEADR+segtext.len+segdata.filelen;
+ symo = segdata.fileoff+segdata.filelen;
break;
ElfSym:
- symo = rnd(HEADR+segtext.filelen, INITRND)+rnd(HEADR+segrodata.filelen, INITRND)+segdata.filelen;
+ symo = segdata.fileoff+segdata.filelen;
symo = rnd(symo, INITRND);
break;
}
diff --git a/src/cmd/addr2line/main.go b/src/cmd/addr2line/main.go
index 3802f764f9..267f4170a8 100644
--- a/src/cmd/addr2line/main.go
+++ b/src/cmd/addr2line/main.go
@@ -19,17 +19,14 @@ package main
import (
"bufio"
- "debug/elf"
- "debug/gosym"
- "debug/macho"
- "debug/pe"
- "debug/plan9obj"
"flag"
"fmt"
"log"
"os"
"strconv"
"strings"
+
+ "cmd/internal/objfile"
)
func printUsage(w *os.File) {
@@ -60,18 +57,12 @@ func main() {
usage()
}
- f, err := os.Open(flag.Arg(0))
+ f, err := objfile.Open(flag.Arg(0))
if err != nil {
log.Fatal(err)
}
- textStart, symtab, pclntab, err := loadTables(f)
- if err != nil {
- log.Fatalf("reading %s: %v", flag.Arg(0), err)
- }
-
- pcln := gosym.NewLineTable(pclntab, textStart)
- tab, err := gosym.NewTable(symtab, pcln)
+ tab, err := f.PCLineTable()
if err != nil {
log.Fatalf("reading %s: %v", flag.Arg(0), err)
}
@@ -102,145 +93,3 @@ func main() {
}
stdout.Flush()
}
-
-func loadTables(f *os.File) (textStart uint64, symtab, pclntab []byte, err error) {
- if obj, err := elf.NewFile(f); err == nil {
- if sect := obj.Section(".text"); sect != nil {
- textStart = sect.Addr
- }
- if sect := obj.Section(".gosymtab"); sect != nil {
- if symtab, err = sect.Data(); err != nil {
- return 0, nil, nil, err
- }
- }
- if sect := obj.Section(".gopclntab"); sect != nil {
- if pclntab, err = sect.Data(); err != nil {
- return 0, nil, nil, err
- }
- }
- return textStart, symtab, pclntab, nil
- }
-
- if obj, err := macho.NewFile(f); err == nil {
- if sect := obj.Section("__text"); sect != nil {
- textStart = sect.Addr
- }
- if sect := obj.Section("__gosymtab"); sect != nil {
- if symtab, err = sect.Data(); err != nil {
- return 0, nil, nil, err
- }
- }
- if sect := obj.Section("__gopclntab"); sect != nil {
- if pclntab, err = sect.Data(); err != nil {
- return 0, nil, nil, err
- }
- }
- return textStart, symtab, pclntab, nil
- }
-
- if obj, err := pe.NewFile(f); err == nil {
- var imageBase uint64
- switch oh := obj.OptionalHeader.(type) {
- case *pe.OptionalHeader32:
- imageBase = uint64(oh.ImageBase)
- case *pe.OptionalHeader64:
- imageBase = oh.ImageBase
- default:
- return 0, nil, nil, fmt.Errorf("pe file format not recognized")
- }
- if sect := obj.Section(".text"); sect != nil {
- textStart = imageBase + uint64(sect.VirtualAddress)
- }
- if pclntab, err = loadPETable(obj, "pclntab", "epclntab"); err != nil {
- return 0, nil, nil, err
- }
- if symtab, err = loadPETable(obj, "symtab", "esymtab"); err != nil {
- return 0, nil, nil, err
- }
- return textStart, symtab, pclntab, nil
- }
-
- if obj, err := plan9obj.NewFile(f); err == nil {
- textStart = obj.LoadAddress + obj.HdrSize
- if pclntab, err = loadPlan9Table(obj, "pclntab", "epclntab"); err != nil {
- return 0, nil, nil, err
- }
- if symtab, err = loadPlan9Table(obj, "symtab", "esymtab"); err != nil {
- return 0, nil, nil, err
- }
- return textStart, symtab, pclntab, nil
- }
-
- return 0, nil, nil, fmt.Errorf("unrecognized binary format")
-}
-
-func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
- for _, s := range f.Symbols {
- if s.Name != name {
- continue
- }
- if s.SectionNumber <= 0 {
- return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber)
- }
- if len(f.Sections) < int(s.SectionNumber) {
- return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections))
- }
- return s, nil
- }
- return nil, fmt.Errorf("no %s symbol found", name)
-}
-
-func loadPETable(f *pe.File, sname, ename string) ([]byte, error) {
- ssym, err := findPESymbol(f, sname)
- if err != nil {
- return nil, err
- }
- esym, err := findPESymbol(f, ename)
- if err != nil {
- return nil, err
- }
- if ssym.SectionNumber != esym.SectionNumber {
- return nil, fmt.Errorf("%s and %s symbols must be in the same section", sname, ename)
- }
- sect := f.Sections[ssym.SectionNumber-1]
- data, err := sect.Data()
- if err != nil {
- return nil, err
- }
- return data[ssym.Value:esym.Value], nil
-}
-
-func findPlan9Symbol(f *plan9obj.File, name string) (*plan9obj.Sym, error) {
- syms, err := f.Symbols()
- if err != nil {
- return nil, err
- }
- for _, s := range syms {
- if s.Name != name {
- continue
- }
- return &s, nil
- }
- return nil, fmt.Errorf("no %s symbol found", name)
-}
-
-func loadPlan9Table(f *plan9obj.File, sname, ename string) ([]byte, error) {
- ssym, err := findPlan9Symbol(f, sname)
- if err != nil {
- return nil, err
- }
- esym, err := findPlan9Symbol(f, ename)
- if err != nil {
- return nil, err
- }
- sect := f.Section("text")
- if sect == nil {
- return nil, err
- }
- data, err := sect.Data()
- if err != nil {
- return nil, err
- }
- textStart := f.LoadAddress + f.HdrSize
- return data[ssym.Value-textStart : esym.Value-textStart], nil
-}
diff --git a/src/cmd/api/goapi.go b/src/cmd/api/goapi.go
index 932b5520f4..7e8f858483 100644
--- a/src/cmd/api/goapi.go
+++ b/src/cmd/api/goapi.go
@@ -378,7 +378,51 @@ func (w *Walker) parseFile(dir, file string) (*ast.File, error) {
}
if w.context != nil && file == fmt.Sprintf("zruntime_defs_%s_%s.go", w.context.GOOS, w.context.GOARCH) {
// Just enough to keep the api checker happy.
- src := "package runtime; type maptype struct{}; type _type struct{}; type alg struct{}; type mspan struct{}; type m struct{}; type lock struct{}; type slicetype struct{};"
+ src := "package runtime; type (" +
+ " _defer struct{};" +
+ " _func struct{};" +
+ " _panic struct{};" +
+ " _select struct{}; " +
+ " _type struct{};" +
+ " alg struct{};" +
+ " chantype struct{};" +
+ " context struct{};" + // windows
+ " eface struct{};" +
+ " funcval struct{};" +
+ " g struct{};" +
+ " gobuf struct{};" +
+ " hchan struct{};" +
+ " iface struct{};" +
+ " interfacetype struct{};" +
+ " itab struct{};" +
+ " m struct{};" +
+ " maptype struct{};" +
+ " mcache struct{};" +
+ " mspan struct{};" +
+ " mutex struct{};" +
+ " note struct{};" +
+ " slicetype struct{};" +
+ " stkframe struct{};" +
+ " sudog struct{};" +
+ " waitq struct{};" +
+ " wincallbackcontext struct{};" +
+ " keventt struct{};" +
+ " timespec struct{};" +
+ " epollevent struct{};" +
+ "); " +
+ "const (" +
+ " cb_max = 2000;" +
+ " _CacheLineSize = 64;" +
+ " _Gidle = 1;" +
+ " _Grunnable = 2;" +
+ " _Grunning = 3;" +
+ " _Gsyscall = 4;" +
+ " _Gwaiting = 5;" +
+ " _Gdead = 6;" +
+ " _Genqueue = 7;" +
+ " _Gcopystack = 8;" +
+ " _NSIG = 32;" +
+ ")"
f, err = parser.ParseFile(fset, filename, src, 0)
if err != nil {
log.Fatalf("incorrect generated file: %s", err)
diff --git a/src/cmd/api/run.go b/src/cmd/api/run.go
index 896b2b4a1a..c323deb603 100644
--- a/src/cmd/api/run.go
+++ b/src/cmd/api/run.go
@@ -98,11 +98,9 @@ func prepGoPath() string {
if err == nil {
username = u.Username
} else {
- // Only need to handle Unix here, as Windows's os/user uses
- // native syscall and should work fine without cgo.
username = os.Getenv("USER")
if username == "" {
- log.Fatalf("Error getting current user: %v", err)
+ username = "nobody"
}
}
diff --git a/src/cmd/cc/cc.h b/src/cmd/cc/cc.h
index c8aac12530..1dae5acd90 100644
--- a/src/cmd/cc/cc.h
+++ b/src/cmd/cc/cc.h
@@ -794,7 +794,7 @@ void xcom(Node*);
int32 exreg(Type*);
int32 align(int32, Type*, int, int32*);
int32 maxround(int32, int32);
-int hasdotdotdot(void);
+int hasdotdotdot(Type*);
void linkarchinit(void);
extern schar ewidth[];
diff --git a/src/cmd/cc/cc.y b/src/cmd/cc/cc.y
index 11ee444b7d..8d7cb1472c 100644
--- a/src/cmd/cc/cc.y
+++ b/src/cmd/cc/cc.y
@@ -1043,6 +1043,7 @@ complex:
}
| LSTRUCT sbody
{
+ diag(Z, "struct must have tag");
taggen++;
sprint(symb, "_%d_", taggen);
$$ = dotag(lookup(), TSTRUCT, autobn);
diff --git a/src/cmd/cc/dcl.c b/src/cmd/cc/dcl.c
index a7a9426865..292717d688 100644
--- a/src/cmd/cc/dcl.c
+++ b/src/cmd/cc/dcl.c
@@ -697,7 +697,8 @@ argmark(Node *n, int pass)
{
Type *t;
- autoffset = align(0, thisfn->link, Aarg0, nil);
+ if(hasdotdotdot(thisfn->link))
+ autoffset = align(0, thisfn->link, Aarg0, nil);
stkoff = 0;
for(; n->left != Z; n = n->left) {
if(n->op != OFUNC || n->left->op != ONAME)
@@ -1401,6 +1402,10 @@ xdecl(int c, Type *t, Sym *s)
}
tmerge(t, s);
s->type = t;
+ if(c == CTYPEDEF && (typechlv[t->etype] || typefd[t->etype])) {
+ s->type = copytyp(t);
+ s->type->tag = s;
+ }
s->class = c;
s->block = 0;
s->offset = o;
@@ -1481,12 +1486,9 @@ edecl(int c, Type *t, Sym *s)
{
Type *t1;
- if(s == S) {
- if(!typesu[t->etype])
- diag(Z, "unnamed structure element must be struct/union");
- if(c != CXXX)
- diag(Z, "unnamed structure element cannot have class");
- } else
+ if(s == S)
+ diag(Z, "unnamed structure elements not supported");
+ else
if(c != CXXX)
diag(Z, "structure element cannot have class: %s", s->name);
t1 = t;
diff --git a/src/cmd/cc/godefs.c b/src/cmd/cc/godefs.c
index 3755a8fc09..d3ab52fde4 100644
--- a/src/cmd/cc/godefs.c
+++ b/src/cmd/cc/godefs.c
@@ -154,7 +154,6 @@ static void
printtypename(Type *t)
{
Sym *s;
- Type *t1;
int w;
char *n;
@@ -188,60 +187,27 @@ printtypename(Type *t)
switch(t->etype) {
case TINT:
- Bprint(&outbuf, "int32");
- break;
case TUINT:
- Bprint(&outbuf, "uint32");
- break;
case TCHAR:
- Bprint(&outbuf, "int8");
- break;
case TUCHAR:
- Bprint(&outbuf, "uint8");
- break;
case TSHORT:
- Bprint(&outbuf, "int16");
- break;
case TUSHORT:
- Bprint(&outbuf, "uint16");
- break;
case TLONG:
- // The 32/64-bit ambiguous types (int,uint,uintptr)
- // are assigned a TLONG/TULONG to distinguish them
- // from always 32-bit types which get a TINT/TUINT.
- // (See int_x/uint_x in pkg/runtime/runtime.h.)
- // For LONG and VLONG types, we generate the
- // unqualified Go type when appropriate.
- // This makes it easier to write Go code that
- // modifies objects with autogenerated-from-C types.
- if(ewidth[TIND] == 4)
- Bprint(&outbuf, "int");
- else
- Bprint(&outbuf, "int32");
- break;
case TULONG:
- if(ewidth[TIND] == 4)
- Bprint(&outbuf, "uint");
- else
- Bprint(&outbuf, "uint32");
- break;
case TVLONG:
- if(ewidth[TIND] == 8)
- Bprint(&outbuf, "int");
- else
- Bprint(&outbuf, "int64");
- break;
case TUVLONG:
- if(ewidth[TIND] == 8)
- Bprint(&outbuf, "uint");
- else
- Bprint(&outbuf, "uint64");
- break;
case TFLOAT:
- Bprint(&outbuf, "float32");
- break;
case TDOUBLE:
- Bprint(&outbuf, "float64");
+ // All names used in the runtime code should be typedefs.
+ if(t->tag != nil) {
+ if(strcmp(t->tag->name, "intgo") == 0)
+ Bprint(&outbuf, "int");
+ else if(strcmp(t->tag->name, "uintgo") == 0)
+ Bprint(&outbuf, "uint");
+ else
+ Bprint(&outbuf, "%s", t->tag->name);
+ } else
+ Bprint(&outbuf, "C.%T", t);
break;
case TUNION:
case TSTRUCT:
@@ -251,27 +217,18 @@ printtypename(Type *t)
n = s->name;
else if(t->tag)
n = t->tag->name;
- if(strcmp(n, "String") == 0){
+ if(strcmp(n, "String") == 0)
Bprint(&outbuf, "string");
- } else if(strcmp(n, "Slice") == 0){
+ else if(strcmp(n, "Slice") == 0)
Bprint(&outbuf, "[]byte");
- } else
+ else if(strcmp(n, "Eface") == 0)
+ Bprint(&outbuf, "interface{}");
+ else
Bprint(&outbuf, "%U", n);
break;
case TFUNC:
- Bprint(&outbuf, "func(");
- for(t1 = t->down; t1 != T; t1 = t1->down) {
- if(t1->etype == TVOID)
- break;
- if(t1 != t->down)
- Bprint(&outbuf, ", ");
- printtypename(t1);
- }
- Bprint(&outbuf, ")");
- if(t->link && t->link->etype != TVOID) {
- Bprint(&outbuf, " ");
- printtypename(t->link);
- }
+ // There's no equivalent to a C function in the Go world.
+ Bprint(&outbuf, "unsafe.Pointer");
break;
case TDOT:
Bprint(&outbuf, "...interface{}");
@@ -360,9 +317,9 @@ godefvar(Sym *s)
switch(t->etype) {
case TENUM:
if(!typefd[t->etype])
- Bprint(&outbuf, "const %U = %lld\n", s->name, s->vconst);
+ Bprint(&outbuf, "const %s = %lld\n", s->name, s->vconst);
else
- Bprint(&outbuf, "const %U = %f\n;", s->name, s->fconst);
+ Bprint(&outbuf, "const %s = %f\n;", s->name, s->fconst);
break;
case TFUNC:
diff --git a/src/cmd/cc/pgen.c b/src/cmd/cc/pgen.c
index c2cf0e1010..4b4684a523 100644
--- a/src/cmd/cc/pgen.c
+++ b/src/cmd/cc/pgen.c
@@ -56,24 +56,24 @@ makefuncdatasym(char *namefmt, int64 funcdatakind)
}
int
-hasdotdotdot(void)
+hasdotdotdot(Type *t)
{
- Type *t;
-
- for(t=thisfn->down; t!=T; t=t->down)
+ for(t=t->down; t!=T; t=t->down)
if(t->etype == TDOT)
return 1;
return 0;
}
vlong
-argsize(void)
+argsize(int doret)
{
Type *t;
int32 s;
//print("t=%T\n", thisfn);
- s = align(0, thisfn->link, Aarg0, nil);
+ s = 0;
+ if(hasdotdotdot(thisfn))
+ s = align(s, thisfn->link, Aarg0, nil);
for(t=thisfn->down; t!=T; t=t->down) {
switch(t->etype) {
case TVOID:
@@ -93,6 +93,14 @@ argsize(void)
s = (s+7) & ~7;
else
s = (s+3) & ~3;
+ if(doret && thisfn->link->etype != TVOID) {
+ s = align(s, thisfn->link, Aarg1, nil);
+ s = align(s, thisfn->link, Aarg2, nil);
+ if(thechar == '6')
+ s = (s+7) & ~7;
+ else
+ s = (s+3) & ~3;
+ }
return s;
}
@@ -123,13 +131,14 @@ codgen(Node *n, Node *nn)
nearln = nn->lineno;
p = gtext(n1->sym, stkoff);
+ p->from.sym->cfunc = 1;
sp = p;
/*
* generate funcdata symbol for this function.
* data is filled in at the end of codgen().
*/
- isvarargs = hasdotdotdot();
+ isvarargs = hasdotdotdot(thisfn);
gcargs = nil;
if(!isvarargs)
gcargs = makefuncdatasym("gcargs·%d", FUNCDATA_ArgsPointerMaps);
@@ -212,7 +221,7 @@ supgen(Node *n)
void
gen(Node *n)
{
- Node *l, nod;
+ Node *l, nod, nod1;
Prog *sp, *spc, *spb;
Case *cn;
long sbc, scc;
@@ -273,14 +282,26 @@ loop:
gbranch(ORETURN);
break;
}
+ if(typecmplx[n->type->etype] && !hasdotdotdot(thisfn)) {
+ regret(&nod, n, thisfn, 2);
+ sugen(l, &nod, n->type->width);
+ noretval(3);
+ gbranch(ORETURN);
+ break;
+ }
if(typecmplx[n->type->etype]) {
sugen(l, nodret, n->type->width);
noretval(3);
gbranch(ORETURN);
break;
}
- regret(&nod, n);
+ regret(&nod1, n, thisfn, 2);
+ nod = nod1;
+ if(nod.op != OREGISTER)
+ regalloc(&nod, n, Z);
cgen(l, &nod);
+ if(nod1.op != OREGISTER)
+ gmove(&nod, &nod1);
regfree(&nod);
if(typefd[n->type->etype])
noretval(1);
@@ -729,9 +750,11 @@ dumpgcargs(Type *fn, Sym *sym)
symoffset = 0;
gextern(sym, nodconst(1), symoffset, 4);
symoffset += 4;
- argbytes = (argsize() + ewidth[TIND] - 1);
+ argbytes = (argsize(1) + ewidth[TIND] - 1);
bv = bvalloc((argbytes / ewidth[TIND]) * BitsPerPointer);
- argoffset = align(0, fn->link, Aarg0, nil);
+ argoffset = 0;
+ if(hasdotdotdot(thisfn))
+ argoffset = align(0, fn->link, Aarg0, nil);
if(argoffset > 0) {
// The C calling convention returns structs by copying them to a
// location pointed to by a hidden first argument. This first
diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go
index 841c848332..d77d56c22a 100644
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -229,7 +229,8 @@ func (p *Package) guessKinds(f *File) []*Name {
// Determine kinds for names we already know about,
// like #defines or 'struct foo', before bothering with gcc.
var names, needType []*Name
- for _, n := range f.Name {
+ for _, key := range nameKeys(f.Name) {
+ n := f.Name[key]
// If we've already found this name as a #define
// and we can translate it as a constant value, do so.
if n.Define != "" {
@@ -331,6 +332,7 @@ func (p *Package) guessKinds(f *File) []*Name {
const (
notType = 1 << iota
notConst
+ notDeclared
)
for _, line := range strings.Split(stderr, "\n") {
if !strings.Contains(line, ": error:") {
@@ -365,7 +367,7 @@ func (p *Package) guessKinds(f *File) []*Name {
completed = true
case "not-declared":
- error_(token.NoPos, "%s", strings.TrimSpace(line[c2+1:]))
+ sniff[i] |= notDeclared
case "not-type":
sniff[i] |= notType
case "not-const":
@@ -374,12 +376,12 @@ func (p *Package) guessKinds(f *File) []*Name {
}
if !completed {
- fatalf("%s did not produce error at completed:1\non input:\n%s", p.gccBaseCmd()[0], b.Bytes())
+ fatalf("%s did not produce error at completed:1\non input:\n%s\nfull error output:\n%s", p.gccBaseCmd()[0], b.Bytes(), stderr)
}
for i, n := range names {
switch sniff[i] {
- case 0:
+ default:
error_(token.NoPos, "could not determine kind of name for C.%s", fixGo(n.Go))
case notType:
n.Kind = "const"
@@ -390,6 +392,14 @@ func (p *Package) guessKinds(f *File) []*Name {
}
}
if nerrors > 0 {
+ // Check if compiling the preamble by itself causes any errors,
+ // because the messages we've printed out so far aren't helpful
+ // to users debugging preamble mistakes. See issue 8442.
+ preambleErrors := p.gccErrors([]byte(f.Preamble))
+ if len(preambleErrors) > 0 {
+ error_(token.NoPos, "\n%s errors for preamble:\n%s", p.gccBaseCmd()[0], preambleErrors)
+ }
+
fatalf("unresolved names")
}
@@ -649,7 +659,13 @@ func (p *Package) rewriteRef(f *File) {
f.Name[fpName] = name
}
r.Name = name
- expr = ast.NewIdent(name.Mangle)
+ // Rewrite into call to _Cgo_ptr to prevent assignments. The _Cgo_ptr
+ // function is defined in out.go and simply returns its argument. See
+ // issue 7757.
+ expr = &ast.CallExpr{
+ Fun: &ast.Ident{NamePos: (*r.Expr).Pos(), Name: "_Cgo_ptr"},
+ Args: []ast.Expr{ast.NewIdent(name.Mangle)},
+ }
} else if r.Name.Kind == "type" {
// Okay - might be new(T)
expr = r.Name.Type.Go
@@ -1068,12 +1084,6 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
return t
}
- // clang won't generate DW_AT_byte_size for pointer types,
- // so we have to fix it here.
- if dt, ok := base(dtype).(*dwarf.PtrType); ok && dt.ByteSize == -1 {
- dt.ByteSize = c.ptrSize
- }
-
t := new(Type)
t.Size = dtype.Size() // note: wrong for array of pointers, corrected below
t.Align = -1
@@ -1097,12 +1107,20 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
t.Go = c.Opaque(t.Size)
break
}
+ count := dt.Count
+ if count == -1 {
+ // Indicates flexible array member, which Go doesn't support.
+ // Translate to zero-length array instead.
+ count = 0
+ }
sub := c.Type(dt.Type, pos)
t.Align = sub.Align
t.Go = &ast.ArrayType{
- Len: c.intExpr(dt.Count),
+ Len: c.intExpr(count),
Elt: sub.Go,
}
+ // Recalculate t.Size now that we know sub.Size.
+ t.Size = count * sub.Size
t.C.Set("__typeof__(%s[%d])", sub.C, dt.Count)
case *dwarf.BoolType:
@@ -1203,6 +1221,11 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
}
case *dwarf.PtrType:
+ // Clang doesn't emit DW_AT_byte_size for pointer types.
+ if t.Size != c.ptrSize && t.Size != -1 {
+ fatalf("%s: unexpected: %d-byte pointer type - %s", lineno(pos), t.Size, dtype)
+ }
+ t.Size = c.ptrSize
t.Align = c.ptrSize
if _, ok := base(dt.Type).(*dwarf.VoidType); ok {
@@ -1374,34 +1397,24 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
}
}
- if t.Size <= 0 {
- // Clang does not record the size of a pointer in its DWARF entry,
- // so if dtype is an array, the call to dtype.Size at the top of the function
- // computed the size as the array length * 0 = 0.
- // The type switch called Type (this function) recursively on the pointer
- // entry, and the code near the top of the function updated the size to
- // be correct, so calling dtype.Size again will produce the correct value.
- t.Size = dtype.Size()
- if t.Size < 0 {
- // Unsized types are [0]byte, unless they're typedefs of other types
- // or structs with tags.
- // if so, use the name we've already defined.
- t.Size = 0
- switch dt := dtype.(type) {
- case *dwarf.TypedefType:
- // ok
- case *dwarf.StructType:
- if dt.StructName != "" {
- break
- }
- t.Go = c.Opaque(0)
- default:
- t.Go = c.Opaque(0)
- }
- if t.C.Empty() {
- t.C.Set("void")
+ if t.Size < 0 {
+ // Unsized types are [0]byte, unless they're typedefs of other types
+ // or structs with tags.
+ // if so, use the name we've already defined.
+ t.Size = 0
+ switch dt := dtype.(type) {
+ case *dwarf.TypedefType:
+ // ok
+ case *dwarf.StructType:
+ if dt.StructName != "" {
+ break
}
- return t
+ t.Go = c.Opaque(0)
+ default:
+ t.Go = c.Opaque(0)
+ }
+ if t.C.Empty() {
+ t.C.Set("void")
}
}
@@ -1533,6 +1546,9 @@ func (c *typeConv) pad(fld []*ast.Field, size int64) []*ast.Field {
// Struct conversion: return Go and (6g) C syntax for type.
func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.StructType, csyntax string, align int64) {
+ // Minimum alignment for a struct is 1 byte.
+ align = 1
+
var buf bytes.Buffer
buf.WriteString("struct {")
fld := make([]*ast.Field, 0, 2*len(dt.Field)+1) // enough for padding around every field
diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go
index 1ef78b757c..6586531ada 100644
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -58,16 +58,14 @@ func (p *Package) writeDefs() {
fmt.Fprintf(fgo2, "// Created by cgo - DO NOT EDIT\n\n")
fmt.Fprintf(fgo2, "package %s\n\n", p.PackageName)
fmt.Fprintf(fgo2, "import \"unsafe\"\n\n")
- if *importSyscall {
- fmt.Fprintf(fgo2, "import \"syscall\"\n\n")
- }
if !*gccgo && *importRuntimeCgo {
fmt.Fprintf(fgo2, "import _ \"runtime/cgo\"\n\n")
}
- fmt.Fprintf(fgo2, "type _ unsafe.Pointer\n\n")
if *importSyscall {
- fmt.Fprintf(fgo2, "func _Cerrno(dst *error, x int32) { *dst = syscall.Errno(x) }\n")
+ fmt.Fprintf(fgo2, "import \"syscall\"\n\n")
+ fmt.Fprintf(fgo2, "var _ syscall.Errno\n")
}
+ fmt.Fprintf(fgo2, "func _Cgo_ptr(ptr unsafe.Pointer) unsafe.Pointer { return ptr }\n\n")
typedefNames := make([]string, 0, len(typedef))
for name := range typedef {
@@ -87,9 +85,10 @@ func (p *Package) writeDefs() {
}
if *gccgo {
- fmt.Fprintf(fc, p.cPrologGccgo())
+ fmt.Fprint(fc, p.cPrologGccgo())
} else {
- fmt.Fprintf(fc, cProlog)
+ fmt.Fprint(fc, cProlog)
+ fmt.Fprint(fgo2, goProlog)
}
gccgoSymbolPrefix := p.gccgoSymbolPrefix()
@@ -296,10 +295,6 @@ func (p *Package) structType(n *Name) (string, int64) {
fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad)
off += pad
}
- if n.AddError {
- fmt.Fprint(&buf, "\t\tint e[2*sizeof(void *)/sizeof(int)]; /* error */\n")
- off += 2 * p.PtrSize
- }
if off == 0 {
fmt.Fprintf(&buf, "\t\tchar unused;\n") // avoid empty struct
}
@@ -334,19 +329,18 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) {
}
// Builtins defined in the C prolog.
- inProlog := name == "CString" || name == "GoString" || name == "GoStringN" || name == "GoBytes" || name == "_CMalloc"
+ inProlog := builtinDefs[name] != ""
+ cname := fmt.Sprintf("_cgo%s%s", cPrefix, n.Mangle)
+ paramnames := []string(nil)
+ for i, param := range d.Type.Params.List {
+ paramName := fmt.Sprintf("p%d", i)
+ param.Names = []*ast.Ident{ast.NewIdent(paramName)}
+ paramnames = append(paramnames, paramName)
+ }
if *gccgo {
// Gccgo style hooks.
fmt.Fprint(fgo2, "\n")
- cname := fmt.Sprintf("_cgo%s%s", cPrefix, n.Mangle)
- paramnames := []string(nil)
- for i, param := range d.Type.Params.List {
- paramName := fmt.Sprintf("p%d", i)
- param.Names = []*ast.Ident{ast.NewIdent(paramName)}
- paramnames = append(paramnames, paramName)
- }
-
conf.Fprint(fgo2, fset, d)
fmt.Fprint(fgo2, " {\n")
if !inProlog {
@@ -383,7 +377,7 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) {
fmt.Fprint(fgo2, "}\n")
// declare the C function.
- fmt.Fprintf(fgo2, "//extern _cgo%s%s\n", cPrefix, n.Mangle)
+ fmt.Fprintf(fgo2, "//extern %s\n", cname)
d.Name = ast.NewIdent(cname)
if n.AddError {
l := d.Type.Results.List
@@ -394,61 +388,49 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) {
return
}
- conf.Fprint(fgo2, fset, d)
- fmt.Fprint(fgo2, "\n")
if inProlog {
+ fmt.Fprint(fgo2, builtinDefs[name])
return
}
- var argSize int64
- _, argSize = p.structType(n)
-
// C wrapper calls into gcc, passing a pointer to the argument frame.
- fmt.Fprintf(fc, "#pragma cgo_import_static _cgo%s%s\n", cPrefix, n.Mangle)
- fmt.Fprintf(fc, "void _cgo%s%s(void*);\n", cPrefix, n.Mangle)
- fmt.Fprintf(fc, "\n")
- fmt.Fprintf(fc, "void\n")
- if argSize == 0 {
- argSize++
+ fmt.Fprintf(fc, "#pragma cgo_import_static %s\n", cname)
+ fmt.Fprintf(fc, "void %s(void*);\n", cname)
+ fmt.Fprintf(fc, "void *·%s = %s;\n", cname, cname)
+
+ nret := 0
+ if !void {
+ d.Type.Results.List[0].Names = []*ast.Ident{ast.NewIdent("r1")}
+ nret = 1
}
- // TODO(rsc): The struct here should declare pointers only where
- // there are pointers in the actual argument frame.
- // This is a workaround for golang.org/issue/6397.
- fmt.Fprintf(fc, "·%s(struct{", n.Mangle)
- if n := argSize / p.PtrSize; n > 0 {
- fmt.Fprintf(fc, "void *y[%d];", n)
+ if n.AddError {
+ d.Type.Results.List[nret].Names = []*ast.Ident{ast.NewIdent("r2")}
}
- if n := argSize % p.PtrSize; n > 0 {
- fmt.Fprintf(fc, "uint8 x[%d];", n)
+
+ fmt.Fprint(fgo2, "\n")
+ fmt.Fprintf(fgo2, "var %s unsafe.Pointer\n", cname)
+ conf.Fprint(fgo2, fset, d)
+ fmt.Fprint(fgo2, " {\n")
+
+ // NOTE: Using uintptr to hide from escape analysis.
+ arg := "0"
+ if len(paramnames) > 0 {
+ arg = "uintptr(unsafe.Pointer(&p0))"
+ } else if !void {
+ arg = "uintptr(unsafe.Pointer(&r1))"
}
- fmt.Fprintf(fc, "}p)\n")
- fmt.Fprintf(fc, "{\n")
- fmt.Fprintf(fc, "\truntime·cgocall(_cgo%s%s, &p);\n", cPrefix, n.Mangle)
+
+ prefix := ""
if n.AddError {
- // gcc leaves errno in first word of interface at end of p.
- // check whether it is zero; if so, turn interface into nil.
- // if not, turn interface into errno.
- // Go init function initializes ·_Cerrno with an os.Errno
- // for us to copy.
- fmt.Fprintln(fc, ` {
- int32 e;
- void **v;
- v = (void**)(&p+1) - 2; /* v = final two void* of p */
- e = *(int32*)v;
- v[0] = (void*)0xdeadbeef;
- v[1] = (void*)0xdeadbeef;
- if(e == 0) {
- /* nil interface */
- v[0] = 0;
- v[1] = 0;
- } else {
- ·_Cerrno(v, e); /* fill in v as error for errno e */
- }
- }`)
+ prefix = "errno := "
}
- fmt.Fprintf(fc, "}\n")
- fmt.Fprintf(fc, "\n")
+ fmt.Fprintf(fgo2, "\t%s_cgo_runtime_cgocall_errno(%s, %s)\n", prefix, cname, arg)
+ if n.AddError {
+ fmt.Fprintf(fgo2, "\tif errno != 0 { r2 = syscall.Errno(errno) }\n")
+ }
+ fmt.Fprintf(fgo2, "\treturn\n")
+ fmt.Fprintf(fgo2, "}\n")
}
// writeOutput creates stubs for a specific source file to be compiled by 6g
@@ -521,7 +503,11 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
// Gcc wrapper unpacks the C argument struct
// and calls the actual C function.
- fmt.Fprintf(fgcc, "void\n")
+ if n.AddError {
+ fmt.Fprintf(fgcc, "int\n")
+ } else {
+ fmt.Fprintf(fgcc, "void\n")
+ }
fmt.Fprintf(fgcc, "_cgo%s%s(void *v)\n", cPrefix, n.Mangle)
fmt.Fprintf(fgcc, "{\n")
if n.AddError {
@@ -557,7 +543,7 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
}
fmt.Fprintf(fgcc, ");\n")
if n.AddError {
- fmt.Fprintf(fgcc, "\t*(int*)(a->e) = errno;\n")
+ fmt.Fprintf(fgcc, "\treturn errno;\n")
}
fmt.Fprintf(fgcc, "}\n")
fmt.Fprintf(fgcc, "\n")
@@ -1166,46 +1152,74 @@ const cProlog = `
#include "runtime.h"
#include "cgocall.h"
+static void *cgocall_errno = runtime·cgocall_errno;
+void *·_cgo_runtime_cgocall_errno = &cgocall_errno;
+
+static void *runtime_gostring = runtime·gostring;
+void *·_cgo_runtime_gostring = &runtime_gostring;
+
+static void *runtime_gostringn = runtime·gostringn;
+void *·_cgo_runtime_gostringn = &runtime_gostringn;
+
+static void *runtime_gobytes = runtime·gobytes;
+void *·_cgo_runtime_gobytes = &runtime_gobytes;
+
+static void *runtime_cmalloc = runtime·cmalloc;
+void *·_cgo_runtime_cmalloc = &runtime_cmalloc;
+
void ·_Cerrno(void*, int32);
+`
-void
-·_Cfunc_GoString(int8 *p, String s)
-{
- s = runtime·gostring((byte*)p);
- FLUSH(&s);
+const goProlog = `
+var _cgo_runtime_cgocall_errno func(unsafe.Pointer, uintptr) int32
+var _cgo_runtime_cmalloc func(uintptr) unsafe.Pointer
+`
+
+const goStringDef = `
+var _cgo_runtime_gostring func(*_Ctype_char) string
+func _Cfunc_GoString(p *_Ctype_char) string {
+ return _cgo_runtime_gostring(p)
}
+`
-void
-·_Cfunc_GoStringN(int8 *p, int32 l, String s)
-{
- s = runtime·gostringn((byte*)p, l);
- FLUSH(&s);
+const goStringNDef = `
+var _cgo_runtime_gostringn func(*_Ctype_char, int) string
+func _Cfunc_GoStringN(p *_Ctype_char, l _Ctype_int) string {
+ return _cgo_runtime_gostringn(p, int(l))
}
+`
-void
-·_Cfunc_GoBytes(int8 *p, int32 l, Slice s)
-{
- s = runtime·gobytes((byte*)p, l);
- FLUSH(&s);
+const goBytesDef = `
+var _cgo_runtime_gobytes func(unsafe.Pointer, int) []byte
+func _Cfunc_GoBytes(p unsafe.Pointer, l _Ctype_int) []byte {
+ return _cgo_runtime_gobytes(p, int(l))
}
+`
-void
-·_Cfunc_CString(String s, int8 *p)
-{
- p = runtime·cmalloc(s.len+1);
- runtime·memmove((byte*)p, s.str, s.len);
- p[s.len] = 0;
- FLUSH(&p);
+const cStringDef = `
+func _Cfunc_CString(s string) *_Ctype_char {
+ p := _cgo_runtime_cmalloc(uintptr(len(s)+1))
+ pp := (*[1<<30]byte)(p)
+ copy(pp[:], s)
+ pp[len(s)] = 0
+ return (*_Ctype_char)(p)
}
+`
-void
-·_Cfunc__CMalloc(uintptr n, int8 *p)
-{
- p = runtime·cmalloc(n);
- FLUSH(&p);
+const cMallocDef = `
+func _Cfunc__CMalloc(n _Ctype_size_t) unsafe.Pointer {
+ return _cgo_runtime_cmalloc(uintptr(n))
}
`
+var builtinDefs = map[string]string{
+ "GoString": goStringDef,
+ "GoStringN": goStringNDef,
+ "GoBytes": goBytesDef,
+ "CString": cStringDef,
+ "_CMalloc": cMallocDef,
+}
+
func (p *Package) cPrologGccgo() string {
return strings.Replace(cPrologGccgo, "PREFIX", cPrefix, -1)
}
diff --git a/src/cmd/dist/a.h b/src/cmd/dist/a.h
index 6222e50604..288063b94b 100644
--- a/src/cmd/dist/a.h
+++ b/src/cmd/dist/a.h
@@ -108,9 +108,6 @@ void mkzexperiment(char*, char*);
// buildgo.c
void mkzdefaultcc(char*, char*);
-// goc2c.c
-void goc2c(char*, char*);
-
// main.c
extern int vflag;
extern int sflag;
@@ -129,6 +126,7 @@ bool isfile(char *p);
char* lastelem(char*);
Time mtime(char*);
void readfile(Buf*, char*);
+void copyfile(char*, char*, int);
void run(Buf *b, char *dir, int mode, char *cmd, ...);
void runv(Buf *b, char *dir, int mode, Vec *argv);
void bgrunv(char *dir, int mode, Vec *argv);
diff --git a/src/cmd/dist/buf.c b/src/cmd/dist/buf.c
index 45fb1954d3..2ddc6be752 100644
--- a/src/cmd/dist/buf.c
+++ b/src/cmd/dist/buf.c
@@ -202,7 +202,7 @@ vadd(Vec *v, char *p)
}
// vaddn adds a string consisting of the n bytes at p to the vector.
-void
+static void
vaddn(Vec *v, char *p, int n)
{
char *q;
diff --git a/src/cmd/dist/build.c b/src/cmd/dist/build.c
index 1feeccf417..6fab14a118 100644
--- a/src/cmd/dist/build.c
+++ b/src/cmd/dist/build.c
@@ -35,7 +35,6 @@ bool rebuildall;
bool defaultclang;
static bool shouldbuild(char*, char*);
-static void copy(char*, char*, int);
static void dopack(char*, char*, char**, int);
static char *findgoversion(void);
@@ -628,7 +627,6 @@ char *depsuffix[] = {
".h",
".s",
".go",
- ".goc",
};
// gentab records how to generate some trivial files.
@@ -661,7 +659,7 @@ install(char *dir)
{
char *name, *p, *elem, *prefix, *exe;
bool islib, ispkg, isgo, stale, ispackcmd;
- Buf b, b1, path;
+ Buf b, b1, path, final_path, final_name;
Vec compile, files, link, go, missing, clean, lib, extra;
Time ttarg, t;
int i, j, k, n, doclean, targ;
@@ -676,6 +674,8 @@ install(char *dir)
binit(&b);
binit(&b1);
binit(&path);
+ binit(&final_path);
+ binit(&final_name);
vinit(&compile);
vinit(&files);
vinit(&link);
@@ -688,11 +688,12 @@ install(char *dir)
// path = full path to dir.
bpathf(&path, "%s/src/%s", goroot, dir);
+ bpathf(&final_path, "%s/src/%s", goroot_final, dir);
name = lastelem(dir);
// For misc/prof, copy into the tool directory and we're done.
if(hasprefix(dir, "misc/")) {
- copy(bpathf(&b, "%s/%s", tooldir, name),
+ copyfile(bpathf(&b, "%s/%s", tooldir, name),
bpathf(&b1, "%s/misc/%s", goroot, name), 1);
goto out;
}
@@ -904,17 +905,19 @@ install(char *dir)
// For package runtime, copy some files into the work space.
if(streq(dir, "pkg/runtime")) {
- copy(bpathf(&b, "%s/arch_GOARCH.h", workdir),
+ copyfile(bpathf(&b, "%s/arch_GOARCH.h", workdir),
bpathf(&b1, "%s/arch_%s.h", bstr(&path), goarch), 0);
- copy(bpathf(&b, "%s/defs_GOOS_GOARCH.h", workdir),
+ copyfile(bpathf(&b, "%s/defs_GOOS_GOARCH.h", workdir),
bpathf(&b1, "%s/defs_%s_%s.h", bstr(&path), goos, goarch), 0);
p = bpathf(&b1, "%s/signal_%s_%s.h", bstr(&path), goos, goarch);
if(isfile(p))
- copy(bpathf(&b, "%s/signal_GOOS_GOARCH.h", workdir), p, 0);
- copy(bpathf(&b, "%s/os_GOOS.h", workdir),
+ copyfile(bpathf(&b, "%s/signal_GOOS_GOARCH.h", workdir), p, 0);
+ copyfile(bpathf(&b, "%s/os_GOOS.h", workdir),
bpathf(&b1, "%s/os_%s.h", bstr(&path), goos), 0);
- copy(bpathf(&b, "%s/signals_GOOS.h", workdir),
+ copyfile(bpathf(&b, "%s/signals_GOOS.h", workdir),
bpathf(&b1, "%s/signals_%s.h", bstr(&path), goos), 0);
+ copyfile(bpathf(&b, "%s/pkg/%s_%s/textflag.h", goroot, goos, goarch),
+ bpathf(&b1, "%s/src/cmd/ld/textflag.h", goroot), 0);
}
// Generate any missing files; regenerate existing ones.
@@ -948,26 +951,10 @@ install(char *dir)
// The last batch was required for the generators.
// This one is generated.
if(streq(dir, "pkg/runtime")) {
- copy(bpathf(&b, "%s/zasm_GOOS_GOARCH.h", workdir),
+ copyfile(bpathf(&b, "%s/zasm_GOOS_GOARCH.h", workdir),
bpathf(&b1, "%s/zasm_%s_%s.h", bstr(&path), goos, goarch), 0);
}
- // Generate .c files from .goc files.
- if(streq(dir, "pkg/runtime")) {
- for(i=0; i<files.len; i++) {
- p = files.p[i];
- if(!hassuffix(p, ".goc"))
- continue;
- // b = path/zp but with _goos_goarch.c instead of .goc
- bprintf(&b, "%s%sz%s", bstr(&path), slash, lastelem(p));
- b.len -= 4;
- bwritef(&b, "_%s_%s.c", goos, goarch);
- goc2c(p, bstr(&b));
- vadd(&files, bstr(&b));
- }
- vuniq(&files);
- }
-
if((!streq(goos, gohostos) || !streq(goarch, gohostarch)) && isgo) {
// We've generated the right files; the go command can do the build.
if(vflag > 1)
@@ -1138,9 +1125,9 @@ nobuild:
// In package runtime, we install runtime.h and cgocall.h too,
// for use by cgo compilation.
if(streq(dir, "pkg/runtime")) {
- copy(bpathf(&b, "%s/pkg/%s_%s/cgocall.h", goroot, goos, goarch),
+ copyfile(bpathf(&b, "%s/pkg/%s_%s/cgocall.h", goroot, goos, goarch),
bpathf(&b1, "%s/src/pkg/runtime/cgocall.h", goroot), 0);
- copy(bpathf(&b, "%s/pkg/%s_%s/runtime.h", goroot, goos, goarch),
+ copyfile(bpathf(&b, "%s/pkg/%s_%s/runtime.h", goroot, goos, goarch),
bpathf(&b1, "%s/src/pkg/runtime/runtime.h", goroot), 0);
}
@@ -1277,8 +1264,8 @@ out:
}
// copy copies the file src to dst, via memory (so only good for small files).
-static void
-copy(char *dst, char *src, int exec)
+void
+copyfile(char *dst, char *src, int exec)
{
Buf b;
diff --git a/src/cmd/dist/buildruntime.c b/src/cmd/dist/buildruntime.c
index 9b40a59686..7af38456d5 100644
--- a/src/cmd/dist/buildruntime.c
+++ b/src/cmd/dist/buildruntime.c
@@ -112,7 +112,7 @@ mkzgoos(char *dir, char *file)
bwritestr(&out, "// auto generated by go tool dist\n\n");
- if (streq(goos, "linux")) {
+ if(streq(goos, "linux")) {
bwritestr(&out, "// +build !android\n\n");
}
@@ -169,11 +169,12 @@ mkzasm(char *dir, char *file)
{
int i, n;
char *aggr, *p;
- Buf in, b, out, exp;
+ Buf in, b, b1, out, exp;
Vec argv, lines, fields;
binit(&in);
binit(&b);
+ binit(&b1);
binit(&out);
binit(&exp);
vinit(&argv);
@@ -181,6 +182,10 @@ mkzasm(char *dir, char *file)
vinit(&fields);
bwritestr(&out, "// auto generated by go tool dist\n\n");
+ if(streq(goos, "linux")) {
+ bwritestr(&out, "// +build !android\n\n");
+ }
+
for(i=0; i<nelem(zasmhdr); i++) {
if(hasprefix(goarch, zasmhdr[i].goarch) && hasprefix(goos, zasmhdr[i].goos)) {
bwritestr(&out, zasmhdr[i].hdr);
@@ -190,6 +195,9 @@ mkzasm(char *dir, char *file)
fatal("unknown $GOOS/$GOARCH in mkzasm");
ok:
+ copyfile(bpathf(&b, "%s/pkg/%s_%s/textflag.h", goroot, goos, goarch),
+ bpathf(&b1, "%s/src/cmd/ld/textflag.h", goroot), 0);
+
// Run 6c -D GOOS_goos -D GOARCH_goarch -I workdir -a -n -o workdir/proc.acid proc.c
// to get acid [sic] output. Run once without the -a -o workdir/proc.acid in order to
// report compilation failures (the -o redirects all messages, unfortunately).
@@ -201,6 +209,8 @@ ok:
vadd(&argv, bprintf(&b, "GOARCH_%s", goarch));
vadd(&argv, "-I");
vadd(&argv, bprintf(&b, "%s", workdir));
+ vadd(&argv, "-I");
+ vadd(&argv, bprintf(&b, "%s/pkg/%s_%s", goroot, goos, goarch));
vadd(&argv, "-n");
vadd(&argv, "-a");
vadd(&argv, "-o");
@@ -241,6 +251,8 @@ ok:
aggr = "seh";
else if(streq(fields.p[1], "Alg"))
aggr = "alg";
+ else if(streq(fields.p[1], "Panic"))
+ aggr = "panic";
}
if(hasprefix(lines.p[i], "}"))
aggr = nil;
@@ -273,6 +285,7 @@ ok:
bfree(&in);
bfree(&b);
+ bfree(&b1);
bfree(&out);
bfree(&exp);
vfree(&argv);
@@ -294,6 +307,10 @@ mkzsys(char *dir, char *file)
binit(&out);
bwritestr(&out, "// auto generated by go tool dist\n\n");
+ if(streq(goos, "linux")) {
+ bwritestr(&out, "// +build !android\n\n");
+ }
+
if(streq(goos, "windows")) {
bwritef(&out,
"// runtime·callbackasm is called by external code to\n"
@@ -346,13 +363,24 @@ mkzruntimedefs(char *dir, char *file)
vinit(&seen);
bwritestr(&out, "// auto generated by go tool dist\n"
- "\n"
+ "\n");
+
+ if(streq(goos, "linux")) {
+ bwritestr(&out, "// +build !android\n\n");
+ }
+
+ bwritestr(&out,
"package runtime\n"
"import \"unsafe\"\n"
"var _ unsafe.Pointer\n"
"\n"
);
+ // Do not emit definitions for these.
+ vadd(&seen, "true");
+ vadd(&seen, "false");
+ vadd(&seen, "raceenabled");
+ vadd(&seen, "allgs");
// Run 6c -D GOOS_goos -D GOARCH_goarch -I workdir -q -n -o workdir/runtimedefs
// on each of the runtimedefs C files.
@@ -363,6 +391,8 @@ mkzruntimedefs(char *dir, char *file)
vadd(&argv, bprintf(&b, "GOARCH_%s", goarch));
vadd(&argv, "-I");
vadd(&argv, bprintf(&b, "%s", workdir));
+ vadd(&argv, "-I");
+ vadd(&argv, bprintf(&b, "%s/pkg/%s_%s", goroot, goos, goarch));
vadd(&argv, "-q");
vadd(&argv, "-n");
vadd(&argv, "-o");
@@ -382,15 +412,15 @@ mkzruntimedefs(char *dir, char *file)
splitlines(&lines, bstr(&in));
for(i=0; i<lines.len; i++) {
p = lines.p[i];
- // Drop comment, func, and const lines.
- if(hasprefix(p, "//") || hasprefix(p, "const") || hasprefix(p, "func"))
+ // Drop comment and func lines.
+ if(hasprefix(p, "//") || hasprefix(p, "func"))
continue;
// Note beginning of type or var decl, which can be multiline.
// Remove duplicates. The linear check of seen here makes the
// whole processing quadratic in aggregate, but there are only
// about 100 declarations, so this is okay (and simple).
- if(hasprefix(p, "type ") || hasprefix(p, "var ")) {
+ if(hasprefix(p, "type ") || hasprefix(p, "var ") || hasprefix(p, "const ")) {
splitfields(&fields, p);
if(fields.len < 2)
continue;
@@ -401,6 +431,17 @@ mkzruntimedefs(char *dir, char *file)
}
vadd(&seen, fields.p[1]);
}
+
+ // Const lines are printed in original case (usually upper). Add a leading _ as needed.
+ if(hasprefix(p, "const ")) {
+ if('A' <= p[6] && p[6] <= 'Z')
+ bwritestr(&out, "const _");
+ else
+ bwritestr(&out, "const ");
+ bwritestr(&out, p+6);
+ continue;
+ }
+
if(skip) {
if(hasprefix(p, "}"))
skip = 0;
@@ -409,6 +450,11 @@ mkzruntimedefs(char *dir, char *file)
bwritestr(&out, p);
}
+
+ // Some windows specific const.
+ if(streq(goos, "windows")) {
+ bwritestr(&out, bprintf(&b, "const cb_max = %d\n", MAXWINCB));
+ }
writefile(&out, file, 0);
diff --git a/src/cmd/dist/goc2c.c b/src/cmd/dist/goc2c.c
deleted file mode 100644
index 72cbc1be62..0000000000
--- a/src/cmd/dist/goc2c.c
+++ /dev/null
@@ -1,833 +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.
-
-#include "a.h"
-
-/*
- * Translate a .goc file into a .c file. A .goc file is a combination
- * of a limited form of Go with C.
- */
-
-/*
- package PACKAGENAME
- {# line}
- func NAME([NAME TYPE { , NAME TYPE }]) [(NAME TYPE { , NAME TYPE })] \{
- C code with proper brace nesting
- \}
-*/
-
-/*
- * We generate C code which implements the function such that it can
- * be called from Go and executes the C code.
- */
-
-static char *input;
-static Buf *output;
-#define EOF -1
-
-enum
-{
- use64bitint = 1,
-};
-
-static int
-xgetchar(void)
-{
- int c;
-
- c = *input;
- if(c == 0)
- return EOF;
- input++;
- return c;
-}
-
-static void
-xungetc(void)
-{
- input--;
-}
-
-static void
-xputchar(char c)
-{
- bwrite(output, &c, 1);
-}
-
-static int
-xisspace(int c)
-{
- return c == ' ' || c == '\t' || c == '\r' || c == '\n';
-}
-
-/* Whether we're emitting for gcc */
-static int gcc;
-
-/* File and line number */
-static const char *file;
-static unsigned int lineno;
-
-/* List of names and types. */
-struct params {
- struct params *next;
- char *name;
- char *type;
-};
-
-/* index into type_table */
-enum {
- Bool,
- Float,
- Int,
- Uint,
- Uintptr,
- String,
- Slice,
- Eface,
- Complex128,
- Float32,
- Float64,
-};
-
-static struct {
- char *name;
- int size;
- int rnd; // alignment
-} type_table[] = {
- /*
- * variable sized first, for easy replacement.
- * order matches enum above.
- * default is 32-bit architecture sizes.
- * spelling as in package runtime, so intgo/uintgo not int/uint.
- */
- {"bool", 1},
- {"float", 4},
- {"intgo", 4},
- {"uintgo", 4},
- {"uintptr", 4},
- {"String", 8},
- {"Slice", 12},
- {"Eface", 8},
- {"Complex128", 16},
-
- /* fixed size */
- {"float32", 4},
- {"float64", 8},
- {"byte", 1},
- {"int8", 1},
- {"uint8", 1},
- {"int16", 2},
- {"uint16", 2},
- {"int32", 4},
- {"rune", 4},
- {"uint32", 4},
- {"int64", 8},
- {"uint64", 8},
-
- {nil, 0},
-};
-
-/* Fixed structure alignment (non-gcc only) */
-int structround = 4;
-
-/* Unexpected EOF. */
-static void
-bad_eof(void)
-{
- fatal("%s:%d: unexpected EOF\n", file, lineno);
-}
-
-/* Free a list of parameters. */
-static void
-free_params(struct params *p)
-{
- while (p != nil) {
- struct params *next;
-
- next = p->next;
- xfree(p->name);
- xfree(p->type);
- xfree(p);
- p = next;
- }
-}
-
-/* Read a character, tracking lineno. */
-static int
-getchar_update_lineno(void)
-{
- int c;
-
- c = xgetchar();
- if (c == '\n')
- ++lineno;
- return c;
-}
-
-/* Read a character, giving an error on EOF, tracking lineno. */
-static int
-getchar_no_eof(void)
-{
- int c;
-
- c = getchar_update_lineno();
- if (c == EOF)
- bad_eof();
- return c;
-}
-
-/* Read a character, skipping comments. */
-static int
-getchar_skipping_comments(void)
-{
- int c;
-
- while (1) {
- c = getchar_update_lineno();
- if (c != '/')
- return c;
-
- c = xgetchar();
- if (c == '/') {
- do {
- c = getchar_update_lineno();
- } while (c != EOF && c != '\n');
- return c;
- } else if (c == '*') {
- while (1) {
- c = getchar_update_lineno();
- if (c == EOF)
- return EOF;
- if (c == '*') {
- do {
- c = getchar_update_lineno();
- } while (c == '*');
- if (c == '/')
- break;
- }
- }
- } else {
- xungetc();
- return '/';
- }
- }
-}
-
-/*
- * Read and return a token. Tokens are string or character literals
- * or else delimited by whitespace or by [(),{}].
- * The latter are all returned as single characters.
- */
-static char *
-read_token(void)
-{
- int c, q;
- char *buf;
- unsigned int alc, off;
- char* delims = "(),{}";
-
- while (1) {
- c = getchar_skipping_comments();
- if (c == EOF)
- return nil;
- if (!xisspace(c))
- break;
- }
- alc = 16;
- buf = xmalloc(alc + 1);
- off = 0;
- if(c == '"' || c == '\'') {
- q = c;
- buf[off] = c;
- ++off;
- while (1) {
- if (off+2 >= alc) { // room for c and maybe next char
- alc *= 2;
- buf = xrealloc(buf, alc + 1);
- }
- c = getchar_no_eof();
- buf[off] = c;
- ++off;
- if(c == q)
- break;
- if(c == '\\') {
- buf[off] = getchar_no_eof();
- ++off;
- }
- }
- } else if (xstrrchr(delims, c) != nil) {
- buf[off] = c;
- ++off;
- } else {
- while (1) {
- if (off >= alc) {
- alc *= 2;
- buf = xrealloc(buf, alc + 1);
- }
- buf[off] = c;
- ++off;
- c = getchar_skipping_comments();
- if (c == EOF)
- break;
- if (xisspace(c) || xstrrchr(delims, c) != nil) {
- if (c == '\n')
- lineno--;
- xungetc();
- break;
- }
- }
- }
- buf[off] = '\0';
- return buf;
-}
-
-/* Read a token, giving an error on EOF. */
-static char *
-read_token_no_eof(void)
-{
- char *token = read_token();
- if (token == nil)
- bad_eof();
- return token;
-}
-
-/* Read the package clause, and return the package name. */
-static char *
-read_package(void)
-{
- char *token;
-
- token = read_token_no_eof();
- if (token == nil)
- fatal("%s:%d: no token\n", file, lineno);
- if (!streq(token, "package")) {
- fatal("%s:%d: expected \"package\", got \"%s\"\n",
- file, lineno, token);
- }
- return read_token_no_eof();
-}
-
-/* Read and copy preprocessor lines. */
-static void
-read_preprocessor_lines(void)
-{
- int first;
-
- first = 1;
- while (1) {
- int c;
-
- do {
- c = getchar_skipping_comments();
- } while (xisspace(c));
- if (c != '#') {
- xungetc();
- break;
- }
- if(first) {
- first = 0;
- xputchar('\n');
- }
- xputchar(c);
- do {
- c = getchar_update_lineno();
- xputchar(c);
- } while (c != '\n');
- }
-}
-
-/*
- * Read a type in Go syntax and return a type in C syntax. We only
- * permit basic types and pointers.
- */
-static char *
-read_type(void)
-{
- char *p, *op, *q;
- int pointer_count;
- unsigned int len;
-
- p = read_token_no_eof();
- if (*p != '*' && !streq(p, "int") && !streq(p, "uint"))
- return p;
- op = p;
- pointer_count = 0;
- while (*p == '*') {
- ++pointer_count;
- ++p;
- }
- len = xstrlen(p);
- q = xmalloc(len + 2 + pointer_count + 1);
- xmemmove(q, p, len);
-
- // Turn int/uint into intgo/uintgo.
- if((len == 3 && xmemcmp(q, "int", 3) == 0) || (len == 4 && xmemcmp(q, "uint", 4) == 0)) {
- q[len++] = 'g';
- q[len++] = 'o';
- }
-
- while (pointer_count-- > 0)
- q[len++] = '*';
-
- q[len] = '\0';
- xfree(op);
- return q;
-}
-
-/* Return the size of the given type. */
-static int
-type_size(char *p, int *rnd)
-{
- int i;
-
- if(p[xstrlen(p)-1] == '*') {
- *rnd = type_table[Uintptr].rnd;
- return type_table[Uintptr].size;
- }
-
- if(streq(p, "Iface"))
- p = "Eface";
-
- for(i=0; type_table[i].name; i++)
- if(streq(type_table[i].name, p)) {
- *rnd = type_table[i].rnd;
- return type_table[i].size;
- }
- fatal("%s:%d: unknown type %s\n", file, lineno, p);
- return 0;
-}
-
-/*
- * Read a list of parameters. Each parameter is a name and a type.
- * The list ends with a ')'. We have already read the '('.
- */
-static struct params *
-read_params(int *poffset)
-{
- char *token;
- struct params *ret, **pp, *p;
- int offset, size, rnd;
-
- ret = nil;
- pp = &ret;
- token = read_token_no_eof();
- offset = 0;
- if (!streq(token, ")")) {
- while (1) {
- p = xmalloc(sizeof(struct params));
- p->name = token;
- p->next = nil;
- *pp = p;
- pp = &p->next;
-
- if(streq(token, "...")) {
- p->type = xstrdup("");
- } else {
- p->type = read_type();
- rnd = 0;
- size = type_size(p->type, &rnd);
- if(rnd > structround)
- rnd = structround;
- if(offset%rnd)
- offset += rnd - offset%rnd;
- offset += size;
- }
-
- token = read_token_no_eof();
- if (!streq(token, ","))
- break;
- token = read_token_no_eof();
- }
- }
- if (!streq(token, ")")) {
- fatal("%s:%d: expected '('\n",
- file, lineno);
- }
- if (poffset != nil)
- *poffset = offset;
- return ret;
-}
-
-/*
- * Read a function header. This reads up to and including the initial
- * '{' character. Returns 1 if it read a header, 0 at EOF.
- */
-static int
-read_func_header(char **name, struct params **params, int *paramwid, struct params **rets)
-{
- int lastline;
- char *token;
-
- lastline = -1;
- while (1) {
- read_preprocessor_lines();
- token = read_token();
- if (token == nil)
- return 0;
- if (streq(token, "func")) {
- if(lastline != -1)
- bwritef(output, "\n");
- break;
- }
- if (lastline != lineno) {
- if (lastline == lineno-1)
- bwritef(output, "\n");
- else
- bwritef(output, "\n#line %d \"%s\"\n", lineno, file);
- lastline = lineno;
- }
- bwritef(output, "%s ", token);
- }
-
- *name = read_token_no_eof();
-
- token = read_token();
- if (token == nil || !streq(token, "(")) {
- fatal("%s:%d: expected \"(\"\n",
- file, lineno);
- }
- *params = read_params(paramwid);
-
- token = read_token();
- if (token == nil || !streq(token, "("))
- *rets = nil;
- else {
- *rets = read_params(nil);
- token = read_token();
- }
- if (token == nil || !streq(token, "{")) {
- fatal("%s:%d: expected \"{\"\n",
- file, lineno);
- }
- return 1;
-}
-
-/* Write out parameters. */
-static void
-write_params(struct params *params, int *first)
-{
- struct params *p;
-
- for (p = params; p != nil; p = p->next) {
- if (*first)
- *first = 0;
- else
- bwritef(output, ", ");
- bwritef(output, "%s %s", p->type, p->name);
- }
-}
-
-/* Write a 6g function header. */
-static void
-write_6g_func_header(char *package, char *name, struct params *params,
- int paramwid, struct params *rets)
-{
- int first, n;
- struct params *p;
-
- bwritef(output, "void\n");
- if(!contains(name, "·"))
- bwritef(output, "%s·", package);
- bwritef(output, "%s(", name);
-
- first = 1;
- write_params(params, &first);
-
- /* insert padding to align output struct */
- if(rets != nil && paramwid%structround != 0) {
- n = structround - paramwid%structround;
- if(n & 1)
- bwritef(output, ", uint8");
- if(n & 2)
- bwritef(output, ", uint16");
- if(n & 4)
- bwritef(output, ", uint32");
- }
-
- write_params(rets, &first);
- bwritef(output, ")\n{\n");
-
- for (p = rets; p != nil; p = p->next) {
- if(streq(p->name, "..."))
- continue;
- if(streq(p->type, "Slice"))
- bwritef(output, "\t%s.array = 0;\n\t%s.len = 0;\n\t%s.cap = 0;\n", p->name, p->name, p->name);
- else if(streq(p->type, "String"))
- bwritef(output, "\t%s.str = 0;\n\t%s.len = 0;\n", p->name, p->name);
- else if(streq(p->type, "Eface"))
- bwritef(output, "\t%s.type = 0;\n\t%s.data = 0;\n", p->name, p->name);
- else if(streq(p->type, "Iface"))
- bwritef(output, "\t%s.tab = 0;\n\t%s.data = 0;\n", p->name, p->name);
- else if(streq(p->type, "Complex128"))
- bwritef(output, "\t%s.real = 0;\n\t%s.imag = 0;\n", p->name, p->name);
- else
- bwritef(output, "\t%s = 0;\n", p->name);
- bwritef(output, "\tFLUSH(&%s);\n", p->name);
- }
-}
-
-/* Write a 6g function trailer. */
-static void
-write_6g_func_trailer(struct params *rets)
-{
- struct params *p;
-
- for (p = rets; p != nil; p = p->next)
- if(!streq(p->name, "..."))
- bwritef(output, "\tFLUSH(&%s);\n", p->name);
- bwritef(output, "}\n");
-}
-
-/* Define the gcc function return type if necessary. */
-static void
-define_gcc_return_type(char *package, char *name, struct params *rets)
-{
- struct params *p;
-
- if (rets == nil || rets->next == nil)
- return;
- bwritef(output, "struct %s_%s_ret {\n", package, name);
- for (p = rets; p != nil; p = p->next)
- bwritef(output, " %s %s;\n", p->type, p->name);
- bwritef(output, "};\n");
-}
-
-/* Write out the gcc function return type. */
-static void
-write_gcc_return_type(char *package, char *name, struct params *rets)
-{
- if (rets == nil)
- bwritef(output, "void");
- else if (rets->next == nil)
- bwritef(output, "%s", rets->type);
- else
- bwritef(output, "struct %s_%s_ret", package, name);
-}
-
-/* Write out a gcc function header. */
-static void
-write_gcc_func_header(char *package, char *name, struct params *params,
- struct params *rets)
-{
- int first;
- struct params *p;
-
- define_gcc_return_type(package, name, rets);
- write_gcc_return_type(package, name, rets);
- bwritef(output, " %s_%s(", package, name);
- first = 1;
- write_params(params, &first);
- bwritef(output, ") asm (\"%s.%s\");\n", package, name);
- write_gcc_return_type(package, name, rets);
- bwritef(output, " %s_%s(", package, name);
- first = 1;
- write_params(params, &first);
- bwritef(output, ")\n{\n");
- for (p = rets; p != nil; p = p->next)
- bwritef(output, " %s %s;\n", p->type, p->name);
-}
-
-/* Write out a gcc function trailer. */
-static void
-write_gcc_func_trailer(char *package, char *name, struct params *rets)
-{
- if (rets == nil) {
- // nothing to do
- }
- else if (rets->next == nil)
- bwritef(output, "return %s;\n", rets->name);
- else {
- struct params *p;
-
- bwritef(output, " {\n struct %s_%s_ret __ret;\n", package, name);
- for (p = rets; p != nil; p = p->next)
- bwritef(output, " __ret.%s = %s;\n", p->name, p->name);
- bwritef(output, " return __ret;\n }\n");
- }
- bwritef(output, "}\n");
-}
-
-/* Write out a function header. */
-static void
-write_func_header(char *package, char *name,
- struct params *params, int paramwid,
- struct params *rets)
-{
- if (gcc)
- write_gcc_func_header(package, name, params, rets);
- else
- write_6g_func_header(package, name, params, paramwid, rets);
- bwritef(output, "#line %d \"%s\"\n", lineno, file);
-}
-
-/* Write out a function trailer. */
-static void
-write_func_trailer(char *package, char *name,
- struct params *rets)
-{
- if (gcc)
- write_gcc_func_trailer(package, name, rets);
- else
- write_6g_func_trailer(rets);
-}
-
-/*
- * Read and write the body of the function, ending in an unnested }
- * (which is read but not written).
- */
-static void
-copy_body(void)
-{
- int nesting = 0;
- while (1) {
- int c;
-
- c = getchar_no_eof();
- if (c == '}' && nesting == 0)
- return;
- xputchar(c);
- switch (c) {
- default:
- break;
- case '{':
- ++nesting;
- break;
- case '}':
- --nesting;
- break;
- case '/':
- c = getchar_update_lineno();
- xputchar(c);
- if (c == '/') {
- do {
- c = getchar_no_eof();
- xputchar(c);
- } while (c != '\n');
- } else if (c == '*') {
- while (1) {
- c = getchar_no_eof();
- xputchar(c);
- if (c == '*') {
- do {
- c = getchar_no_eof();
- xputchar(c);
- } while (c == '*');
- if (c == '/')
- break;
- }
- }
- }
- break;
- case '"':
- case '\'':
- {
- int delim = c;
- do {
- c = getchar_no_eof();
- xputchar(c);
- if (c == '\\') {
- c = getchar_no_eof();
- xputchar(c);
- c = '\0';
- }
- } while (c != delim);
- }
- break;
- }
- }
-}
-
-/* Process the entire file. */
-static void
-process_file(void)
-{
- char *package, *name, *p, *n;
- struct params *params, *rets;
- int paramwid;
-
- package = read_package();
- read_preprocessor_lines();
- while (read_func_header(&name, &params, &paramwid, &rets)) {
- // name may have a package override already
- n = xstrstr(name, "·");
- if(n != nil) {
- p = xmalloc(n - name + 1);
- xmemmove(p, name, n - name);
- p[n - name] = 0;
- n += xstrlen("·");
- } else {
- p = package;
- n = name;
- }
- write_func_header(p, n, params, paramwid, rets);
- copy_body();
- write_func_trailer(p, n, rets);
- xfree(name);
- if(p != package) xfree(p);
- free_params(params);
- free_params(rets);
- }
- xfree(package);
-}
-
-void
-goc2c(char *goc, char *c)
-{
- int i;
- Buf in, out;
-
- binit(&in);
- binit(&out);
-
- file = goc;
- readfile(&in, goc);
-
- // TODO: set gcc=1 when using gcc
-
- if(!gcc) {
- if(contains(goarch, "64p32")) {
- type_table[Uintptr].size = 4;
- type_table[Int].size = 4;
- structround = 8;
- } else if(contains(goarch, "64")) {
- type_table[Uintptr].size = 8;
- if(use64bitint) {
- type_table[Int].size = 8;
- } else {
- type_table[Int].size = 4;
- }
- structround = 8;
- } else {
- // NOTE: These are set in the initializer,
- // but they might have been changed by a
- // previous invocation of goc2c, so we have
- // to restore them.
- type_table[Uintptr].size = 4;
- type_table[Int].size = 4;
- structround = 4;
- }
-
- type_table[Uint].size = type_table[Int].size;
- type_table[Slice].size = type_table[Uintptr].size+2*type_table[Int].size;
- type_table[Eface].size = 2*type_table[Uintptr].size;
- type_table[String].size = 2*type_table[Uintptr].size;
-
- for(i=0; i<nelem(type_table); i++)
- type_table[i].rnd = type_table[i].size;
-
- type_table[String].rnd = type_table[Uintptr].rnd;
- type_table[Slice].rnd = type_table[Uintptr].rnd;
- type_table[Eface].rnd = type_table[Uintptr].rnd;
- type_table[Complex128].rnd = type_table[Float64].rnd;
- }
-
- bprintf(&out, "// auto generated by go tool dist\n// goos=%s goarch=%s\n\n", goos, goarch);
- input = bstr(&in);
- output = &out;
-
- lineno = 1;
- process_file();
-
- writefile(&out, c, 0);
-}
diff --git a/src/cmd/dist/plan9.c b/src/cmd/dist/plan9.c
index 8d492ebc67..e4bf251475 100644
--- a/src/cmd/dist/plan9.c
+++ b/src/cmd/dist/plan9.c
@@ -23,7 +23,7 @@ bprintf(Buf *b, char *fmt, ...)
{
va_list arg;
char buf[4096];
-
+
breset(b);
va_start(arg, fmt);
vsnprintf(buf, sizeof buf, fmt, arg);
@@ -572,10 +572,10 @@ bool
hassuffix(char *p, char *suffix)
{
int np, ns;
-
+
np = strlen(p);
ns = strlen(suffix);
- return np >= ns && strcmp(p+np-ns, suffix) == 0;
+ return np >= ns && streq(p+np-ns, suffix);
}
// hasprefix reports whether p begins with prefix.
diff --git a/src/cmd/dist/unix.c b/src/cmd/dist/unix.c
index 668b189cac..893ed5cdd2 100644
--- a/src/cmd/dist/unix.c
+++ b/src/cmd/dist/unix.c
@@ -431,7 +431,7 @@ xremoveall(char *p)
}
bfree(&b);
- vfree(&dir);
+ vfree(&dir);
}
// xreaddir replaces dst with a list of the names of the files in dir.
@@ -441,13 +441,13 @@ xreaddir(Vec *dst, char *dir)
{
DIR *d;
struct dirent *dp;
-
+
vreset(dst);
d = opendir(dir);
if(d == nil)
fatal("opendir %s: %s", dir, strerror(errno));
while((dp = readdir(d)) != nil) {
- if(strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
+ if(streq(dp->d_name, ".") || streq(dp->d_name, ".."))
continue;
vadd(dst, dp->d_name);
}
@@ -461,7 +461,7 @@ xworkdir(void)
{
Buf b;
char *p;
-
+
binit(&b);
xgetenv(&b, "TMPDIR");
@@ -546,10 +546,10 @@ bool
hassuffix(char *p, char *suffix)
{
int np, ns;
-
+
np = strlen(p);
ns = strlen(suffix);
- return np >= ns && strcmp(p+np-ns, suffix) == 0;
+ return np >= ns && streq(p+np-ns, suffix);
}
// hasprefix reports whether p begins with prefix.
@@ -716,7 +716,7 @@ main(int argc, char **argv)
fatal("unknown architecture: %s", u.machine);
}
- if(strcmp(gohostarch, "arm") == 0)
+ if(streq(gohostarch, "arm"))
maxnbg = 1;
// The OS X 10.6 linker does not support external linking mode.
@@ -728,7 +728,7 @@ main(int argc, char **argv)
//
// Roughly, OS X 10.N shows up as uname release (N+4),
// so OS X 10.6 is uname version 10 and OS X 10.8 is uname version 12.
- if(strcmp(gohostos, "darwin") == 0) {
+ if(streq(gohostos, "darwin")) {
if(uname(&u) < 0)
fatal("uname: %s", strerror(errno));
osx = atoi(u.release) - 4;
diff --git a/src/cmd/dist/windows.c b/src/cmd/dist/windows.c
index 2839c4bc51..1102adff5e 100644
--- a/src/cmd/dist/windows.c
+++ b/src/cmd/dist/windows.c
@@ -770,10 +770,10 @@ bool
hassuffix(char *p, char *suffix)
{
int np, ns;
-
+
np = strlen(p);
ns = strlen(suffix);
- return np >= ns && strcmp(p+np-ns, suffix) == 0;
+ return np >= ns && streq(p+np-ns, suffix);
}
bool
diff --git a/src/cmd/fix/doc.go b/src/cmd/fix/doc.go
index 5de3e08c59..0570169576 100644
--- a/src/cmd/fix/doc.go
+++ b/src/cmd/fix/doc.go
@@ -27,7 +27,7 @@ rewrites are idempotent, so that it is safe to apply fix to updated
or partially updated code even without using the -r flag.
Fix prints the full list of fixes it can apply in its help output;
-to see them, run go tool fix -?.
+to see them, run go tool fix -help.
Fix does not make backup copies of the files that it edits.
Instead, use a version control system's ``diff'' functionality to inspect
diff --git a/src/cmd/gc/builtin.c b/src/cmd/gc/builtin.c
index 4808269b7f..60b7c2f977 100644
--- a/src/cmd/gc/builtin.c
+++ b/src/cmd/gc/builtin.c
@@ -9,11 +9,12 @@ char *runtimeimport =
"func @\"\".throwreturn ()\n"
"func @\"\".throwinit ()\n"
"func @\"\".panicwrap (? string, ? string, ? string)\n"
- "func @\"\".panic (? interface {})\n"
- "func @\"\".recover (? *int32) (? interface {})\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"
@@ -64,7 +65,6 @@ char *runtimeimport =
"func @\"\".efaceeq (@\"\".i1·2 any, @\"\".i2·3 any) (@\"\".ret·1 bool)\n"
"func @\"\".ifacethash (@\"\".i1·2 any) (@\"\".ret·1 uint32)\n"
"func @\"\".efacethash (@\"\".i1·2 any) (@\"\".ret·1 uint32)\n"
- "func @\"\".equal (@\"\".typ·2 *byte, @\"\".x1·3 any, @\"\".x2·4 any) (@\"\".ret·1 bool)\n"
"func @\"\".makemap (@\"\".mapType·2 *byte, @\"\".hint·3 int64) (@\"\".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"
@@ -96,12 +96,12 @@ char *runtimeimport =
"func @\"\".makeslice (@\"\".typ·2 *byte, @\"\".nel·3 int64, @\"\".cap·4 int64) (@\"\".ary·1 []any)\n"
"func @\"\".growslice (@\"\".typ·2 *byte, @\"\".old·3 []any, @\"\".n·4 int64) (@\"\".ary·1 []any)\n"
"func @\"\".memmove (@\"\".to·1 *any, @\"\".frm·2 *any, @\"\".length·3 uintptr)\n"
- "func @\"\".memequal (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n"
- "func @\"\".memequal8 (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n"
- "func @\"\".memequal16 (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n"
- "func @\"\".memequal32 (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n"
- "func @\"\".memequal64 (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n"
- "func @\"\".memequal128 (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n"
+ "func @\"\".memequal (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\n"
+ "func @\"\".memequal8 (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\n"
+ "func @\"\".memequal16 (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\n"
+ "func @\"\".memequal32 (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\n"
+ "func @\"\".memequal64 (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\n"
+ "func @\"\".memequal128 (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\n"
"func @\"\".int64div (? int64, ? int64) (? int64)\n"
"func @\"\".uint64div (? uint64, ? uint64) (? uint64)\n"
"func @\"\".int64mod (? int64, ? int64) (? int64)\n"
diff --git a/src/cmd/gc/bv.c b/src/cmd/gc/bv.c
index 0e8f8d4739..cfd1cd2811 100644
--- a/src/cmd/gc/bv.c
+++ b/src/cmd/gc/bv.c
@@ -108,6 +108,9 @@ bvnext(Bvec *bv, int32 i)
{
uint32 w;
+ if(i >= bv->n)
+ return -1;
+
// Jump i ahead to next word with bits.
if((bv->b[i>>WORDSHIFT]>>(i&WORDMASK)) == 0) {
i &= ~WORDMASK;
@@ -117,7 +120,7 @@ bvnext(Bvec *bv, int32 i)
}
if(i >= bv->n)
return -1;
-
+
// Find 1 bit.
w = bv->b[i>>WORDSHIFT]>>(i&WORDMASK);
while((w&1) == 0) {
diff --git a/src/cmd/gc/const.c b/src/cmd/gc/const.c
index c01784a81b..e418b9c561 100644
--- a/src/cmd/gc/const.c
+++ b/src/cmd/gc/const.c
@@ -1566,7 +1566,6 @@ isgoconst(Node *n)
case ORSH:
case OSUB:
case OXOR:
- case OCONV:
case OIOTA:
case OCOMPLEX:
case OREAL:
@@ -1574,7 +1573,12 @@ isgoconst(Node *n)
if(isgoconst(n->left) && (n->right == N || isgoconst(n->right)))
return 1;
break;
-
+
+ case OCONV:
+ if(okforconst[n->type->etype] && isgoconst(n->left))
+ return 1;
+ break;
+
case OLEN:
case OCAP:
l = n->left;
diff --git a/src/cmd/gc/fmt.c b/src/cmd/gc/fmt.c
index 951170aeff..98556a658f 100644
--- a/src/cmd/gc/fmt.c
+++ b/src/cmd/gc/fmt.c
@@ -1153,7 +1153,7 @@ exprfmt(Fmt *f, Node *n, int prec)
case Csend:
return fmtprint(f, "chan<- %N", n->left);
default:
- if(n->left != N && n->left->op == TCHAN && n->left->sym == S && n->left->etype == Crecv)
+ if(n->left != N && n->left->op == OTCHAN && n->left->sym == S && n->left->etype == Crecv)
return fmtprint(f, "chan (%N)", n->left);
else
return fmtprint(f, "chan %N", n->left);
diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c
index 908a5e53d9..86acd88259 100644
--- a/src/cmd/gc/gen.c
+++ b/src/cmd/gc/gen.c
@@ -806,7 +806,8 @@ cgen_eface(Node *n, Node *res)
void
cgen_slice(Node *n, Node *res)
{
- Node src, dst, *cap, *len, *offs, *add, *base;
+ Node src, dst, *cap, *len, *offs, *add, *base, *tmpcap, *tmplen, *cmp, con;
+ Prog *p1, *p2;
cap = n->list->n;
len = n->list->next->n;
@@ -823,6 +824,11 @@ cgen_slice(Node *n, Node *res)
// garbage collector can see.
base = temp(types[TUINTPTR]);
+ tmplen = temp(types[TINT]);
+ if(n->op != OSLICESTR)
+ tmpcap = temp(types[TINT]);
+ else
+ tmpcap = tmplen;
if(isnil(n->left)) {
tempname(&src, n->left->type);
@@ -837,43 +843,62 @@ cgen_slice(Node *n, Node *res)
fatal("slicearr is supposed to work on pointer: %+N\n", n);
cgen(&src, base);
cgen_checknil(base);
- if(offs != N) {
- add = nod(OADD, base, offs);
- typecheck(&add, Erv);
- cgen(add, base);
- }
- } else if(offs == N) {
- src.type = types[tptr];
- cgen(&src, base);
} else {
src.type = types[tptr];
- add = nod(OADDPTR, &src, offs);
- typecheck(&add, Erv);
- cgen(add, base);
+ cgen(&src, base);
}
// committed to the update
gvardef(res);
+ // compute len and cap.
+ // len = n-i, cap = m-i, and offs = i*width.
+ // computing offs last lets the multiply overwrite i.
+ cgen(len, tmplen);
+ if(n->op != OSLICESTR)
+ cgen(cap, tmpcap);
+
+ // if new cap != 0 { base += add }
+ // This avoids advancing base past the end of the underlying array/string,
+ // so that it cannot point at the next object in memory.
+ // If cap == 0, the base doesn't matter except insofar as it is 0 or non-zero.
+ // In essence we are replacing x[i:j:k] where i == j == k
+ // or x[i:j] where i == j == cap(x) with x[0:0:0].
+ if(offs != N) {
+ p1 = gjmp(P);
+ p2 = gjmp(P);
+ patch(p1, pc);
+
+ nodconst(&con, tmpcap->type, 0);
+ cmp = nod(OEQ, tmpcap, &con);
+ typecheck(&cmp, Erv);
+ bgen(cmp, 1, -1, p2);
+
+ add = nod(OADD, base, offs);
+ typecheck(&add, Erv);
+ cgen(add, base);
+
+ patch(p2, pc);
+ }
+
// dst.array = src.array [ + lo *width ]
dst = *res;
dst.xoffset += Array_array;
dst.type = types[tptr];
-
cgen(base, &dst);
// dst.len = hi [ - lo ]
dst = *res;
dst.xoffset += Array_nel;
dst.type = types[simtype[TUINT]];
- cgen(len, &dst);
+ cgen(tmplen, &dst);
if(n->op != OSLICESTR) {
// dst.cap = cap [ - lo ]
dst = *res;
dst.xoffset += Array_cap;
dst.type = types[simtype[TUINT]];
- cgen(cap, &dst);
+ cgen(tmpcap, &dst);
}
}
diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h
index c3da5f636a..12c1e98539 100644
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -447,7 +447,6 @@ enum
OSUB, // x - y
OOR, // x | y
OXOR, // x ^ y
- OADDPTR, // ptr + uintptr, inserted by compiler only, used to avoid unsafe type changes during codegen
OADDSTR, // s + "foo"
OADDR, // &x
OANDAND, // b0 && b1
@@ -1169,6 +1168,7 @@ void cgen_callmeth(Node *n, int proc);
void cgen_eface(Node* n, Node* res);
void cgen_slice(Node* n, Node* res);
void clearlabels(void);
+void clearslim(Node*);
void checklabels(void);
int dotoffset(Node *n, int64 *oary, Node **nn);
void gen(Node *n);
@@ -1363,6 +1363,7 @@ int is64(Type *t);
int isbadimport(Strlit *s);
int isblank(Node *n);
int isblanksym(Sym *s);
+int isdirectiface(Type*);
int isfixedarray(Type *t);
int isideal(Type *t);
int isinter(Type *t);
diff --git a/src/cmd/gc/mparith1.c b/src/cmd/gc/mparith1.c
index 1519caec7a..d33a81e09d 100644
--- a/src/cmd/gc/mparith1.c
+++ b/src/cmd/gc/mparith1.c
@@ -591,7 +591,7 @@ Fconv(Fmt *fp)
d = mpgetflt(fvp);
if(d >= 0 && (fp->flags & FmtSign))
fmtprint(fp, "+");
- return fmtprint(fp, "%g", d, exp, fvp);
+ return fmtprint(fp, "%g", d);
}
// very out of range. compute decimal approximation by hand.
diff --git a/src/cmd/gc/mparith3.c b/src/cmd/gc/mparith3.c
index 95618f1c61..6afd75c023 100644
--- a/src/cmd/gc/mparith3.c
+++ b/src/cmd/gc/mparith3.c
@@ -251,8 +251,8 @@ mpgetfltN(Mpflt *a, int prec, int bias)
s = minexp - e;
if(s > prec+1)
s = prec+1;
- if((v & ((1<<s)-1)) != 0)
- v |= 1<<s;
+ if((v & ((1ULL<<s)-1)) != 0)
+ v |= 1ULL<<s;
v >>= s;
e = minexp;
}
diff --git a/src/cmd/gc/order.c b/src/cmd/gc/order.c
index 30dbc7dacc..d11e9828cb 100644
--- a/src/cmd/gc/order.c
+++ b/src/cmd/gc/order.c
@@ -593,7 +593,10 @@ orderstmt(Node *n, Order *order)
orderexpr(&n->rlist->n->left, order); // arg to recv
ch = n->rlist->n->left->type;
tmp1 = ordertemp(ch->type, order, haspointers(ch->type));
- tmp2 = ordertemp(types[TBOOL], order, 0);
+ if(!isblank(n->list->next->n))
+ tmp2 = ordertemp(n->list->next->n->type, order, 0);
+ else
+ tmp2 = ordertemp(types[TBOOL], order, 0);
order->out = list(order->out, n);
r = nod(OAS, n->list->n, tmp1);
typecheck(&r, Etop);
@@ -768,6 +771,12 @@ orderstmt(Node *n, Order *order)
// Special: clean case temporaries in each block entry.
// Select must enter one of its blocks, so there is no
// need for a cleaning at the end.
+ // Doubly special: evaluation order for select is stricter
+ // than ordinary expressions. Even something like p.c
+ // has to be hoisted into a temporary, so that it cannot be
+ // reordered after the channel evaluation for a different
+ // case (if p were nil, then the timing of the fault would
+ // give this away).
t = marktemp(order);
for(l=n->list; l; l=l->next) {
if(l->n->op != OXCASE)
@@ -810,6 +819,8 @@ orderstmt(Node *n, Order *order)
// r->left == N means 'case <-c'.
// c is always evaluated; x and ok are only evaluated when assigned.
orderexpr(&r->right->left, order);
+ if(r->right->left->op != ONAME)
+ r->right->left = ordercopyexpr(r->right->left, r->right->left->type, order, 0);
// Introduce temporary for receive and move actual copy into case body.
// avoids problems with target being addressed, as usual.
@@ -1055,6 +1066,19 @@ orderexpr(Node **np, Order *order)
orderexpr(&n->left, order);
n = ordercopyexpr(n, n->type, order, 1);
break;
+
+ case OEQ:
+ case ONE:
+ orderexpr(&n->left, order);
+ orderexpr(&n->right, order);
+ t = n->left->type;
+ if(t->etype == TSTRUCT || isfixedarray(t)) {
+ // for complex comparisons, we need both args to be
+ // addressable so we can pass them to the runtime.
+ orderaddrtemp(&n->left, order);
+ orderaddrtemp(&n->right, order);
+ }
+ break;
}
lineno = lno;
diff --git a/src/cmd/gc/plive.c b/src/cmd/gc/plive.c
index 716cdd108d..dc4edad631 100644
--- a/src/cmd/gc/plive.c
+++ b/src/cmd/gc/plive.c
@@ -1113,8 +1113,7 @@ twobitwalktype1(Type *t, vlong *xoffset, Bvec *bv)
// struct { byte *str; intgo len; }
if((*xoffset & (widthptr-1)) != 0)
fatal("twobitwalktype1: invalid alignment, %T", t);
- bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 0);
- bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 3:0 = multiword:string
+ bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr in first slot
*xoffset += t->width;
break;
@@ -1145,9 +1144,7 @@ twobitwalktype1(Type *t, vlong *xoffset, Bvec *bv)
// struct { byte *array; uintgo len; uintgo cap; }
if((*xoffset & (widthptr-1)) != 0)
fatal("twobitwalktype1: invalid TARRAY alignment, %T", t);
- bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 0);
- bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1);
- bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 2); // 3:1 = multiword/slice
+ bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr in first slot
*xoffset += t->width;
} else
for(i = 0; i < t->bound; i++)
diff --git a/src/cmd/gc/popt.c b/src/cmd/gc/popt.c
index 4c75e6c265..6e6db88ef8 100644
--- a/src/cmd/gc/popt.c
+++ b/src/cmd/gc/popt.c
@@ -49,7 +49,7 @@ noreturn(Prog *p)
symlist[0] = pkglookup("panicindex", runtimepkg);
symlist[1] = pkglookup("panicslice", runtimepkg);
symlist[2] = pkglookup("throwinit", runtimepkg);
- symlist[3] = pkglookup("panic", runtimepkg);
+ symlist[3] = pkglookup("gopanic", runtimepkg);
symlist[4] = pkglookup("panicwrap", runtimepkg);
symlist[5] = pkglookup("throwreturn", runtimepkg);
symlist[6] = pkglookup("selectgo", runtimepkg);
diff --git a/src/cmd/gc/racewalk.c b/src/cmd/gc/racewalk.c
index 285bd78a25..27581702cc 100644
--- a/src/cmd/gc/racewalk.c
+++ b/src/cmd/gc/racewalk.c
@@ -419,8 +419,10 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
ret:
if(n->op != OBLOCK) // OBLOCK is handled above in a special way.
racewalklist(n->list, init);
- racewalknode(&n->ntest, &n->ntest->ninit, 0, 0);
- racewalknode(&n->nincr, &n->nincr->ninit, 0, 0);
+ if(n->ntest != N)
+ racewalknode(&n->ntest, &n->ntest->ninit, 0, 0);
+ if(n->nincr != N)
+ racewalknode(&n->nincr, &n->nincr->ninit, 0, 0);
racewalklist(n->nbody, nil);
racewalklist(n->nelse, nil);
racewalklist(n->rlist, nil);
diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c
index 8170c15b62..f227054caf 100644
--- a/src/cmd/gc/reflect.c
+++ b/src/cmd/gc/reflect.c
@@ -378,7 +378,7 @@ methods(Type *t)
// type stored in interface word
it = t;
- if(it->width > widthptr)
+ if(!isdirectiface(it))
it = ptrto(t);
// make list of methods for t,
@@ -724,7 +724,7 @@ dcommontype(Sym *s, int ot, Type *t)
if(ot != 0)
fatal("dcommontype %d", ot);
- sizeofAlg = 4*widthptr;
+ sizeofAlg = 2*widthptr;
if(algarray == nil)
algarray = pkglookup("algarray", runtimepkg);
alg = algtype(t);
@@ -785,6 +785,8 @@ dcommontype(Sym *s, int ot, Type *t)
i = KindSlice;
if(!haspointers(t))
i |= KindNoPointers;
+ if(isdirectiface(t))
+ i |= KindDirectIface;
if(gcprog)
i |= KindGCProg;
ot = duint8(s, ot, i); // kind
@@ -1239,8 +1241,7 @@ static Sym*
dalgsym(Type *t)
{
int ot;
- Sym *s, *hash, *hashfunc, *eq;
- char buf[100];
+ Sym *s, *hash, *hashfunc, *eq, *eqfunc;
// dalgsym is only called for a type that needs an algorithm table,
// which implies that the type is comparable (or else it would use ANOEQ).
@@ -1251,29 +1252,18 @@ dalgsym(Type *t)
eq = typesymprefix(".eq", t);
geneq(eq, t);
- // make Go func (a closure) for calling the hash function from Go
+ // make Go funcs (closures) for calling hash and equal from Go
hashfunc = typesymprefix(".hashfunc", t);
dsymptr(hashfunc, 0, hash, 0);
ggloblsym(hashfunc, widthptr, DUPOK|RODATA);
+ eqfunc = typesymprefix(".eqfunc", t);
+ dsymptr(eqfunc, 0, eq, 0);
+ ggloblsym(eqfunc, widthptr, DUPOK|RODATA);
- // ../../pkg/runtime/runtime.h:/Alg
+ // ../../pkg/runtime/alg.go:/typeAlg
ot = 0;
ot = dsymptr(s, ot, hashfunc, 0);
- ot = dsymptr(s, ot, eq, 0);
- ot = dsymptr(s, ot, pkglookup("memprint", runtimepkg), 0);
- switch(t->width) {
- default:
- ot = dsymptr(s, ot, pkglookup("memcopy", runtimepkg), 0);
- break;
- case 1:
- case 2:
- case 4:
- case 8:
- case 16:
- snprint(buf, sizeof buf, "memcopy%d", (int)t->width*8);
- ot = dsymptr(s, ot, pkglookup(buf, runtimepkg), 0);
- break;
- }
+ ot = dsymptr(s, ot, eqfunc, 0);
ggloblsym(s, ot, DUPOK|RODATA);
return s;
@@ -1511,8 +1501,8 @@ gengcprog1(ProgGen *g, Type *t, vlong *xoffset)
*xoffset += t->width;
break;
case TSTRING:
- proggendata(g, BitsMultiWord);
- proggendata(g, BitsString);
+ proggendata(g, BitsPointer);
+ proggendata(g, BitsScalar);
*xoffset += t->width;
break;
case TINTER:
@@ -1525,8 +1515,8 @@ gengcprog1(ProgGen *g, Type *t, vlong *xoffset)
break;
case TARRAY:
if(isslice(t)) {
- proggendata(g, BitsMultiWord);
- proggendata(g, BitsSlice);
+ proggendata(g, BitsPointer);
+ proggendata(g, BitsScalar);
proggendata(g, BitsScalar);
} else {
t1 = t->type;
diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go
index 0257c3c7d6..128fd1a31c 100644
--- a/src/cmd/gc/runtime.go
+++ b/src/cmd/gc/runtime.go
@@ -20,12 +20,13 @@ func throwreturn()
func throwinit()
func panicwrap(string, string, string)
-func panic(interface{})
-func recover(*int32) interface{}
+func gopanic(interface{})
+func gorecover(*int32) interface{}
func printbool(bool)
func printfloat(float64)
func printint(int64)
+func printhex(uint64)
func printuint(uint64)
func printcomplex(complex128)
func printstring(string)
@@ -84,8 +85,6 @@ func efaceeq(i1 any, i2 any) (ret bool)
func ifacethash(i1 any) (ret uint32)
func efacethash(i1 any) (ret uint32)
-func equal(typ *byte, x1, x2 any) (ret bool)
-
// *byte is really *runtime.Type
func makemap(mapType *byte, hint int64) (hmap map[any]any)
func mapaccess1(mapType *byte, hmap map[any]any, key *any) (val *any)
@@ -124,12 +123,12 @@ func makeslice(typ *byte, nel int64, cap int64) (ary []any)
func growslice(typ *byte, old []any, n int64) (ary []any)
func memmove(to *any, frm *any, length uintptr)
-func memequal(eq *bool, size uintptr, x, y *any)
-func memequal8(eq *bool, size uintptr, x, y *any)
-func memequal16(eq *bool, size uintptr, x, y *any)
-func memequal32(eq *bool, size uintptr, x, y *any)
-func memequal64(eq *bool, size uintptr, x, y *any)
-func memequal128(eq *bool, size uintptr, x, y *any)
+func memequal(x, y *any, size uintptr) bool
+func memequal8(x, y *any, size uintptr) bool
+func memequal16(x, y *any, size uintptr) bool
+func memequal32(x, y *any, size uintptr) bool
+func memequal64(x, y *any, size uintptr) bool
+func memequal128(x, y *any, size uintptr) bool
// only used on 32-bit
func int64div(int64, int64) int64
diff --git a/src/cmd/gc/select.c b/src/cmd/gc/select.c
index 7168e6b806..ed23e4318b 100644
--- a/src/cmd/gc/select.c
+++ b/src/cmd/gc/select.c
@@ -337,19 +337,23 @@ selecttype(int32 size)
sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("g")), typenod(ptrto(types[TUINT8]))));
sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("selectdone")), typenod(ptrto(types[TUINT8]))));
sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("link")), typenod(ptrto(types[TUINT8]))));
+ sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("prev")), typenod(ptrto(types[TUINT8]))));
sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("elem")), typenod(ptrto(types[TUINT8]))));
sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("releasetime")), typenod(types[TUINT64])));
+ sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("nrelease")), typenod(types[TINT32])));
+ sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("waitlink")), typenod(ptrto(types[TUINT8]))));
typecheck(&sudog, Etype);
sudog->type->noalg = 1;
sudog->type->local = 1;
scase = nod(OTSTRUCT, N, N);
- scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("sg")), sudog));
+ scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("elem")), typenod(ptrto(types[TUINT8]))));
scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("chan")), typenod(ptrto(types[TUINT8]))));
scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("pc")), typenod(types[TUINTPTR])));
scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("kind")), typenod(types[TUINT16])));
scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("so")), typenod(types[TUINT16])));
scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("receivedp")), typenod(ptrto(types[TUINT8]))));
+ scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("releasetime")), typenod(types[TUINT64])));
typecheck(&scase, Etype);
scase->type->noalg = 1;
scase->type->local = 1;
diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c
index 0195f3d629..d62d55e773 100644
--- a/src/cmd/gc/subr.c
+++ b/src/cmd/gc/subr.c
@@ -656,11 +656,15 @@ maptype(Type *key, Type *val)
{
Type *t;
Type *bad;
- int atype;
+ int atype, mtype;
if(key != nil) {
atype = algtype1(key, &bad);
- switch(bad == T ? key->etype : bad->etype) {
+ if(bad == T)
+ mtype = key->etype;
+ else
+ mtype = bad->etype;
+ switch(mtype) {
default:
if(atype == ANOEQ)
yyerror("invalid map key type %T", key);
@@ -2856,18 +2860,19 @@ genhash(Sym *sym, Type *t)
}
// Return node for
-// if p.field != q.field { *eq = false; return }
+// if p.field != q.field { return false }
static Node*
-eqfield(Node *p, Node *q, Node *field, Node *eq)
+eqfield(Node *p, Node *q, Node *field)
{
- Node *nif, *nx, *ny;
+ Node *nif, *nx, *ny, *r;
nx = nod(OXDOT, p, field);
ny = nod(OXDOT, q, field);
nif = nod(OIF, N, N);
nif->ntest = nod(ONE, nx, ny);
- nif->nbody = list(nif->nbody, nod(OAS, nod(OIND, eq, N), nodbool(0)));
- nif->nbody = list(nif->nbody, nod(ORETURN, N, N));
+ r = nod(ORETURN, N, N);
+ r->list = list(r->list, nodbool(0));
+ nif->nbody = list(nif->nbody, r);
return nif;
}
@@ -2896,11 +2901,11 @@ eqmemfunc(vlong size, Type *type)
}
// Return node for
-// if memequal(size, &p.field, &q.field, eq); !*eq { return }
+// if !memequal(&p.field, &q.field, size) { return false }
static Node*
-eqmem(Node *p, Node *q, Node *field, vlong size, Node *eq)
+eqmem(Node *p, Node *q, Node *field, vlong size)
{
- Node *nif, *nx, *ny, *call;
+ Node *nif, *nx, *ny, *call, *r;
nx = nod(OADDR, nod(OXDOT, p, field), N);
nx->etype = 1; // does not escape
@@ -2910,15 +2915,16 @@ eqmem(Node *p, Node *q, Node *field, vlong size, Node *eq)
typecheck(&ny, Erv);
call = nod(OCALL, eqmemfunc(size, nx->type->type), N);
- call->list = list(call->list, eq);
- call->list = list(call->list, nodintconst(size));
call->list = list(call->list, nx);
call->list = list(call->list, ny);
+ call->list = list(call->list, nodintconst(size));
nif = nod(OIF, N, N);
nif->ninit = list(nif->ninit, call);
- nif->ntest = nod(ONOT, nod(OIND, eq, N), N);
- nif->nbody = list(nif->nbody, nod(ORETURN, N, N));
+ nif->ntest = nod(ONOT, call, N);
+ r = nod(ORETURN, N, N);
+ r->list = list(r->list, nodbool(0));
+ nif->nbody = list(nif->nbody, r);
return nif;
}
@@ -2928,7 +2934,7 @@ eqmem(Node *p, Node *q, Node *field, vlong size, Node *eq)
void
geneq(Sym *sym, Type *t)
{
- Node *n, *fn, *np, *neq, *nq, *tfn, *nif, *ni, *nx, *ny, *nrange;
+ Node *n, *fn, *np, *nq, *tfn, *nif, *ni, *nx, *ny, *nrange, *r;
Type *t1, *first;
int old_safemode;
int64 size;
@@ -2941,24 +2947,23 @@ geneq(Sym *sym, Type *t)
dclcontext = PEXTERN;
markdcl();
- // func sym(eq *bool, s uintptr, p, q *T)
+ // func sym(p, q *T, s uintptr) bool
fn = nod(ODCLFUNC, N, N);
fn->nname = newname(sym);
fn->nname->class = PFUNC;
tfn = nod(OTFUNC, N, N);
fn->nname->ntype = tfn;
- n = nod(ODCLFIELD, newname(lookup("eq")), typenod(ptrto(types[TBOOL])));
- tfn->list = list(tfn->list, n);
- neq = n->left;
- n = nod(ODCLFIELD, newname(lookup("s")), typenod(types[TUINTPTR]));
- tfn->list = list(tfn->list, n);
n = nod(ODCLFIELD, newname(lookup("p")), typenod(ptrto(t)));
tfn->list = list(tfn->list, n);
np = n->left;
n = nod(ODCLFIELD, newname(lookup("q")), typenod(ptrto(t)));
tfn->list = list(tfn->list, n);
nq = n->left;
+ n = nod(ODCLFIELD, newname(lookup("s")), typenod(types[TUINTPTR]));
+ tfn->list = list(tfn->list, n);
+ n = nod(ODCLFIELD, N, typenod(types[TBOOL]));
+ tfn->rlist = list(tfn->rlist, n);
funchdr(fn);
@@ -2984,7 +2989,7 @@ geneq(Sym *sym, Type *t)
colasdefn(nrange->list, nrange);
ni = nrange->list->n;
- // if p[i] != q[i] { *eq = false; return }
+ // if p[i] != q[i] { return false }
nx = nod(OINDEX, np, ni);
nx->bounded = 1;
ny = nod(OINDEX, nq, ni);
@@ -2992,13 +2997,11 @@ geneq(Sym *sym, Type *t)
nif = nod(OIF, N, N);
nif->ntest = nod(ONE, nx, ny);
- nif->nbody = list(nif->nbody, nod(OAS, nod(OIND, neq, N), nodbool(0)));
- nif->nbody = list(nif->nbody, nod(ORETURN, N, N));
+ r = nod(ORETURN, N, N);
+ r->list = list(r->list, nodbool(0));
+ nif->nbody = list(nif->nbody, r);
nrange->nbody = list(nrange->nbody, nif);
fn->nbody = list(fn->nbody, nrange);
-
- // *eq = true;
- fn->nbody = list(fn->nbody, nod(OAS, nod(OIND, neq, N), nodbool(1)));
break;
case TSTRUCT:
@@ -3023,16 +3026,16 @@ geneq(Sym *sym, Type *t)
// cross-package unexported fields.
if(first != T) {
if(first->down == t1) {
- fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym), neq));
+ fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym)));
} else if(first->down->down == t1) {
- fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym), neq));
+ fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym)));
first = first->down;
if(!isblanksym(first->sym))
- fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym), neq));
+ fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym)));
} else {
// More than two fields: use memequal.
size = offend - first->width; // first->width is offset
- fn->nbody = list(fn->nbody, eqmem(np, nq, newname(first->sym), size, neq));
+ fn->nbody = list(fn->nbody, eqmem(np, nq, newname(first->sym), size));
}
first = T;
}
@@ -3042,14 +3045,17 @@ geneq(Sym *sym, Type *t)
continue;
// Check this field, which is not just memory.
- fn->nbody = list(fn->nbody, eqfield(np, nq, newname(t1->sym), neq));
+ fn->nbody = list(fn->nbody, eqfield(np, nq, newname(t1->sym)));
}
- // *eq = true;
- fn->nbody = list(fn->nbody, nod(OAS, nod(OIND, neq, N), nodbool(1)));
break;
}
+ // return true
+ r = nod(ORETURN, N, N);
+ r->list = list(r->list, nodbool(1));
+ fn->nbody = list(fn->nbody, r);
+
if(debug['r'])
dumplist("geneq body", fn->nbody);
@@ -3792,3 +3798,42 @@ checknil(Node *x, NodeList **init)
n->typecheck = 1;
*init = list(*init, n);
}
+
+/*
+ * Can this type be stored directly in an interface word?
+ */
+int
+isdirectiface(Type *t)
+{
+ // Setting IfacePointerOnly = 1 changes the
+ // interface representation so that the data word
+ // in an interface value must always be a pointer.
+ // Setting it to 0 uses the original representation,
+ // where the data word can hold a pointer or any
+ // non-pointer value no bigger than a pointer.
+ enum {
+ IfacePointerOnly = 1,
+ };
+
+ if(IfacePointerOnly) {
+ switch(t->etype) {
+ case TPTR32:
+ case TPTR64:
+ case TCHAN:
+ case TMAP:
+ case TFUNC:
+ case TUNSAFEPTR:
+ return 1;
+ case TARRAY:
+ // Array of 1 direct iface type can be direct.
+ return t->bound == 1 && isdirectiface(t->type);
+ case TSTRUCT:
+ // Struct with 1 field of direct iface type can be direct.
+ return t->type != T && t->type->down == T && isdirectiface(t->type->type);
+ }
+ return 0;
+ }
+
+ dowidth(t);
+ return t->width <= widthptr;
+}
diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c
index 92e9ad5215..746feb4d1b 100644
--- a/src/cmd/gc/typecheck.c
+++ b/src/cmd/gc/typecheck.c
@@ -525,19 +525,6 @@ reswitch:
op = n->etype;
goto arith;
- case OADDPTR:
- ok |= Erv;
- l = typecheck(&n->left, Erv);
- r = typecheck(&n->right, Erv);
- if(l->type == T || r->type == T)
- goto error;
- if(l->type->etype != tptr)
- fatal("bad OADDPTR left type %E for %N", l->type->etype, n->left);
- if(r->type->etype != TUINTPTR)
- fatal("bad OADDPTR right type %E for %N", r->type->etype, n->right);
- n->type = types[tptr];
- goto ret;
-
case OADD:
case OAND:
case OANDAND:
@@ -2965,7 +2952,7 @@ typecheckas2(Node *n)
if(l->defn == n)
l->type = r->type;
l = n->list->next->n;
- if(l->type != T)
+ if(l->type != T && l->type->etype != TBOOL)
checkassignto(types[TBOOL], l);
if(l->defn == n && l->ntype == N)
l->type = types[TBOOL];
diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c
index cf25a3eac7..365ece2671 100644
--- a/src/cmd/gc/walk.c
+++ b/src/cmd/gc/walk.c
@@ -543,11 +543,11 @@ walkexpr(Node **np, NodeList **init)
goto ret;
case OPANIC:
- n = mkcall("panic", T, init, n->left);
+ n = mkcall("gopanic", T, init, n->left);
goto ret;
case ORECOVER:
- n = mkcall("recover", n->type, init, nod(OADDR, nodfp, N));
+ n = mkcall("gorecover", n->type, init, nod(OADDR, nodfp, N));
goto ret;
case OLITERAL:
@@ -673,7 +673,7 @@ walkexpr(Node **np, NodeList **init)
n1 = nod(OADDR, n->list->n, N);
n1->etype = 1; // addr does not escape
fn = chanfn("chanrecv2", 2, r->left->type);
- r = mkcall1(fn, types[TBOOL], init, typename(r->left->type), r->left, n1);
+ r = mkcall1(fn, n->list->next->n->type, init, typename(r->left->type), r->left, n1);
n = nod(OAS, n->list->next->n, r);
typecheck(&n, Etop);
goto ret;
@@ -723,6 +723,12 @@ walkexpr(Node **np, NodeList **init)
var->typecheck = 1;
fn = mapfn(p, t);
r = mkcall1(fn, getoutargx(fn->type), init, typename(t), r->left, key);
+
+ // mapaccess2* returns a typed bool, but due to spec changes,
+ // the boolean result of i.(T) is now untyped so we make it the
+ // same type as the variable on the lhs.
+ if(!isblank(n->list->next->n))
+ r->type->type->down->type = n->list->next->n->type;
n->rlist = list1(r);
n->op = OAS2FUNC;
n->list->n = var;
@@ -770,6 +776,12 @@ walkexpr(Node **np, NodeList **init)
*p = '\0';
fn = syslook(buf, 1);
+
+ // runtime.assert(E|I)2TOK returns a typed bool, but due
+ // to spec changes, the boolean result of i.(T) is now untyped
+ // so we make it the same type as the variable on the lhs.
+ if(!isblank(n->list->next->n))
+ fn->type->type->down->type->type = n->list->next->n->type;
ll = list1(typename(r->type));
ll = list(ll, r->left);
argtype(fn, r->left->type);
@@ -822,9 +834,7 @@ walkexpr(Node **np, NodeList **init)
walkexpr(&n->left, init);
// Optimize convT2E as a two-word copy when T is uintptr-shaped.
- if(!isinter(n->left->type) && isnilinter(n->type) &&
- (n->left->type->width == widthptr) &&
- isint[simsimtype(n->left->type)]) {
+ if(isnilinter(n->type) && isdirectiface(n->left->type) && n->left->type->width == widthptr && isint[simsimtype(n->left->type)]) {
l = nod(OEFACE, typename(n->left->type), n->left);
l->type = n->type;
l->typecheck = n->typecheck;
@@ -872,8 +882,7 @@ walkexpr(Node **np, NodeList **init)
l->addable = 1;
ll = list(ll, l);
- if(n->left->type->width == widthptr &&
- isint[simsimtype(n->left->type)]) {
+ if(isdirectiface(n->left->type) && n->left->type->width == widthptr && isint[simsimtype(n->left->type)]) {
/* For pointer types, we can make a special form of optimization
*
* These statements are put onto the expression init list:
@@ -1828,9 +1837,12 @@ walkprint(Node *nn, NodeList **init, int defer)
t = types[TINT64];
}
} else {
- if(et == TUINT64)
- on = syslook("printuint", 0);
- else
+ if(et == TUINT64) {
+ if((t->sym->pkg == runtimepkg || compiling_runtime) && strcmp(t->sym->name, "hex") == 0)
+ on = syslook("printhex", 0);
+ else
+ on = syslook("printuint", 0);
+ } else
on = syslook("printint", 0);
}
} else if(isfloat[et]) {
@@ -2866,14 +2878,14 @@ sliceany(Node* n, NodeList **init)
lb = N;
}
- // dynamic checks convert all bounds to unsigned to save us the bound < 0 comparison
- // generate
- // if hb > bound || lb > hb { panicslice() }
+ // Checking src[lb:hb:cb] or src[lb:hb].
+ // if chk0 || chk1 || chk2 { panicslice() }
chk = N;
- chk0 = N;
- chk1 = N;
- chk2 = N;
+ chk0 = N; // cap(src) < cb
+ chk1 = N; // cb < hb for src[lb:hb:cb]; cap(src) < hb for src[lb:hb]
+ chk2 = N; // hb < lb
+ // All comparisons are unsigned to avoid testing < 0.
bt = types[simtype[TUINT]];
if(cb != N && cb->type->width > 4)
bt = types[TUINT64];
@@ -3013,10 +3025,10 @@ eqfor(Type *t)
n = newname(sym);
n->class = PFUNC;
ntype = nod(OTFUNC, N, N);
- ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(ptrto(types[TBOOL]))));
- ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(ptrto(t))));
ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(ptrto(t))));
+ ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
+ ntype->rlist = list(ntype->rlist, nod(ODCLFIELD, N, typenod(types[TBOOL])));
typecheck(&ntype, Etype);
n->type = ntype->type;
return n;
@@ -3037,10 +3049,9 @@ countfield(Type *t)
static void
walkcompare(Node **np, NodeList **init)
{
- Node *n, *l, *r, *fn, *call, *a, *li, *ri, *expr;
+ Node *n, *l, *r, *call, *a, *li, *ri, *expr;
int andor, i;
Type *t, *t1;
- static Node *tempbool;
n = *np;
@@ -3058,8 +3069,9 @@ walkcompare(Node **np, NodeList **init)
break;
}
- if(!islvalue(n->left) || !islvalue(n->right))
- goto hard;
+ if(!islvalue(n->left) || !islvalue(n->right)) {
+ fatal("arguments of comparison must be lvalues");
+ }
l = temp(ptrto(t));
a = nod(OAS, l, nod(OADDR, n->left, N));
@@ -3118,57 +3130,16 @@ walkcompare(Node **np, NodeList **init)
goto ret;
}
- // Chose not to inline, but still have addresses.
- // Call equality function directly.
- // The equality function requires a bool pointer for
- // storing its address, because it has to be callable
- // from C, and C can't access an ordinary Go return value.
- // To avoid creating many temporaries, cache one per function.
- if(tempbool == N || tempbool->curfn != curfn)
- tempbool = temp(types[TBOOL]);
-
+ // Chose not to inline. Call equality function directly.
call = nod(OCALL, eqfor(t), N);
- a = nod(OADDR, tempbool, N);
- a->etype = 1; // does not escape
- call->list = list(call->list, a);
- call->list = list(call->list, nodintconst(t->width));
call->list = list(call->list, l);
call->list = list(call->list, r);
- typecheck(&call, Etop);
- walkstmt(&call);
- *init = list(*init, call);
-
- // tempbool cannot be used directly as multiple comparison
- // expressions may exist in the same statement. Create another
- // temporary to hold the value (its address is not taken so it can
- // be optimized away).
- r = temp(types[TBOOL]);
- a = nod(OAS, r, tempbool);
- typecheck(&a, Etop);
- walkstmt(&a);
- *init = list(*init, a);
-
+ call->list = list(call->list, nodintconst(t->width));
+ r = call;
if(n->op != OEQ)
r = nod(ONOT, r, N);
goto ret;
-hard:
- // Cannot take address of one or both of the operands.
- // Instead, pass directly to runtime helper function.
- // Easier on the stack than passing the address
- // of temporary variables, because we are better at reusing
- // the argument space than temporary variable space.
- fn = syslook("equal", 1);
- l = n->left;
- r = n->right;
- argtype(fn, n->left->type);
- argtype(fn, n->left->type);
- r = mkcall1(fn, n->type, init, typename(n->left->type), l, r);
- if(n->op == ONE) {
- r = nod(ONOT, r, N);
- }
- goto ret;
-
ret:
typecheck(&r, Erv);
walkexpr(&r, init);
diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go
index fa9262c0f0..fd92ca2783 100644
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -885,7 +885,7 @@ func (b *builder) build(a *action) (err error) {
}
if len(gofiles) == 0 {
- return &build.NoGoError{a.p.Dir}
+ return &build.NoGoError{Dir: a.p.Dir}
}
// If we're doing coverage, preprocess the .go files and put them in the work directory
@@ -1613,8 +1613,10 @@ func (gcToolchain) gc(b *builder, p *Package, archive, obj string, importArgs []
}
func (gcToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error {
+ // Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files.
+ inc := filepath.Join(goroot, "pkg", fmt.Sprintf("%s_%s", goos, goarch))
sfile = mkAbs(p.Dir, sfile)
- return b.run(p.Dir, p.ImportPath, nil, tool(archChar+"a"), "-trimpath", b.work, "-I", obj, "-o", ofile, "-D", "GOOS_"+goos, "-D", "GOARCH_"+goarch, sfile)
+ return b.run(p.Dir, p.ImportPath, nil, tool(archChar+"a"), "-trimpath", b.work, "-I", obj, "-I", inc, "-o", ofile, "-D", "GOOS_"+goos, "-D", "GOARCH_"+goarch, sfile)
}
func (gcToolchain) pkgpath(basedir string, p *Package) string {
@@ -2312,7 +2314,23 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles, gxxfiles, mfiles
nonGccObjs = append(nonGccObjs, f)
}
}
- if err := b.gccld(p, ofile, stringList(bareLDFLAGS, "-Wl,-r", "-nostdlib", staticLibs), gccObjs); err != nil {
+ ldflags := stringList(bareLDFLAGS, "-Wl,-r", "-nostdlib", staticLibs)
+
+ // Some systems, such as Ubuntu, always add --build-id to
+ // every link, but we don't want a build ID since we are
+ // producing an object file. On some of those system a plain
+ // -r (not -Wl,-r) will turn off --build-id, but clang 3.0
+ // doesn't support a plain -r. I don't know how to turn off
+ // --build-id when using clang other than passing a trailing
+ // --build-id=none. So that is what we do, but only on
+ // systems likely to support it, which is to say, systems that
+ // normally use gold or the GNU linker.
+ switch goos {
+ case "android", "dragonfly", "linux", "netbsd":
+ ldflags = append(ldflags, "-Wl,--build-id=none")
+ }
+
+ if err := b.gccld(p, ofile, ldflags, gccObjs); err != nil {
return nil, nil, err
}
diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go
index 4778048b52..0d4e263891 100644
--- a/src/cmd/go/doc.go
+++ b/src/cmd/go/doc.go
@@ -19,6 +19,7 @@ The commands are:
env print Go environment information
fix run go tool fix on packages
fmt run gofmt on package sources
+ generate generate Go files by processing source
get download and install packages and dependencies
install compile and install packages and dependencies
list list packages
@@ -219,6 +220,98 @@ To run gofmt with specific options, run gofmt itself.
See also: go fix, go vet.
+Generate Go files by processing source
+
+Usage:
+
+ go generate [-run regexp] [file.go... | packages]
+
+Generate runs commands described by directives within existing
+files. Those commands can run any process but the intent is to
+create or update Go source files, for instance by running yacc.
+
+Go generate is never run automatically by go build, go get, go test,
+and so on. It must be run explicitly.
+
+Directives are written as a whole-line comment of the form
+
+ //go:generate command argument...
+
+(note: no space in "//go") where command is the generator to be
+run, corresponding to an executable file that can be run locally.
+It must either be in the shell path (gofmt), a fully qualified path
+(/usr/you/bin/mytool), or a command alias, described below.
+
+The arguments are space-separated tokens or double-quoted strings
+passed to the generator as individual arguments when it is run.
+
+Quoted strings use Go syntax and are evaluated before execution; a
+quoted string appears a single argument to the generator.
+
+Go generate sets several variables when it runs the generator:
+
+ $GOARCH
+ The execution architecture (arm, amd64, etc.)
+ $GOOS
+ The execution operating system (linux, windows, etc.)
+ $GOFILE
+ The base name of the file.
+ $GOPACKAGE
+ The name of the package of the file containing the directive.
+
+Other than variable substition and quoted-string evaluation, no
+special processing such as "globbing" is performed on the command
+line.
+
+As a last step before running the command, any invocations of any
+environment variables with alphanumeric names, such as $GOFILE or
+$HOME, are expanded throughout the command line. The syntax for
+variable expansion is $NAME on all operating systems. Due to the
+order of evaluation, variables are expanded even inside quoted
+strings. If the variable NAME is not set, $NAME expands to the
+empty string.
+
+A directive of the form,
+
+ //go:generate -command xxx args...
+
+specifies, for the remainder of this source file only, that the
+string xxx represents the command identified by the arguments. This
+can be used to create aliases or to handle multiword generators.
+For example,
+
+ //go:generate -command yacc go tool yacc
+
+specifies that the command "yacc" represents the generator
+"go tool yacc".
+
+Generate processes packages in the order given on the command line,
+one at a time. If the command line lists .go files, they are treated
+as a single package. Within a package, generate processes the
+source files in a package in file name order, one at a time. Within
+a source file, generate runs generators in the order they appear
+in the file, one at a time.
+
+If any generator returns an error exit status, "go generate" skips
+all further processing for that package.
+
+The generator is run in the package's source directory.
+
+Go generate accepts one specific flag:
+
+ -run=""
+ if non-empty, specifies a regular expression to
+ select directives whose command matches the expression.
+
+It also accepts the standard build flags -v, -n, and -x.
+The -v flag prints the names of packages and files as they are
+processed.
+The -n flag prints commands that would be executed.
+The -x flag prints commands as they are executed.
+
+For more about specifying packages, see 'go help packages'.
+
+
Download and install packages and dependencies
Usage:
@@ -750,10 +843,10 @@ will result in the following request(s):
If that page contains the meta tag
- <meta name="go-import" content="example.org git https://code.example/r/p/exproj">
+ <meta name="go-import" content="example.org git https://code.org/r/p/exproj">
the go tool will verify that https://example.org/?go-get=1 contains the
-same meta tag and then git clone https://code.example/r/p/exproj into
+same meta tag and then git clone https://code.org/r/p/exproj into
GOPATH/src/example.org.
New downloaded packages are written to the first directory
diff --git a/src/cmd/go/generate.go b/src/cmd/go/generate.go
new file mode 100644
index 0000000000..167758207e
--- /dev/null
+++ b/src/cmd/go/generate.go
@@ -0,0 +1,355 @@
+// 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 main
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "strconv"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+var cmdGenerate = &Command{
+ Run: runGenerate,
+ UsageLine: "generate [-run regexp] [file.go... | packages]",
+ Short: "generate Go files by processing source",
+ Long: `
+Generate runs commands described by directives within existing
+files. Those commands can run any process but the intent is to
+create or update Go source files, for instance by running yacc.
+
+Go generate is never run automatically by go build, go get, go test,
+and so on. It must be run explicitly.
+
+Directives are written as a whole-line comment of the form
+
+ //go:generate command argument...
+
+(note: no space in "//go") where command is the generator to be
+run, corresponding to an executable file that can be run locally.
+It must either be in the shell path (gofmt), a fully qualified path
+(/usr/you/bin/mytool), or a command alias, described below.
+
+The arguments are space-separated tokens or double-quoted strings
+passed to the generator as individual arguments when it is run.
+
+Quoted strings use Go syntax and are evaluated before execution; a
+quoted string appears a single argument to the generator.
+
+Go generate sets several variables when it runs the generator:
+
+ $GOARCH
+ The execution architecture (arm, amd64, etc.)
+ $GOOS
+ The execution operating system (linux, windows, etc.)
+ $GOFILE
+ The base name of the file.
+ $GOPACKAGE
+ The name of the package of the file containing the directive.
+
+Other than variable substition and quoted-string evaluation, no
+special processing such as "globbing" is performed on the command
+line.
+
+As a last step before running the command, any invocations of any
+environment variables with alphanumeric names, such as $GOFILE or
+$HOME, are expanded throughout the command line. The syntax for
+variable expansion is $NAME on all operating systems. Due to the
+order of evaluation, variables are expanded even inside quoted
+strings. If the variable NAME is not set, $NAME expands to the
+empty string.
+
+A directive of the form,
+
+ //go:generate -command xxx args...
+
+specifies, for the remainder of this source file only, that the
+string xxx represents the command identified by the arguments. This
+can be used to create aliases or to handle multiword generators.
+For example,
+
+ //go:generate -command yacc go tool yacc
+
+specifies that the command "yacc" represents the generator
+"go tool yacc".
+
+Generate processes packages in the order given on the command line,
+one at a time. If the command line lists .go files, they are treated
+as a single package. Within a package, generate processes the
+source files in a package in file name order, one at a time. Within
+a source file, generate runs generators in the order they appear
+in the file, one at a time.
+
+If any generator returns an error exit status, "go generate" skips
+all further processing for that package.
+
+The generator is run in the package's source directory.
+
+Go generate accepts one specific flag:
+
+ -run=""
+ if non-empty, specifies a regular expression to
+ select directives whose command matches the expression.
+
+It also accepts the standard build flags -v, -n, and -x.
+The -v flag prints the names of packages and files as they are
+processed.
+The -n flag prints commands that would be executed.
+The -x flag prints commands as they are executed.
+
+For more about specifying packages, see 'go help packages'.
+ `,
+}
+
+var generateRunFlag string // generate -run flag
+
+func init() {
+ addBuildFlags(cmdGenerate)
+ cmdGenerate.Flag.StringVar(&generateRunFlag, "run", "", "")
+}
+
+func runGenerate(cmd *Command, args []string) {
+ // Even if the arguments are .go files, this loop suffices.
+ for _, pkg := range packages(args) {
+ for _, file := range pkg.gofiles {
+ if !generate(pkg.Name, file) {
+ break
+ }
+ }
+ }
+}
+
+// generate runs the generation directives for a single file.
+func generate(pkg, absFile string) bool {
+ fd, err := os.Open(absFile)
+ if err != nil {
+ log.Fatalf("generate: %s", err)
+ }
+ defer fd.Close()
+ g := &Generator{
+ r: fd,
+ path: absFile,
+ pkg: pkg,
+ commands: make(map[string][]string),
+ }
+ return g.run()
+}
+
+// A Generator represents the state of a single Go source file
+// being scanned for generator commands.
+type Generator struct {
+ r io.Reader
+ path string // full rooted path name.
+ dir string // full rooted directory of file.
+ file string // base name of file.
+ pkg string
+ commands map[string][]string
+ lineNum int
+}
+
+// run runs the generators in the current file.
+func (g *Generator) run() (ok bool) {
+ // Processing below here calls g.errorf on failure, which does panic(stop).
+ // If we encounter an error, we abort the package.
+ defer func() {
+ e := recover()
+ if e != nil {
+ ok = false
+ if e != stop {
+ panic(e)
+ }
+ }
+ }()
+ g.dir, g.file = filepath.Split(g.path)
+ g.dir = filepath.Clean(g.dir) // No final separator please.
+ if buildV {
+ fmt.Fprintf(os.Stderr, "%s\n", shortPath(g.path))
+ }
+
+ s := bufio.NewScanner(g.r)
+ for s.Scan() {
+ g.lineNum++
+ if !bytes.HasPrefix(s.Bytes(), []byte("//go:generate ")) && !bytes.HasPrefix(s.Bytes(), []byte("//go:generate\t")) {
+ continue
+ }
+ words := g.split(s.Text())
+ if len(words) == 0 {
+ g.errorf("no arguments to directive")
+ }
+ if words[0] == "-command" {
+ g.setShorthand(words)
+ continue
+ }
+ // Run the command line.
+ if buildN || buildX {
+ fmt.Fprintf(os.Stderr, "%s\n", strings.Join(words, " "))
+ }
+ if buildN {
+ continue
+ }
+ g.exec(words)
+ }
+ if s.Err() != nil {
+ g.errorf("error reading %s: %s", shortPath(g.path), s.Err())
+ }
+ return true
+}
+
+// split breaks the line into words, evaluating quoted
+// strings and evaluating environment variables.
+// The initial //go:generate element is dropped.
+func (g *Generator) split(line string) []string {
+ // Parse line, obeying quoted strings.
+ var words []string
+ line = line[len("//go:generate "):]
+ // One (possibly quoted) word per iteration.
+Words:
+ for {
+ line = strings.TrimLeft(line, " \t")
+ if len(line) == 0 {
+ break
+ }
+ if line[0] == '"' {
+ for i := 1; i < len(line); i++ {
+ c := line[i] // Only looking for ASCII so this is OK.
+ switch c {
+ case '\\':
+ if i+1 == len(line) {
+ g.errorf("bad backslash")
+ }
+ i++ // Absorb next byte (If it's a multibyte we'll get an error in Unquote).
+ case '"':
+ word, err := strconv.Unquote(line[0 : i+1])
+ if err != nil {
+ g.errorf("bad quoted string")
+ }
+ words = append(words, word)
+ line = line[i+1:]
+ // Check the next character is space or end of line.
+ if len(line) > 0 && line[0] != ' ' && line[0] != '\t' {
+ g.errorf("expect space after quoted argument")
+ }
+ continue Words
+ }
+ }
+ g.errorf("mismatched quoted string")
+ }
+ i := strings.IndexAny(line, " \t")
+ if i < 0 {
+ i = len(line)
+ }
+ words = append(words, line[0:i])
+ line = line[i:]
+ }
+ // Substitute command if required.
+ if len(words) > 0 && g.commands[words[0]] != nil {
+ // Replace 0th word by command substitution.
+ words = append(g.commands[words[0]], words[1:]...)
+ }
+ // Substitute environment variables.
+ for i, word := range words {
+ words[i] = g.expandEnv(word)
+ }
+ return words
+}
+
+var stop = fmt.Errorf("error in generation")
+
+// errorf logs an error message prefixed with the file and line number.
+// It then exits the program because generation stops at the first error.
+func (g *Generator) errorf(format string, args ...interface{}) {
+ fmt.Fprintf(os.Stderr, "%s:%d: %s\n", shortPath(g.path), g.lineNum,
+ fmt.Sprintf(format, args...))
+ panic(stop)
+}
+
+// expandEnv expands any $XXX invocations in word.
+func (g *Generator) expandEnv(word string) string {
+ if !strings.ContainsRune(word, '$') {
+ return word
+ }
+ var buf bytes.Buffer
+ var w int
+ var r rune
+ for i := 0; i < len(word); i += w {
+ r, w = utf8.DecodeRuneInString(word[i:])
+ if r != '$' {
+ buf.WriteRune(r)
+ continue
+ }
+ w += g.identLength(word[i+w:])
+ envVar := word[i+1 : i+w]
+ var sub string
+ switch envVar {
+ case "GOARCH":
+ sub = runtime.GOARCH
+ case "GOOS":
+ sub = runtime.GOOS
+ case "GOFILE":
+ sub = g.file
+ case "GOPACKAGE":
+ sub = g.pkg
+ default:
+ sub = os.Getenv(envVar)
+ }
+ buf.WriteString(sub)
+ }
+ return buf.String()
+}
+
+// identLength returns the length of the identifier beginning the string.
+func (g *Generator) identLength(word string) int {
+ for i, r := range word {
+ if r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r) {
+ continue
+ }
+ return i
+ }
+ return len(word)
+}
+
+// setShorthand installs a new shorthand as defined by a -command directive.
+func (g *Generator) setShorthand(words []string) {
+ // Create command shorthand.
+ if len(words) == 1 {
+ g.errorf("no command specified for -command")
+ }
+ command := words[1]
+ if g.commands[command] != nil {
+ g.errorf("command %q defined multiply defined", command)
+ }
+ g.commands[command] = words[2:len(words):len(words)] // force later append to make copy
+}
+
+// exec runs the command specified by the argument. The first word is
+// the command name itself.
+func (g *Generator) exec(words []string) {
+ cmd := exec.Command(words[0], words[1:]...)
+ // Standard in and out of generator should be the usual.
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ // Run the command in the package directory.
+ cmd.Dir = g.dir
+ env := []string{
+ "GOARCH=" + runtime.GOARCH,
+ "GOOS=" + runtime.GOOS,
+ "GOFILE=" + g.file,
+ "GOPACKAGE=" + g.pkg,
+ }
+ cmd.Env = mergeEnvLists(env, os.Environ())
+ err := cmd.Run()
+ if err != nil {
+ g.errorf("running %q: %s", words[0], err)
+ }
+}
diff --git a/src/cmd/go/generate_test.go b/src/cmd/go/generate_test.go
new file mode 100644
index 0000000000..93c0ae66e9
--- /dev/null
+++ b/src/cmd/go/generate_test.go
@@ -0,0 +1,48 @@
+// 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 main
+
+import (
+ "reflect"
+ "runtime"
+ "testing"
+)
+
+type splitTest struct {
+ in string
+ out []string
+}
+
+var splitTests = []splitTest{
+ {"", nil},
+ {"x", []string{"x"}},
+ {" a b\tc ", []string{"a", "b", "c"}},
+ {` " a " `, []string{" a "}},
+ {"$GOARCH", []string{runtime.GOARCH}},
+ {"$GOOS", []string{runtime.GOOS}},
+ {"$GOFILE", []string{"proc.go"}},
+ {"$GOPACKAGE", []string{"sys"}},
+ {"a $XXNOTDEFINEDXX b", []string{"a", "", "b"}},
+ {"/$XXNOTDEFINED/", []string{"//"}},
+ {"yacc -o $GOARCH/yacc_$GOFILE", []string{"go", "tool", "yacc", "-o", runtime.GOARCH + "/yacc_proc.go"}},
+}
+
+func TestGenerateCommandParse(t *testing.T) {
+ g := &Generator{
+ r: nil, // Unused here.
+ path: "/usr/ken/sys/proc.go",
+ dir: "/usr/ken/sys",
+ file: "proc.go",
+ pkg: "sys",
+ commands: make(map[string][]string),
+ }
+ g.setShorthand([]string{"-command", "yacc", "go", "tool", "yacc"})
+ for _, test := range splitTests {
+ got := g.split("//go:generate " + test.in)
+ if !reflect.DeepEqual(got, test.out) {
+ t.Errorf("split(%q): got %q expected %q", test.in, got, test.out)
+ }
+ }
+}
diff --git a/src/cmd/go/get.go b/src/cmd/go/get.go
index e708fcf779..a34286f540 100644
--- a/src/cmd/go/get.go
+++ b/src/cmd/go/get.go
@@ -151,7 +151,9 @@ func download(arg string, stk *importStack, getTestDeps bool) {
}
// Only process each package once.
- if downloadCache[arg] {
+ // (Unless we're fetching test dependencies for this package,
+ // in which case we want to process it again.)
+ if downloadCache[arg] && !getTestDeps {
return
}
downloadCache[arg] = true
diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go
index 5b1194aaa3..eb69606def 100644
--- a/src/cmd/go/main.go
+++ b/src/cmd/go/main.go
@@ -79,6 +79,7 @@ var commands = []*Command{
cmdEnv,
cmdFix,
cmdFmt,
+ cmdGenerate,
cmdGet,
cmdInstall,
cmdList,
diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go
index d45df265b9..1af33f037a 100644
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -244,6 +244,9 @@ func loadImport(path string, srcDir string, stk *importStack, importPos []token.
importPath = dirToImportPath(filepath.Join(srcDir, path))
}
if p := packageCache[importPath]; p != nil {
+ if perr := disallowInternal(srcDir, p, stk); perr != p {
+ return perr
+ }
return reusePackage(p, stk)
}
@@ -258,11 +261,14 @@ func loadImport(path string, srcDir string, stk *importStack, importPos []token.
//
// TODO: After Go 1, decide when to pass build.AllowBinary here.
// See issue 3268 for mistakes to avoid.
- bp, err := buildContext.Import(path, srcDir, 0)
+ bp, err := buildContext.Import(path, srcDir, build.ImportComment)
bp.ImportPath = importPath
if gobin != "" {
bp.BinDir = gobin
}
+ if err == nil && !isLocal && bp.ImportComment != "" && bp.ImportComment != path {
+ err = fmt.Errorf("code in directory %s expects import %q", bp.Dir, bp.ImportComment)
+ }
p.load(stk, bp, err)
if p.Error != nil && len(importPos) > 0 {
pos := importPos[0]
@@ -270,6 +276,10 @@ func loadImport(path string, srcDir string, stk *importStack, importPos []token.
p.Error.Pos = pos.String()
}
+ if perr := disallowInternal(srcDir, p, stk); perr != p {
+ return perr
+ }
+
return p
}
@@ -298,6 +308,75 @@ func reusePackage(p *Package, stk *importStack) *Package {
return p
}
+// disallowInternal checks that srcDir is allowed to import p.
+// If the import is allowed, disallowInternal returns the original package p.
+// If not, it returns a new package containing just an appropriate error.
+func disallowInternal(srcDir string, p *Package, stk *importStack) *Package {
+ // golang.org/s/go14internal:
+ // An import of a path containing the element “internal”
+ // is disallowed if the importing code is outside the tree
+ // rooted at the parent of the “internal” directory.
+ //
+ // ... For Go 1.4, we will implement the rule first for $GOROOT, but not $GOPATH.
+
+ // Only applies to $GOROOT.
+ if !p.Standard {
+ return p
+ }
+
+ // The stack includes p.ImportPath.
+ // If that's the only thing on the stack, we started
+ // with a name given on the command line, not an
+ // import. Anything listed on the command line is fine.
+ if len(*stk) == 1 {
+ return p
+ }
+
+ // Check for "internal" element: four cases depending on begin of string and/or end of string.
+ i, ok := findInternal(p.ImportPath)
+ if !ok {
+ return p
+ }
+
+ // Internal is present.
+ // Map import path back to directory corresponding to parent of internal.
+ if i > 0 {
+ i-- // rewind over slash in ".../internal"
+ }
+ parent := p.Dir[:i+len(p.Dir)-len(p.ImportPath)]
+ if hasPathPrefix(filepath.ToSlash(srcDir), filepath.ToSlash(parent)) {
+ return p
+ }
+
+ // Internal is present, and srcDir is outside parent's tree. Not allowed.
+ perr := *p
+ perr.Error = &PackageError{
+ ImportStack: stk.copy(),
+ Err: "use of internal package not allowed",
+ }
+ perr.Incomplete = true
+ return &perr
+}
+
+// findInternal looks for the final "internal" path element in the given import path.
+// 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.
+ // The order matters: we must return the index of the final element,
+ // because the final one produces the most restrictive requirement
+ // on the importer.
+ switch {
+ case strings.HasSuffix(path, "/internal"):
+ return len(path) - len("internal"), true
+ case strings.Contains(path, "/internal/"):
+ return strings.LastIndex(path, "/internal/") + 1, true
+ case path == "internal", strings.HasPrefix(path, "internal/"):
+ return 0, true
+ }
+ return 0, false
+}
+
type targetDir int
const (
@@ -482,7 +561,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
// Build list of imported packages and full dependency list.
imports := make([]*Package, 0, len(p.Imports))
- deps := make(map[string]bool)
+ deps := make(map[string]*Package)
for i, path := range importPaths {
if path == "C" {
continue
@@ -502,10 +581,10 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
path = p1.ImportPath
importPaths[i] = path
}
- deps[path] = true
+ deps[path] = p1
imports = append(imports, p1)
- for _, dep := range p1.Deps {
- deps[dep] = true
+ for _, dep := range p1.deps {
+ deps[dep.ImportPath] = dep
}
if p1.Incomplete {
p.Incomplete = true
@@ -519,7 +598,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
}
sort.Strings(p.Deps)
for _, dep := range p.Deps {
- p1 := packageCache[dep]
+ p1 := deps[dep]
if p1 == nil {
panic("impossible: missing entry in package cache for " + dep + " imported by " + p.ImportPath)
}
diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash
index c62f629405..24640e2723 100755
--- a/src/cmd/go/test.bash
+++ b/src/cmd/go/test.bash
@@ -105,6 +105,58 @@ cp -R testdata/local "testdata/$bad"
testlocal "$bad" 'with bad characters in path'
rm -rf "testdata/$bad"
+TEST 'internal packages in $GOROOT are respected'
+if ./testgo build -v ./testdata/testinternal >testdata/std.out 2>&1; then
+ echo "go build ./testdata/testinternal succeeded incorrectly"
+ ok=false
+elif ! grep 'use of internal package not allowed' testdata/std.out >/dev/null; then
+ echo "wrong error message for testdata/testinternal"
+ cat std.out
+ ok=false
+fi
+
+TEST 'internal packages outside $GOROOT are not respected'
+if ! ./testgo build -v ./testdata/testinternal2; then
+ echo "go build ./testdata/testinternal2 failed"
+ ok=false
+fi
+
+export GOPATH=$(pwd)/testdata/importcom
+TEST 'import comment - match'
+if ! ./testgo build ./testdata/importcom/works.go; then
+ echo 'go build ./testdata/importcom/works.go failed'
+ ok=false
+fi
+TEST 'import comment - mismatch'
+if ./testgo build ./testdata/importcom/wrongplace.go 2>testdata/err; then
+ echo 'go build ./testdata/importcom/wrongplace.go suceeded'
+ ok=false
+elif ! grep 'wrongplace expects import "my/x"' testdata/err >/dev/null; then
+ echo 'go build did not mention incorrect import:'
+ cat testdata/err
+ ok=false
+fi
+TEST 'import comment - syntax error'
+if ./testgo build ./testdata/importcom/bad.go 2>testdata/err; then
+ echo 'go build ./testdata/importcom/bad.go suceeded'
+ ok=false
+elif ! grep 'cannot parse import comment' testdata/err >/dev/null; then
+ echo 'go build did not mention syntax error:'
+ cat testdata/err
+ ok=false
+fi
+TEST 'import comment - conflict'
+if ./testgo build ./testdata/importcom/conflict.go 2>testdata/err; then
+ echo 'go build ./testdata/importcom/conflict.go suceeded'
+ ok=false
+elif ! grep 'found import comments' testdata/err >/dev/null; then
+ echo 'go build did not mention comment conflict:'
+ cat testdata/err
+ ok=false
+fi
+rm -f ./testdata/err
+unset GOPATH
+
TEST error message for syntax error in test go file says FAIL
export GOPATH=$(pwd)/testdata
if ./testgo test syntaxerror 2>testdata/err; then
@@ -527,6 +579,17 @@ TEST go get cover
unset GOPATH
rm -rf $d
+TEST go get -t "code.google.com/p/go-get-issue-8181/{a,b}"
+d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX)
+export GOPATH=$d
+if ./testgo get -t code.google.com/p/go-get-issue-8181/{a,b}; then
+ ./testgo list ... | grep go.tools/godoc > /dev/null || ok=false
+else
+ ok=false
+fi
+unset GOPATH
+rm -rf $d
+
TEST shadowing logic
export GOPATH=$(pwd)/testdata/shadow/root1:$(pwd)/testdata/shadow/root2
@@ -815,6 +878,33 @@ elif ! grep 'File with non-runnable example was built.' testdata/std.out > /dev/
ok=false
fi
+TEST 'go generate handles simple command'
+if ! ./testgo generate ./testdata/generate/test1.go > testdata/std.out; then
+ echo "go test ./testdata/generate/test1.go failed to run"
+ ok=false
+elif ! grep 'Success' testdata/std.out > /dev/null; then
+ echo "go test ./testdata/generate/test1.go generated wrong output"
+ ok=false
+fi
+
+TEST 'go generate handles command alias'
+if ! ./testgo generate ./testdata/generate/test2.go > testdata/std.out; then
+ echo "go test ./testdata/generate/test2.go failed to run"
+ ok=false
+elif ! grep 'Now is the time for all good men' testdata/std.out > /dev/null; then
+ echo "go test ./testdata/generate/test2.go generated wrong output"
+ ok=false
+fi
+
+TEST 'go generate variable substitution'
+if ! ./testgo generate ./testdata/generate/test3.go > testdata/std.out; then
+ echo "go test ./testdata/generate/test3.go failed to run"
+ ok=false
+elif ! grep "$GOARCH test3.go p xyzp/test3.go/123" testdata/std.out > /dev/null; then
+ echo "go test ./testdata/generate/test3.go generated wrong output"
+ ok=false
+fi
+
# clean up
if $started; then stop; fi
rm -rf testdata/bin testdata/bin1
diff --git a/src/cmd/go/testdata/generate/test1.go b/src/cmd/go/testdata/generate/test1.go
new file mode 100644
index 0000000000..1f05734f04
--- /dev/null
+++ b/src/cmd/go/testdata/generate/test1.go
@@ -0,0 +1,13 @@
+// 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.
+
+// Simple test for go generate.
+
+// We include a build tag that go generate should ignore.
+
+// +build ignore
+
+//go:generate echo Success
+
+package p
diff --git a/src/cmd/go/testdata/generate/test2.go b/src/cmd/go/testdata/generate/test2.go
new file mode 100644
index 0000000000..ef1a3d9515
--- /dev/null
+++ b/src/cmd/go/testdata/generate/test2.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.
+
+// Test that go generate handles command aliases.
+
+//go:generate -command run echo Now is the time
+//go:generate run for all good men
+
+package p
diff --git a/src/cmd/go/testdata/generate/test3.go b/src/cmd/go/testdata/generate/test3.go
new file mode 100644
index 0000000000..41ffb7ea87
--- /dev/null
+++ b/src/cmd/go/testdata/generate/test3.go
@@ -0,0 +1,9 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test go generate variable substitution.
+
+//go:generate echo $GOARCH $GOFILE $GOPACKAGE xyz$GOPACKAGE/$GOFILE/123
+
+package p
diff --git a/src/cmd/go/testdata/importcom/bad.go b/src/cmd/go/testdata/importcom/bad.go
new file mode 100644
index 0000000000..e104c2e992
--- /dev/null
+++ b/src/cmd/go/testdata/importcom/bad.go
@@ -0,0 +1,3 @@
+package p
+
+import "bad"
diff --git a/src/cmd/go/testdata/importcom/conflict.go b/src/cmd/go/testdata/importcom/conflict.go
new file mode 100644
index 0000000000..995556c511
--- /dev/null
+++ b/src/cmd/go/testdata/importcom/conflict.go
@@ -0,0 +1,3 @@
+package p
+
+import "conflict"
diff --git a/src/cmd/go/testdata/importcom/src/bad/bad.go b/src/cmd/go/testdata/importcom/src/bad/bad.go
new file mode 100644
index 0000000000..bc51fd3fde
--- /dev/null
+++ b/src/cmd/go/testdata/importcom/src/bad/bad.go
@@ -0,0 +1 @@
+package bad // import
diff --git a/src/cmd/go/testdata/importcom/src/conflict/a.go b/src/cmd/go/testdata/importcom/src/conflict/a.go
new file mode 100644
index 0000000000..2d67703511
--- /dev/null
+++ b/src/cmd/go/testdata/importcom/src/conflict/a.go
@@ -0,0 +1 @@
+package conflict // import "a"
diff --git a/src/cmd/go/testdata/importcom/src/conflict/b.go b/src/cmd/go/testdata/importcom/src/conflict/b.go
new file mode 100644
index 0000000000..8fcfb3c8bd
--- /dev/null
+++ b/src/cmd/go/testdata/importcom/src/conflict/b.go
@@ -0,0 +1 @@
+package conflict /* import "b" */
diff --git a/src/cmd/go/testdata/importcom/src/works/x/x.go b/src/cmd/go/testdata/importcom/src/works/x/x.go
new file mode 100644
index 0000000000..044c6eca80
--- /dev/null
+++ b/src/cmd/go/testdata/importcom/src/works/x/x.go
@@ -0,0 +1 @@
+package x // import "works/x"
diff --git a/src/cmd/go/testdata/importcom/src/works/x/x1.go b/src/cmd/go/testdata/importcom/src/works/x/x1.go
new file mode 100644
index 0000000000..2449b29df5
--- /dev/null
+++ b/src/cmd/go/testdata/importcom/src/works/x/x1.go
@@ -0,0 +1 @@
+package x // important! not an import comment
diff --git a/src/cmd/go/testdata/importcom/src/wrongplace/x.go b/src/cmd/go/testdata/importcom/src/wrongplace/x.go
new file mode 100644
index 0000000000..b89849da78
--- /dev/null
+++ b/src/cmd/go/testdata/importcom/src/wrongplace/x.go
@@ -0,0 +1 @@
+package x // import "my/x"
diff --git a/src/cmd/go/testdata/importcom/works.go b/src/cmd/go/testdata/importcom/works.go
new file mode 100644
index 0000000000..31b55d08a3
--- /dev/null
+++ b/src/cmd/go/testdata/importcom/works.go
@@ -0,0 +1,3 @@
+package p
+
+import _ "works/x"
diff --git a/src/cmd/go/testdata/importcom/wrongplace.go b/src/cmd/go/testdata/importcom/wrongplace.go
new file mode 100644
index 0000000000..e2535e01ae
--- /dev/null
+++ b/src/cmd/go/testdata/importcom/wrongplace.go
@@ -0,0 +1,3 @@
+package p
+
+import "wrongplace"
diff --git a/src/cmd/go/testdata/testinternal/p.go b/src/cmd/go/testdata/testinternal/p.go
new file mode 100644
index 0000000000..e3558a53b2
--- /dev/null
+++ b/src/cmd/go/testdata/testinternal/p.go
@@ -0,0 +1,3 @@
+package p
+
+import _ "net/http/internal"
diff --git a/src/cmd/go/testdata/testinternal2/p.go b/src/cmd/go/testdata/testinternal2/p.go
new file mode 100644
index 0000000000..c594f5c5e9
--- /dev/null
+++ b/src/cmd/go/testdata/testinternal2/p.go
@@ -0,0 +1,3 @@
+package p
+
+import _ "./x/y/z/internal/w"
diff --git a/src/cmd/go/testdata/testinternal2/x/y/z/internal/w/w.go b/src/cmd/go/testdata/testinternal2/x/y/z/internal/w/w.go
new file mode 100644
index 0000000000..a796c0b5f4
--- /dev/null
+++ b/src/cmd/go/testdata/testinternal2/x/y/z/internal/w/w.go
@@ -0,0 +1 @@
+package w
diff --git a/src/cmd/go/vcs_test.go b/src/cmd/go/vcs_test.go
index f9bf75fef1..14d681ba6a 100644
--- a/src/cmd/go/vcs_test.go
+++ b/src/cmd/go/vcs_test.go
@@ -12,6 +12,9 @@ import (
// Test that RepoRootForImportPath creates the correct RepoRoot for a given importPath.
// TODO(cmang): Add tests for SVN and BZR.
func TestRepoRootForImportPath(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping test to avoid external network")
+ }
switch runtime.GOOS {
case "nacl", "android":
t.Skipf("no networking available on %s", runtime.GOOS)
@@ -106,7 +109,7 @@ func TestRepoRootForImportPath(t *testing.T) {
if want == nil {
if err == nil {
- t.Errorf("RepoRootForImport(%q): Error expected but not received")
+ t.Errorf("RepoRootForImport(%q): Error expected but not received", test.path)
}
continue
}
diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go
index 576cae5228..f322a2b0a0 100644
--- a/src/cmd/gofmt/gofmt.go
+++ b/src/cmd/gofmt/gofmt.go
@@ -122,7 +122,7 @@ func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error
fmt.Fprintln(out, filename)
}
if *write {
- err = ioutil.WriteFile(filename, res, 0)
+ err = ioutil.WriteFile(filename, res, 0644)
if err != nil {
return err
}
@@ -186,6 +186,11 @@ func gofmtMain() {
initRewrite()
if flag.NArg() == 0 {
+ if *write {
+ fmt.Fprintln(os.Stderr, "error: cannot use -w with standard input")
+ exitCode = 2
+ return
+ }
if err := processFile("<standard input>", os.Stdin, os.Stdout, true); err != nil {
report(err)
}
@@ -277,14 +282,14 @@ func parse(fset *token.FileSet, filename string, src []byte, stdin bool) (*ast.F
// into a function body. This handles expressions too.
// Insert using a ;, not a newline, so that the line numbers
// in fsrc match the ones in src.
- fsrc := append(append([]byte("package p; func _() {"), src...), '}')
+ fsrc := append(append([]byte("package p; func _() {"), src...), '\n', '}')
file, err = parser.ParseFile(fset, filename, fsrc, parserMode)
if err == nil {
adjust := func(orig, src []byte) []byte {
// Remove the wrapping.
// Gofmt has turned the ; into a \n\n.
src = src[len("package p\n\nfunc _() {"):]
- src = src[:len(src)-len("}\n")]
+ src = src[:len(src)-len("\n}\n")]
// Gofmt has also indented the function body one level.
// Remove that indent.
src = bytes.Replace(src, []byte("\n\t"), []byte("\n"), -1)
diff --git a/src/cmd/gofmt/gofmt_test.go b/src/cmd/gofmt/gofmt_test.go
index ca44f3dcf7..d1edb7bcc1 100644
--- a/src/cmd/gofmt/gofmt_test.go
+++ b/src/cmd/gofmt/gofmt_test.go
@@ -6,18 +6,60 @@ package main
import (
"bytes"
+ "flag"
"io/ioutil"
+ "os"
"path/filepath"
"strings"
"testing"
+ "text/scanner"
)
-func runTest(t *testing.T, in, out, flags string) {
+var update = flag.Bool("update", false, "update .golden files")
+
+// gofmtFlags looks for a comment of the form
+//
+// //gofmt flags
+//
+// within the first maxLines lines of the given file,
+// and returns the flags string, if any. Otherwise it
+// returns the empty string.
+func gofmtFlags(filename string, maxLines int) string {
+ f, err := os.Open(filename)
+ if err != nil {
+ return "" // ignore errors - they will be found later
+ }
+ defer f.Close()
+
+ // initialize scanner
+ var s scanner.Scanner
+ s.Init(f)
+ s.Error = func(*scanner.Scanner, string) {} // ignore errors
+ s.Mode = scanner.GoTokens &^ scanner.SkipComments // want comments
+
+ // look for //gofmt comment
+ for s.Line <= maxLines {
+ switch s.Scan() {
+ case scanner.Comment:
+ const prefix = "//gofmt "
+ if t := s.TokenText(); strings.HasPrefix(t, prefix) {
+ return strings.TrimSpace(t[len(prefix):])
+ }
+ case scanner.EOF:
+ return ""
+ }
+
+ }
+
+ return ""
+}
+
+func runTest(t *testing.T, in, out string) {
// process flags
*simplifyAST = false
*rewriteRule = ""
stdin := false
- for _, flag := range strings.Split(flags, " ") {
+ for _, flag := range strings.Split(gofmtFlags(in, 20), " ") {
elts := strings.SplitN(flag, "=", 2)
name := elts[0]
value := ""
@@ -56,6 +98,17 @@ func runTest(t *testing.T, in, out, flags string) {
}
if got := buf.Bytes(); !bytes.Equal(got, expected) {
+ if *update {
+ if in != out {
+ if err := ioutil.WriteFile(out, got, 0666); err != nil {
+ t.Error(err)
+ }
+ return
+ }
+ // in == out: don't accidentally destroy input
+ t.Errorf("WARNING: -update did not rewrite input file %s", in)
+ }
+
t.Errorf("(gofmt %s) != %s (see %s.gofmt)", in, out, in)
d, err := diff(expected, got)
if err == nil {
@@ -67,53 +120,37 @@ func runTest(t *testing.T, in, out, flags string) {
}
}
-var tests = []struct {
- in, flags string
-}{
- {"gofmt.go", ""},
- {"gofmt_test.go", ""},
- {"testdata/composites.input", "-s"},
- {"testdata/slices1.input", "-s"},
- {"testdata/slices2.input", "-s"},
- {"testdata/ranges.input", "-s"},
- {"testdata/old.input", ""},
- {"testdata/rewrite1.input", "-r=Foo->Bar"},
- {"testdata/rewrite2.input", "-r=int->bool"},
- {"testdata/rewrite3.input", "-r=x->x"},
- {"testdata/rewrite4.input", "-r=(x)->x"},
- {"testdata/rewrite5.input", "-r=x+x->2*x"},
- {"testdata/rewrite6.input", "-r=fun(x)->Fun(x)"},
- {"testdata/rewrite7.input", "-r=fun(x...)->Fun(x)"},
- {"testdata/rewrite8.input", "-r=interface{}->int"},
- {"testdata/stdin*.input", "-stdin"},
- {"testdata/comments.input", ""},
- {"testdata/import.input", ""},
- {"testdata/crlf.input", ""}, // test case for issue 3961; see also TestCRLF
- {"testdata/typeswitch.input", ""}, // test case for issue 4470
- {"testdata/emptydecl.input", "-s"}, // test case for issue 7631
-}
-
+// TestRewrite processes testdata/*.input files and compares them to the
+// corresponding testdata/*.golden files. The gofmt flags used to process
+// a file must be provided via a comment of the form
+//
+// //gofmt flags
+//
+// in the processed file within the first 20 lines, if any.
func TestRewrite(t *testing.T) {
- for _, test := range tests {
- match, err := filepath.Glob(test.in)
- if err != nil {
- t.Error(err)
- continue
+ // determine input files
+ match, err := filepath.Glob("testdata/*.input")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // add larger examples
+ match = append(match, "gofmt.go", "gofmt_test.go")
+
+ for _, in := range match {
+ out := in // for files where input and output are identical
+ if strings.HasSuffix(in, ".input") {
+ out = in[:len(in)-len(".input")] + ".golden"
}
- for _, in := range match {
- out := in
- if strings.HasSuffix(in, ".input") {
- out = in[:len(in)-len(".input")] + ".golden"
- }
- runTest(t, in, out, test.flags)
- if in != out {
- // Check idempotence.
- runTest(t, out, out, test.flags)
- }
+ runTest(t, in, out)
+ if in != out {
+ // Check idempotence.
+ runTest(t, out, out)
}
}
}
+// Test case for issue 3961.
func TestCRLF(t *testing.T) {
const input = "testdata/crlf.input" // must contain CR/LF's
const golden = "testdata/crlf.golden" // must not contain any CR's
diff --git a/src/cmd/gofmt/testdata/composites.golden b/src/cmd/gofmt/testdata/composites.golden
index b2825e732a..fc9c98e625 100644
--- a/src/cmd/gofmt/testdata/composites.golden
+++ b/src/cmd/gofmt/testdata/composites.golden
@@ -1,3 +1,5 @@
+//gofmt -s
+
package P
type T struct {
diff --git a/src/cmd/gofmt/testdata/composites.input b/src/cmd/gofmt/testdata/composites.input
index 7210dafc96..fc7598af99 100644
--- a/src/cmd/gofmt/testdata/composites.input
+++ b/src/cmd/gofmt/testdata/composites.input
@@ -1,3 +1,5 @@
+//gofmt -s
+
package P
type T struct {
diff --git a/src/cmd/gofmt/testdata/crlf.golden b/src/cmd/gofmt/testdata/crlf.golden
index 57679f770f..193dbacc72 100644
--- a/src/cmd/gofmt/testdata/crlf.golden
+++ b/src/cmd/gofmt/testdata/crlf.golden
@@ -2,6 +2,7 @@
Source containing CR/LF line endings.
The gofmt'ed output must only have LF
line endings.
+ Test case for issue 3961.
*/
package main
diff --git a/src/cmd/gofmt/testdata/crlf.input b/src/cmd/gofmt/testdata/crlf.input
index 61a1aa0b4e..ae7e14dbf1 100644
--- a/src/cmd/gofmt/testdata/crlf.input
+++ b/src/cmd/gofmt/testdata/crlf.input
@@ -2,6 +2,7 @@
Source containing CR/LF line endings.
The gofmt'ed output must only have LF
line endings.
+ Test case for issue 3961.
*/
package main
diff --git a/src/cmd/gofmt/testdata/emptydecl.golden b/src/cmd/gofmt/testdata/emptydecl.golden
index 9fe62c9738..33d6435e0a 100644
--- a/src/cmd/gofmt/testdata/emptydecl.golden
+++ b/src/cmd/gofmt/testdata/emptydecl.golden
@@ -1,3 +1,7 @@
+//gofmt -s
+
+// Test case for issue 7631.
+
package main
// Keep this declaration
diff --git a/src/cmd/gofmt/testdata/emptydecl.input b/src/cmd/gofmt/testdata/emptydecl.input
index d1cab00ef7..4948a61f0d 100644
--- a/src/cmd/gofmt/testdata/emptydecl.input
+++ b/src/cmd/gofmt/testdata/emptydecl.input
@@ -1,3 +1,7 @@
+//gofmt -s
+
+// Test case for issue 7631.
+
package main
// Keep this declaration
diff --git a/src/cmd/gofmt/testdata/ranges.golden b/src/cmd/gofmt/testdata/ranges.golden
index 42168526d1..506b3a035a 100644
--- a/src/cmd/gofmt/testdata/ranges.golden
+++ b/src/cmd/gofmt/testdata/ranges.golden
@@ -1,3 +1,5 @@
+//gofmt -s
+
// Test cases for range simplification.
package p
diff --git a/src/cmd/gofmt/testdata/ranges.input b/src/cmd/gofmt/testdata/ranges.input
index 4b02d51752..df5f8333c2 100644
--- a/src/cmd/gofmt/testdata/ranges.input
+++ b/src/cmd/gofmt/testdata/ranges.input
@@ -1,3 +1,5 @@
+//gofmt -s
+
// Test cases for range simplification.
package p
diff --git a/src/cmd/gofmt/testdata/rewrite1.golden b/src/cmd/gofmt/testdata/rewrite1.golden
index d9beb37058..3ee5373a79 100644
--- a/src/cmd/gofmt/testdata/rewrite1.golden
+++ b/src/cmd/gofmt/testdata/rewrite1.golden
@@ -1,3 +1,5 @@
+//gofmt -r=Foo->Bar
+
// 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.
diff --git a/src/cmd/gofmt/testdata/rewrite1.input b/src/cmd/gofmt/testdata/rewrite1.input
index bdb894320d..a84c8f7816 100644
--- a/src/cmd/gofmt/testdata/rewrite1.input
+++ b/src/cmd/gofmt/testdata/rewrite1.input
@@ -1,3 +1,5 @@
+//gofmt -r=Foo->Bar
+
// 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.
diff --git a/src/cmd/gofmt/testdata/rewrite2.golden b/src/cmd/gofmt/testdata/rewrite2.golden
index 64c67ffa67..f980e03530 100644
--- a/src/cmd/gofmt/testdata/rewrite2.golden
+++ b/src/cmd/gofmt/testdata/rewrite2.golden
@@ -1,3 +1,5 @@
+//gofmt -r=int->bool
+
// 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.
diff --git a/src/cmd/gofmt/testdata/rewrite2.input b/src/cmd/gofmt/testdata/rewrite2.input
index 21171447a1..489be4e07d 100644
--- a/src/cmd/gofmt/testdata/rewrite2.input
+++ b/src/cmd/gofmt/testdata/rewrite2.input
@@ -1,3 +1,5 @@
+//gofmt -r=int->bool
+
// 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.
diff --git a/src/cmd/gofmt/testdata/rewrite3.golden b/src/cmd/gofmt/testdata/rewrite3.golden
index 0d16d16011..261a220c65 100644
--- a/src/cmd/gofmt/testdata/rewrite3.golden
+++ b/src/cmd/gofmt/testdata/rewrite3.golden
@@ -1,3 +1,5 @@
+//gofmt -r=x->x
+
// 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.
diff --git a/src/cmd/gofmt/testdata/rewrite3.input b/src/cmd/gofmt/testdata/rewrite3.input
index 0d16d16011..261a220c65 100644
--- a/src/cmd/gofmt/testdata/rewrite3.input
+++ b/src/cmd/gofmt/testdata/rewrite3.input
@@ -1,3 +1,5 @@
+//gofmt -r=x->x
+
// 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.
diff --git a/src/cmd/gofmt/testdata/rewrite4.golden b/src/cmd/gofmt/testdata/rewrite4.golden
index 8dfc81a074..b05547b4bf 100644
--- a/src/cmd/gofmt/testdata/rewrite4.golden
+++ b/src/cmd/gofmt/testdata/rewrite4.golden
@@ -1,3 +1,5 @@
+//gofmt -r=(x)->x
+
// 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.
diff --git a/src/cmd/gofmt/testdata/rewrite4.input b/src/cmd/gofmt/testdata/rewrite4.input
index 164cc0451f..0817099209 100644
--- a/src/cmd/gofmt/testdata/rewrite4.input
+++ b/src/cmd/gofmt/testdata/rewrite4.input
@@ -1,3 +1,5 @@
+//gofmt -r=(x)->x
+
// 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.
diff --git a/src/cmd/gofmt/testdata/rewrite5.golden b/src/cmd/gofmt/testdata/rewrite5.golden
index 5a448a63d3..9beb34aee7 100644
--- a/src/cmd/gofmt/testdata/rewrite5.golden
+++ b/src/cmd/gofmt/testdata/rewrite5.golden
@@ -1,3 +1,5 @@
+//gofmt -r=x+x->2*x
+
// 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.
diff --git a/src/cmd/gofmt/testdata/rewrite5.input b/src/cmd/gofmt/testdata/rewrite5.input
index 0d759e69b6..d7a6122d07 100644
--- a/src/cmd/gofmt/testdata/rewrite5.input
+++ b/src/cmd/gofmt/testdata/rewrite5.input
@@ -1,3 +1,5 @@
+//gofmt -r=x+x->2*x
+
// 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.
diff --git a/src/cmd/gofmt/testdata/rewrite6.golden b/src/cmd/gofmt/testdata/rewrite6.golden
index e565dbdd97..48ec9aa0df 100644
--- a/src/cmd/gofmt/testdata/rewrite6.golden
+++ b/src/cmd/gofmt/testdata/rewrite6.golden
@@ -1,3 +1,5 @@
+//gofmt -r=fun(x)->Fun(x)
+
// 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.
diff --git a/src/cmd/gofmt/testdata/rewrite6.input b/src/cmd/gofmt/testdata/rewrite6.input
index 8c088b3e87..b085a84fef 100644
--- a/src/cmd/gofmt/testdata/rewrite6.input
+++ b/src/cmd/gofmt/testdata/rewrite6.input
@@ -1,3 +1,5 @@
+//gofmt -r=fun(x)->Fun(x)
+
// 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.
diff --git a/src/cmd/gofmt/testdata/rewrite7.golden b/src/cmd/gofmt/testdata/rewrite7.golden
index 29babad9f9..8386a0b2a3 100644
--- a/src/cmd/gofmt/testdata/rewrite7.golden
+++ b/src/cmd/gofmt/testdata/rewrite7.golden
@@ -1,3 +1,5 @@
+//gofmt -r=fun(x...)->Fun(x)
+
// 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.
diff --git a/src/cmd/gofmt/testdata/rewrite7.input b/src/cmd/gofmt/testdata/rewrite7.input
index 073e2a3e6f..c1984708e7 100644
--- a/src/cmd/gofmt/testdata/rewrite7.input
+++ b/src/cmd/gofmt/testdata/rewrite7.input
@@ -1,3 +1,5 @@
+//gofmt -r=fun(x...)->Fun(x)
+
// 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.
diff --git a/src/cmd/gofmt/testdata/rewrite8.golden b/src/cmd/gofmt/testdata/rewrite8.golden
index cfc452b031..62f0419dfb 100644
--- a/src/cmd/gofmt/testdata/rewrite8.golden
+++ b/src/cmd/gofmt/testdata/rewrite8.golden
@@ -1,3 +1,5 @@
+//gofmt -r=interface{}->int
+
// 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.
diff --git a/src/cmd/gofmt/testdata/rewrite8.input b/src/cmd/gofmt/testdata/rewrite8.input
index 235efa91cc..7964c5c75c 100644
--- a/src/cmd/gofmt/testdata/rewrite8.input
+++ b/src/cmd/gofmt/testdata/rewrite8.input
@@ -1,3 +1,5 @@
+//gofmt -r=interface{}->int
+
// 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.
diff --git a/src/cmd/gofmt/testdata/slices1.golden b/src/cmd/gofmt/testdata/slices1.golden
index 6633a5e001..04bc16f216 100644
--- a/src/cmd/gofmt/testdata/slices1.golden
+++ b/src/cmd/gofmt/testdata/slices1.golden
@@ -1,3 +1,5 @@
+//gofmt -s
+
// Test cases for slice expression simplification.
package p
diff --git a/src/cmd/gofmt/testdata/slices1.input b/src/cmd/gofmt/testdata/slices1.input
index 27e9cb8fef..1f25c43ccb 100644
--- a/src/cmd/gofmt/testdata/slices1.input
+++ b/src/cmd/gofmt/testdata/slices1.input
@@ -1,3 +1,5 @@
+//gofmt -s
+
// Test cases for slice expression simplification.
package p
diff --git a/src/cmd/gofmt/testdata/slices2.golden b/src/cmd/gofmt/testdata/slices2.golden
index 433788e1ee..ab657004e6 100644
--- a/src/cmd/gofmt/testdata/slices2.golden
+++ b/src/cmd/gofmt/testdata/slices2.golden
@@ -1,3 +1,5 @@
+//gofmt -s
+
// Test cases for slice expression simplification.
// Because of a dot import, these slices must remain untouched.
package p
diff --git a/src/cmd/gofmt/testdata/slices2.input b/src/cmd/gofmt/testdata/slices2.input
index 433788e1ee..ab657004e6 100644
--- a/src/cmd/gofmt/testdata/slices2.input
+++ b/src/cmd/gofmt/testdata/slices2.input
@@ -1,3 +1,5 @@
+//gofmt -s
+
// Test cases for slice expression simplification.
// Because of a dot import, these slices must remain untouched.
package p
diff --git a/src/cmd/gofmt/testdata/stdin1.golden b/src/cmd/gofmt/testdata/stdin1.golden
index ff8b0b7ab4..9e4dcd20fe 100644
--- a/src/cmd/gofmt/testdata/stdin1.golden
+++ b/src/cmd/gofmt/testdata/stdin1.golden
@@ -1,3 +1,5 @@
+ //gofmt -stdin
+
if x {
y
}
diff --git a/src/cmd/gofmt/testdata/stdin1.golden.gofmt b/src/cmd/gofmt/testdata/stdin1.golden.gofmt
deleted file mode 100644
index 1f888877d0..0000000000
--- a/src/cmd/gofmt/testdata/stdin1.golden.gofmt
+++ /dev/null
@@ -1,3 +0,0 @@
- if x {
- y
-}
diff --git a/src/cmd/gofmt/testdata/stdin1.input b/src/cmd/gofmt/testdata/stdin1.input
index ff8b0b7ab4..9e4dcd20fe 100644
--- a/src/cmd/gofmt/testdata/stdin1.input
+++ b/src/cmd/gofmt/testdata/stdin1.input
@@ -1,3 +1,5 @@
+ //gofmt -stdin
+
if x {
y
}
diff --git a/src/cmd/gofmt/testdata/stdin1.input.gofmt b/src/cmd/gofmt/testdata/stdin1.input.gofmt
deleted file mode 100644
index 1f888877d0..0000000000
--- a/src/cmd/gofmt/testdata/stdin1.input.gofmt
+++ /dev/null
@@ -1,3 +0,0 @@
- if x {
- y
-}
diff --git a/src/cmd/gofmt/testdata/stdin2.golden b/src/cmd/gofmt/testdata/stdin2.golden
index 7eb1b54fec..57df355403 100644
--- a/src/cmd/gofmt/testdata/stdin2.golden
+++ b/src/cmd/gofmt/testdata/stdin2.golden
@@ -1,4 +1,4 @@
-
+//gofmt -stdin
var x int
diff --git a/src/cmd/gofmt/testdata/stdin2.golden.gofmt b/src/cmd/gofmt/testdata/stdin2.golden.gofmt
deleted file mode 100644
index 85e8003008..0000000000
--- a/src/cmd/gofmt/testdata/stdin2.golden.gofmt
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-var x int
-
-func f() {
- y := z
-}
-
-
diff --git a/src/cmd/gofmt/testdata/stdin2.input b/src/cmd/gofmt/testdata/stdin2.input
index 99defd2d10..69d6bdd682 100644
--- a/src/cmd/gofmt/testdata/stdin2.input
+++ b/src/cmd/gofmt/testdata/stdin2.input
@@ -1,4 +1,4 @@
-
+//gofmt -stdin
var x int
diff --git a/src/cmd/gofmt/testdata/stdin2.input.gofmt b/src/cmd/gofmt/testdata/stdin2.input.gofmt
deleted file mode 100644
index 7eb1b54fec..0000000000
--- a/src/cmd/gofmt/testdata/stdin2.input.gofmt
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-var x int
-
-func f() {
- y := z
- /* this is a comment */
- // this is a comment too
-}
-
-
diff --git a/src/cmd/gofmt/testdata/stdin3.golden b/src/cmd/gofmt/testdata/stdin3.golden
index 1bf2f5a483..d6da0e417a 100644
--- a/src/cmd/gofmt/testdata/stdin3.golden
+++ b/src/cmd/gofmt/testdata/stdin3.golden
@@ -1,3 +1,4 @@
+ //gofmt -stdin
/* note: no newline at end of file */
for i := 0; i < 10; i++ {
diff --git a/src/cmd/gofmt/testdata/stdin3.golden.gofmt b/src/cmd/gofmt/testdata/stdin3.golden.gofmt
deleted file mode 100644
index b4d1d4663e..0000000000
--- a/src/cmd/gofmt/testdata/stdin3.golden.gofmt
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- /* note: no newline at end of file */
- for i := 0; i < 10; i++ {
- s += i
- }
- \ No newline at end of file
diff --git a/src/cmd/gofmt/testdata/stdin3.input b/src/cmd/gofmt/testdata/stdin3.input
index d963bd0d21..ab46c1063b 100644
--- a/src/cmd/gofmt/testdata/stdin3.input
+++ b/src/cmd/gofmt/testdata/stdin3.input
@@ -1,3 +1,4 @@
+ //gofmt -stdin
/* note: no newline at end of file */
for i := 0; i < 10; i++ { s += i }
diff --git a/src/cmd/gofmt/testdata/stdin3.input.gofmt b/src/cmd/gofmt/testdata/stdin3.input.gofmt
deleted file mode 100644
index b4d1d4663e..0000000000
--- a/src/cmd/gofmt/testdata/stdin3.input.gofmt
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- /* note: no newline at end of file */
- for i := 0; i < 10; i++ {
- s += i
- }
- \ No newline at end of file
diff --git a/src/cmd/gofmt/testdata/stdin4.golden b/src/cmd/gofmt/testdata/stdin4.golden
index 5f73435517..0c7acace5d 100644
--- a/src/cmd/gofmt/testdata/stdin4.golden
+++ b/src/cmd/gofmt/testdata/stdin4.golden
@@ -1,3 +1,5 @@
+ //gofmt -stdin
+
// comment
i := 0
diff --git a/src/cmd/gofmt/testdata/stdin4.golden.gofmt b/src/cmd/gofmt/testdata/stdin4.golden.gofmt
deleted file mode 100644
index 5f73435517..0000000000
--- a/src/cmd/gofmt/testdata/stdin4.golden.gofmt
+++ /dev/null
@@ -1,3 +0,0 @@
- // comment
-
- i := 0
diff --git a/src/cmd/gofmt/testdata/stdin4.input b/src/cmd/gofmt/testdata/stdin4.input
index f02a54fb1a..1fc73f31e5 100644
--- a/src/cmd/gofmt/testdata/stdin4.input
+++ b/src/cmd/gofmt/testdata/stdin4.input
@@ -1,3 +1,5 @@
+ //gofmt -stdin
+
// comment
i := 0
diff --git a/src/cmd/gofmt/testdata/stdin4.input.gofmt b/src/cmd/gofmt/testdata/stdin4.input.gofmt
deleted file mode 100644
index 5f73435517..0000000000
--- a/src/cmd/gofmt/testdata/stdin4.input.gofmt
+++ /dev/null
@@ -1,3 +0,0 @@
- // comment
-
- i := 0
diff --git a/src/cmd/gofmt/testdata/stdin5.golden b/src/cmd/gofmt/testdata/stdin5.golden
new file mode 100644
index 0000000000..31ce6b2485
--- /dev/null
+++ b/src/cmd/gofmt/testdata/stdin5.golden
@@ -0,0 +1,3 @@
+//gofmt -stdin
+
+i := 5 // Line comment without newline. \ No newline at end of file
diff --git a/src/cmd/gofmt/testdata/stdin5.input b/src/cmd/gofmt/testdata/stdin5.input
new file mode 100644
index 0000000000..0a7c97d180
--- /dev/null
+++ b/src/cmd/gofmt/testdata/stdin5.input
@@ -0,0 +1,3 @@
+//gofmt -stdin
+
+i :=5// Line comment without newline. \ No newline at end of file
diff --git a/src/cmd/nm/elf.go b/src/cmd/internal/objfile/elf.go
index 5aaa194dd1..8495fa7532 100644
--- a/src/cmd/nm/elf.go
+++ b/src/cmd/internal/objfile/elf.go
@@ -4,24 +4,29 @@
// Parsing of ELF executables (Linux, FreeBSD, and so on).
-package main
+package objfile
import (
"debug/elf"
"os"
)
-func elfSymbols(f *os.File) []Sym {
- p, err := elf.NewFile(f)
+type elfFile struct {
+ elf *elf.File
+}
+
+func openElf(r *os.File) (rawFile, error) {
+ f, err := elf.NewFile(r)
if err != nil {
- errorf("parsing %s: %v", f.Name(), err)
- return nil
+ return nil, err
}
+ return &elfFile{f}, nil
+}
- elfSyms, err := p.Symbols()
+func (f *elfFile) symbols() ([]Sym, error) {
+ elfSyms, err := f.elf.Symbols()
if err != nil {
- errorf("parsing %s: %v", f.Name(), err)
- return nil
+ return nil, err
}
var syms []Sym
@@ -34,10 +39,10 @@ func elfSymbols(f *os.File) []Sym {
sym.Code = 'B'
default:
i := int(s.Section)
- if i < 0 || i >= len(p.Sections) {
+ if i < 0 || i >= len(f.elf.Sections) {
break
}
- sect := p.Sections[i]
+ sect := f.elf.Sections[i]
switch sect.Flags & (elf.SHF_WRITE | elf.SHF_ALLOC | elf.SHF_EXECINSTR) {
case elf.SHF_ALLOC | elf.SHF_EXECINSTR:
sym.Code = 'T'
@@ -53,5 +58,22 @@ func elfSymbols(f *os.File) []Sym {
syms = append(syms, sym)
}
- return syms
+ return syms, nil
+}
+
+func (f *elfFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
+ if sect := f.elf.Section(".text"); sect != nil {
+ textStart = sect.Addr
+ }
+ if sect := f.elf.Section(".gosymtab"); sect != nil {
+ if symtab, err = sect.Data(); err != nil {
+ return 0, nil, nil, err
+ }
+ }
+ if sect := f.elf.Section(".gopclntab"); sect != nil {
+ if pclntab, err = sect.Data(); err != nil {
+ return 0, nil, nil, err
+ }
+ }
+ return textStart, symtab, pclntab, nil
}
diff --git a/src/cmd/nm/goobj.go b/src/cmd/internal/objfile/goobj.go
index b0de51db9c..a4f49ebe44 100644
--- a/src/cmd/nm/goobj.go
+++ b/src/cmd/internal/objfile/goobj.go
@@ -4,7 +4,7 @@
// Parsing of Go intermediate object files and archives.
-package main
+package objfile
import (
"debug/goobj"
@@ -12,6 +12,18 @@ import (
"os"
)
+type goobjFile struct {
+ goobj *goobj.Package
+}
+
+func openGoobj(r *os.File) (rawFile, error) {
+ f, err := goobj.Parse(r, `""`)
+ if err != nil {
+ return nil, err
+ }
+ return &goobjFile{f}, nil
+}
+
func goobjName(id goobj.SymID) string {
if id.Version == 0 {
return id.Name
@@ -19,17 +31,11 @@ func goobjName(id goobj.SymID) string {
return fmt.Sprintf("%s<%d>", id.Name, id.Version)
}
-func goobjSymbols(f *os.File) []Sym {
- pkg, err := goobj.Parse(f, `""`)
- if err != nil {
- errorf("parsing %s: %v", f.Name(), err)
- return nil
- }
-
+func (f *goobjFile) symbols() ([]Sym, error) {
seen := make(map[goobj.SymID]bool)
var syms []Sym
- for _, s := range pkg.Syms {
+ for _, s := range f.goobj.Syms {
seen[s.SymID] = true
sym := Sym{Addr: uint64(s.Data.Offset), Name: goobjName(s.SymID), Size: int64(s.Size), Type: s.Type.Name, Code: '?'}
switch s.Kind {
@@ -50,7 +56,7 @@ func goobjSymbols(f *os.File) []Sym {
syms = append(syms, sym)
}
- for _, s := range pkg.Syms {
+ for _, s := range f.goobj.Syms {
for _, r := range s.Reloc {
if !seen[r.Sym] {
seen[r.Sym] = true
@@ -64,5 +70,12 @@ func goobjSymbols(f *os.File) []Sym {
}
}
- return syms
+ return syms, nil
+}
+
+// pcln does not make sense for Go object files, because each
+// symbol has its own individual pcln table, so there is no global
+// space of addresses to map.
+func (f *goobjFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
+ return 0, nil, nil, fmt.Errorf("pcln not available in go object file")
}
diff --git a/src/cmd/nm/macho.go b/src/cmd/internal/objfile/macho.go
index c60bde55b4..f845792ffa 100644
--- a/src/cmd/nm/macho.go
+++ b/src/cmd/internal/objfile/macho.go
@@ -4,36 +4,42 @@
// Parsing of Mach-O executables (OS X).
-package main
+package objfile
import (
"debug/macho"
+ "fmt"
"os"
"sort"
)
-func machoSymbols(f *os.File) []Sym {
- p, err := macho.NewFile(f)
+type machoFile struct {
+ macho *macho.File
+}
+
+func openMacho(r *os.File) (rawFile, error) {
+ f, err := macho.NewFile(r)
if err != nil {
- errorf("parsing %s: %v", f.Name(), err)
- return nil
+ return nil, err
}
+ return &machoFile{f}, nil
+}
- if p.Symtab == nil {
- errorf("%s: no symbol table", f.Name())
- return nil
+func (f *machoFile) symbols() ([]Sym, error) {
+ if f.macho.Symtab == nil {
+ return nil, fmt.Errorf("missing symbol table")
}
// Build sorted list of addresses of all symbols.
// We infer the size of a symbol by looking at where the next symbol begins.
var addrs []uint64
- for _, s := range p.Symtab.Syms {
+ for _, s := range f.macho.Symtab.Syms {
addrs = append(addrs, s.Value)
}
sort.Sort(uint64s(addrs))
var syms []Sym
- for _, s := range p.Symtab.Syms {
+ for _, s := range f.macho.Symtab.Syms {
sym := Sym{Name: s.Name, Addr: s.Value, Code: '?'}
i := sort.Search(len(addrs), func(x int) bool { return addrs[x] > s.Value })
if i < len(addrs) {
@@ -41,8 +47,8 @@ func machoSymbols(f *os.File) []Sym {
}
if s.Sect == 0 {
sym.Code = 'U'
- } else if int(s.Sect) <= len(p.Sections) {
- sect := p.Sections[s.Sect-1]
+ } else if int(s.Sect) <= len(f.macho.Sections) {
+ sect := f.macho.Sections[s.Sect-1]
switch sect.Seg {
case "__TEXT":
sym.Code = 'R'
@@ -59,7 +65,24 @@ func machoSymbols(f *os.File) []Sym {
syms = append(syms, sym)
}
- return syms
+ return syms, nil
+}
+
+func (f *machoFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
+ if sect := f.macho.Section("__text"); sect != nil {
+ textStart = sect.Addr
+ }
+ if sect := f.macho.Section("__gosymtab"); sect != nil {
+ if symtab, err = sect.Data(); err != nil {
+ return 0, nil, nil, err
+ }
+ }
+ if sect := f.macho.Section("__gopclntab"); sect != nil {
+ if pclntab, err = sect.Data(); err != nil {
+ return 0, nil, nil, err
+ }
+ }
+ return textStart, symtab, pclntab, nil
}
type uint64s []uint64
diff --git a/src/cmd/internal/objfile/objfile.go b/src/cmd/internal/objfile/objfile.go
new file mode 100644
index 0000000000..09fa63e60b
--- /dev/null
+++ b/src/cmd/internal/objfile/objfile.go
@@ -0,0 +1,72 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package objfile implements portable access to OS-specific executable files.
+package objfile
+
+import (
+ "debug/gosym"
+ "fmt"
+ "os"
+)
+
+type rawFile interface {
+ symbols() (syms []Sym, err error)
+ pcln() (textStart uint64, symtab, pclntab []byte, err error)
+}
+
+// A File is an opened executable file.
+type File struct {
+ r *os.File
+ raw rawFile
+}
+
+// A Sym is a symbol defined in an executable file.
+type Sym struct {
+ Name string // symbol name
+ Addr uint64 // virtual address of symbol
+ Size int64 // size in bytes
+ Code rune // nm code (T for text, D for data, and so on)
+ Type string // XXX?
+}
+
+var openers = []func(*os.File) (rawFile, error){
+ openElf,
+ openGoobj,
+ openMacho,
+ openPE,
+ openPlan9,
+}
+
+// Open opens the named file.
+// The caller must call f.Close when the file is no longer needed.
+func Open(name string) (*File, error) {
+ r, err := os.Open(name)
+ if err != nil {
+ return nil, err
+ }
+ for _, try := range openers {
+ if raw, err := try(r); err == nil {
+ return &File{r, raw}, nil
+ }
+ }
+ r.Close()
+ return nil, fmt.Errorf("open %s: unrecognized object file", name)
+}
+
+func (f *File) Close() error {
+ return f.r.Close()
+}
+
+func (f *File) Symbols() ([]Sym, error) {
+ return f.raw.symbols()
+}
+
+func (f *File) PCLineTable() (*gosym.Table, error) {
+ textStart, symtab, pclntab, err := f.raw.pcln()
+ if err != nil {
+ return nil, err
+ }
+ return gosym.NewTable(symtab, gosym.NewLineTable(pclntab, textStart))
+}
diff --git a/src/cmd/internal/objfile/pe.go b/src/cmd/internal/objfile/pe.go
new file mode 100644
index 0000000000..868709eaf9
--- /dev/null
+++ b/src/cmd/internal/objfile/pe.go
@@ -0,0 +1,170 @@
+// 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.
+
+// Parsing of PE executables (Microsoft Windows).
+
+package objfile
+
+import (
+ "debug/pe"
+ "fmt"
+ "os"
+ "sort"
+)
+
+type peFile struct {
+ pe *pe.File
+}
+
+func openPE(r *os.File) (rawFile, error) {
+ f, err := pe.NewFile(r)
+ if err != nil {
+ return nil, err
+ }
+ switch f.OptionalHeader.(type) {
+ case *pe.OptionalHeader32, *pe.OptionalHeader64:
+ // ok
+ default:
+ return nil, fmt.Errorf("unrecognized PE format")
+ }
+ return &peFile{f}, nil
+}
+
+func (f *peFile) symbols() ([]Sym, error) {
+ // Build sorted list of addresses of all symbols.
+ // We infer the size of a symbol by looking at where the next symbol begins.
+ var addrs []uint64
+
+ var imageBase uint64
+ switch oh := f.pe.OptionalHeader.(type) {
+ case *pe.OptionalHeader32:
+ imageBase = uint64(oh.ImageBase)
+ case *pe.OptionalHeader64:
+ imageBase = oh.ImageBase
+ }
+
+ var syms []Sym
+ for _, s := range f.pe.Symbols {
+ const (
+ N_UNDEF = 0 // An undefined (extern) symbol
+ N_ABS = -1 // An absolute symbol (e_value is a constant, not an address)
+ N_DEBUG = -2 // A debugging symbol
+ )
+ sym := Sym{Name: s.Name, Addr: uint64(s.Value), Code: '?'}
+ switch s.SectionNumber {
+ case N_UNDEF:
+ sym.Code = 'U'
+ case N_ABS:
+ sym.Code = 'C'
+ case N_DEBUG:
+ sym.Code = '?'
+ default:
+ if s.SectionNumber < 0 || len(f.pe.Sections) < int(s.SectionNumber) {
+ return nil, fmt.Errorf("invalid section number in symbol table")
+ }
+ sect := f.pe.Sections[s.SectionNumber-1]
+ const (
+ text = 0x20
+ data = 0x40
+ bss = 0x80
+ permX = 0x20000000
+ permR = 0x40000000
+ permW = 0x80000000
+ )
+ ch := sect.Characteristics
+ switch {
+ case ch&text != 0:
+ sym.Code = 'T'
+ case ch&data != 0:
+ if ch&permW == 0 {
+ sym.Code = 'R'
+ } else {
+ sym.Code = 'D'
+ }
+ case ch&bss != 0:
+ sym.Code = 'B'
+ }
+ sym.Addr += imageBase + uint64(sect.VirtualAddress)
+ }
+ syms = append(syms, sym)
+ addrs = append(addrs, sym.Addr)
+ }
+
+ sort.Sort(uint64s(addrs))
+ for i := range syms {
+ j := sort.Search(len(addrs), func(x int) bool { return addrs[x] > syms[i].Addr })
+ if j < len(addrs) {
+ syms[i].Size = int64(addrs[j] - syms[i].Addr)
+ }
+ }
+
+ return syms, nil
+}
+
+func (f *peFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
+ var imageBase uint64
+ switch oh := f.pe.OptionalHeader.(type) {
+ case *pe.OptionalHeader32:
+ imageBase = uint64(oh.ImageBase)
+ case *pe.OptionalHeader64:
+ imageBase = oh.ImageBase
+ default:
+ return 0, nil, nil, fmt.Errorf("pe file format not recognized")
+ }
+ if sect := f.pe.Section(".text"); sect != nil {
+ textStart = imageBase + uint64(sect.VirtualAddress)
+ }
+ if pclntab, err = loadPETable(f.pe, "runtime.pclntab", "runtime.epclntab"); err != nil {
+ // We didn't find the symbols, so look for the names used in 1.3 and earlier.
+ // TODO: Remove code looking for the old symbols when we no longer care about 1.3.
+ var err2 error
+ if pclntab, err2 = loadPETable(f.pe, "pclntab", "epclntab"); err2 != nil {
+ return 0, nil, nil, err
+ }
+ }
+ if symtab, err = loadPETable(f.pe, "runtime.symtab", "runtime.esymtab"); err != nil {
+ // Same as above.
+ var err2 error
+ if symtab, err2 = loadPETable(f.pe, "symtab", "esymtab"); err2 != nil {
+ return 0, nil, nil, err
+ }
+ }
+ return textStart, symtab, pclntab, nil
+}
+
+func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
+ for _, s := range f.Symbols {
+ if s.Name != name {
+ continue
+ }
+ if s.SectionNumber <= 0 {
+ return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber)
+ }
+ if len(f.Sections) < int(s.SectionNumber) {
+ return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections))
+ }
+ return s, nil
+ }
+ return nil, fmt.Errorf("no %s symbol found", name)
+}
+
+func loadPETable(f *pe.File, sname, ename string) ([]byte, error) {
+ ssym, err := findPESymbol(f, sname)
+ if err != nil {
+ return nil, err
+ }
+ esym, err := findPESymbol(f, ename)
+ if err != nil {
+ return nil, err
+ }
+ if ssym.SectionNumber != esym.SectionNumber {
+ return nil, fmt.Errorf("%s and %s symbols must be in the same section", sname, ename)
+ }
+ sect := f.Sections[ssym.SectionNumber-1]
+ data, err := sect.Data()
+ if err != nil {
+ return nil, err
+ }
+ return data[ssym.Value:esym.Value], nil
+}
diff --git a/src/cmd/internal/objfile/plan9obj.go b/src/cmd/internal/objfile/plan9obj.go
new file mode 100644
index 0000000000..80744f82a8
--- /dev/null
+++ b/src/cmd/internal/objfile/plan9obj.go
@@ -0,0 +1,124 @@
+// 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.
+
+// Parsing of Plan 9 a.out executables.
+
+package objfile
+
+import (
+ "debug/plan9obj"
+ "fmt"
+ "os"
+ "sort"
+)
+
+var validSymType = map[rune]bool{
+ 'T': true,
+ 't': true,
+ 'D': true,
+ 'd': true,
+ 'B': true,
+ 'b': true,
+}
+
+type plan9File struct {
+ plan9 *plan9obj.File
+}
+
+func openPlan9(r *os.File) (rawFile, error) {
+ f, err := plan9obj.NewFile(r)
+ if err != nil {
+ return nil, err
+ }
+ return &plan9File{f}, nil
+}
+
+func (f *plan9File) symbols() ([]Sym, error) {
+ plan9Syms, err := f.plan9.Symbols()
+ if err != nil {
+ return nil, err
+ }
+
+ // Build sorted list of addresses of all symbols.
+ // We infer the size of a symbol by looking at where the next symbol begins.
+ var addrs []uint64
+ for _, s := range plan9Syms {
+ if !validSymType[s.Type] {
+ continue
+ }
+ addrs = append(addrs, s.Value)
+ }
+ sort.Sort(uint64s(addrs))
+
+ var syms []Sym
+
+ for _, s := range plan9Syms {
+ if !validSymType[s.Type] {
+ continue
+ }
+ sym := Sym{Addr: s.Value, Name: s.Name, Code: rune(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)
+ }
+ syms = append(syms, sym)
+ }
+
+ return syms, nil
+}
+
+func (f *plan9File) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
+ textStart = f.plan9.LoadAddress + f.plan9.HdrSize
+ if pclntab, err = loadPlan9Table(f.plan9, "runtime.pclntab", "runtime.epclntab"); err != nil {
+ // We didn't find the symbols, so look for the names used in 1.3 and earlier.
+ // TODO: Remove code looking for the old symbols when we no longer care about 1.3.
+ var err2 error
+ if pclntab, err2 = loadPlan9Table(f.plan9, "pclntab", "epclntab"); err2 != nil {
+ return 0, nil, nil, err
+ }
+ }
+ if symtab, err = loadPlan9Table(f.plan9, "runtime.symtab", "runtime.esymtab"); err != nil {
+ // Same as above.
+ var err2 error
+ if symtab, err2 = loadPlan9Table(f.plan9, "symtab", "esymtab"); err2 != nil {
+ return 0, nil, nil, err
+ }
+ }
+ return textStart, symtab, pclntab, nil
+}
+
+func findPlan9Symbol(f *plan9obj.File, name string) (*plan9obj.Sym, error) {
+ syms, err := f.Symbols()
+ if err != nil {
+ return nil, err
+ }
+ for _, s := range syms {
+ if s.Name != name {
+ continue
+ }
+ return &s, nil
+ }
+ return nil, fmt.Errorf("no %s symbol found", name)
+}
+
+func loadPlan9Table(f *plan9obj.File, sname, ename string) ([]byte, error) {
+ ssym, err := findPlan9Symbol(f, sname)
+ if err != nil {
+ return nil, err
+ }
+ esym, err := findPlan9Symbol(f, ename)
+ if err != nil {
+ return nil, err
+ }
+ sect := f.Section("text")
+ if sect == nil {
+ return nil, err
+ }
+ data, err := sect.Data()
+ if err != nil {
+ return nil, err
+ }
+ textStart := f.LoadAddress + f.HdrSize
+ return data[ssym.Value-textStart : esym.Value-textStart], nil
+}
diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c
index 702f559a88..7bf18f0815 100644
--- a/src/cmd/ld/data.c
+++ b/src/cmd/ld/data.c
@@ -145,7 +145,7 @@ relocsym(LSym *s)
diag("%s: invalid relocation %d+%d not in [%d,%d)", s->name, off, siz, 0, s->np);
continue;
}
- if(r->sym != S && (r->sym->type & SMASK == 0 || r->sym->type & SMASK == SXREF)) {
+ if(r->sym != S && ((r->sym->type & (SMASK | SHIDDEN)) == 0 || (r->sym->type & SMASK) == SXREF)) {
diag("%s: not defined", r->sym->name);
continue;
}
@@ -281,9 +281,13 @@ relocsym(LSym *s)
if(thechar == '6')
o = 0;
} else if(HEADTYPE == Hdarwin) {
- if(rs->type != SHOSTOBJ)
- o += symaddr(rs) - rs->sect->vaddr;
- o -= r->off; // WTF?
+ if(r->type == R_CALL) {
+ if(rs->type != SHOSTOBJ)
+ o += symaddr(rs) - rs->sect->vaddr;
+ o -= r->off; // relative to section offset, not symbol
+ } else {
+ o += r->siz;
+ }
} else {
diag("unhandled pcrel relocation for %s", headstring);
}
@@ -425,10 +429,10 @@ dynreloc(void)
}
static void
-blk(LSym *start, int32 addr, int32 size)
+blk(LSym *start, int64 addr, int64 size)
{
LSym *sym;
- int32 eaddr;
+ int64 eaddr;
uchar *p, *ep;
for(sym = start; sym != nil; sym = sym->next)
@@ -469,10 +473,10 @@ blk(LSym *start, int32 addr, int32 size)
}
void
-codeblk(int32 addr, int32 size)
+codeblk(int64 addr, int64 size)
{
LSym *sym;
- int32 eaddr, n;
+ int64 eaddr, n;
uchar *q;
if(debug['a'])
@@ -529,10 +533,10 @@ codeblk(int32 addr, int32 size)
}
void
-datblk(int32 addr, int32 size)
+datblk(int64 addr, int64 size)
{
LSym *sym;
- int32 i, eaddr;
+ int64 i, eaddr;
uchar *p, *ep;
char *typ, *rsname;
Reloc *r;
@@ -985,8 +989,8 @@ dodata(void)
sect->align = maxalign(s, SINITARR-1);
datsize = rnd(datsize, sect->align);
sect->vaddr = datsize;
- linklookup(ctxt, "noptrdata", 0)->sect = sect;
- linklookup(ctxt, "enoptrdata", 0)->sect = sect;
+ linklookup(ctxt, "runtime.noptrdata", 0)->sect = sect;
+ linklookup(ctxt, "runtime.enoptrdata", 0)->sect = sect;
for(; s != nil && s->type < SINITARR; s = s->next) {
datsize = aligndatsize(datsize, s);
s->sect = sect;
@@ -1016,9 +1020,9 @@ dodata(void)
sect->align = maxalign(s, SBSS-1);
datsize = rnd(datsize, sect->align);
sect->vaddr = datsize;
- linklookup(ctxt, "data", 0)->sect = sect;
- linklookup(ctxt, "edata", 0)->sect = sect;
- gcdata = linklookup(ctxt, "gcdata", 0);
+ linklookup(ctxt, "runtime.data", 0)->sect = sect;
+ linklookup(ctxt, "runtime.edata", 0)->sect = sect;
+ gcdata = linklookup(ctxt, "runtime.gcdata", 0);
proggeninit(&gen, gcdata);
for(; s != nil && s->type < SBSS; s = s->next) {
if(s->type == SINITARR) {
@@ -1040,9 +1044,9 @@ dodata(void)
sect->align = maxalign(s, SNOPTRBSS-1);
datsize = rnd(datsize, sect->align);
sect->vaddr = datsize;
- linklookup(ctxt, "bss", 0)->sect = sect;
- linklookup(ctxt, "ebss", 0)->sect = sect;
- gcbss = linklookup(ctxt, "gcbss", 0);
+ linklookup(ctxt, "runtime.bss", 0)->sect = sect;
+ linklookup(ctxt, "runtime.ebss", 0)->sect = sect;
+ gcbss = linklookup(ctxt, "runtime.gcbss", 0);
proggeninit(&gen, gcbss);
for(; s != nil && s->type < SNOPTRBSS; s = s->next) {
s->sect = sect;
@@ -1059,8 +1063,8 @@ dodata(void)
sect->align = maxalign(s, SNOPTRBSS);
datsize = rnd(datsize, sect->align);
sect->vaddr = datsize;
- linklookup(ctxt, "noptrbss", 0)->sect = sect;
- linklookup(ctxt, "enoptrbss", 0)->sect = sect;
+ linklookup(ctxt, "runtime.noptrbss", 0)->sect = sect;
+ linklookup(ctxt, "runtime.enoptrbss", 0)->sect = sect;
for(; s != nil && s->type == SNOPTRBSS; s = s->next) {
datsize = aligndatsize(datsize, s);
s->sect = sect;
@@ -1068,7 +1072,7 @@ dodata(void)
growdatsize(&datsize, s);
}
sect->len = datsize - sect->vaddr;
- linklookup(ctxt, "end", 0)->sect = sect;
+ linklookup(ctxt, "runtime.end", 0)->sect = sect;
// 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits.
if(datsize != (uint32)datsize) {
@@ -1139,8 +1143,8 @@ dodata(void)
sect->align = maxalign(s, STYPELINK-1);
datsize = rnd(datsize, sect->align);
sect->vaddr = 0;
- linklookup(ctxt, "rodata", 0)->sect = sect;
- linklookup(ctxt, "erodata", 0)->sect = sect;
+ linklookup(ctxt, "runtime.rodata", 0)->sect = sect;
+ linklookup(ctxt, "runtime.erodata", 0)->sect = sect;
for(; s != nil && s->type < STYPELINK; s = s->next) {
datsize = aligndatsize(datsize, s);
s->sect = sect;
@@ -1155,8 +1159,8 @@ dodata(void)
sect->align = maxalign(s, STYPELINK);
datsize = rnd(datsize, sect->align);
sect->vaddr = datsize;
- linklookup(ctxt, "typelink", 0)->sect = sect;
- linklookup(ctxt, "etypelink", 0)->sect = sect;
+ linklookup(ctxt, "runtime.typelink", 0)->sect = sect;
+ linklookup(ctxt, "runtime.etypelink", 0)->sect = sect;
for(; s != nil && s->type == STYPELINK; s = s->next) {
datsize = aligndatsize(datsize, s);
s->sect = sect;
@@ -1171,8 +1175,8 @@ dodata(void)
sect->align = maxalign(s, SPCLNTAB-1);
datsize = rnd(datsize, sect->align);
sect->vaddr = datsize;
- linklookup(ctxt, "symtab", 0)->sect = sect;
- linklookup(ctxt, "esymtab", 0)->sect = sect;
+ linklookup(ctxt, "runtime.symtab", 0)->sect = sect;
+ linklookup(ctxt, "runtime.esymtab", 0)->sect = sect;
for(; s != nil && s->type < SPCLNTAB; s = s->next) {
datsize = aligndatsize(datsize, s);
s->sect = sect;
@@ -1187,8 +1191,8 @@ dodata(void)
sect->align = maxalign(s, SELFROSECT-1);
datsize = rnd(datsize, sect->align);
sect->vaddr = datsize;
- linklookup(ctxt, "pclntab", 0)->sect = sect;
- linklookup(ctxt, "epclntab", 0)->sect = sect;
+ linklookup(ctxt, "runtime.pclntab", 0)->sect = sect;
+ linklookup(ctxt, "runtime.epclntab", 0)->sect = sect;
for(; s != nil && s->type < SELFROSECT; s = s->next) {
datsize = aligndatsize(datsize, s);
s->sect = sect;
@@ -1241,8 +1245,8 @@ textaddress(void)
// and then letting threads copy down, but probably not worth it.
sect = segtext.sect;
sect->align = funcalign;
- linklookup(ctxt, "text", 0)->sect = sect;
- linklookup(ctxt, "etext", 0)->sect = sect;
+ linklookup(ctxt, "runtime.text", 0)->sect = sect;
+ linklookup(ctxt, "runtime.etext", 0)->sect = sect;
va = INITTEXT;
sect->vaddr = va;
for(sym = ctxt->textp; sym != nil; sym = sym->next) {
@@ -1353,32 +1357,32 @@ address(void)
sub->value += sym->value;
}
- xdefine("text", STEXT, text->vaddr);
- xdefine("etext", STEXT, text->vaddr + text->len);
- xdefine("rodata", SRODATA, rodata->vaddr);
- xdefine("erodata", SRODATA, rodata->vaddr + rodata->len);
- xdefine("typelink", SRODATA, typelink->vaddr);
- xdefine("etypelink", SRODATA, typelink->vaddr + typelink->len);
-
- sym = linklookup(ctxt, "gcdata", 0);
- xdefine("egcdata", SRODATA, symaddr(sym) + sym->size);
- linklookup(ctxt, "egcdata", 0)->sect = sym->sect;
-
- sym = linklookup(ctxt, "gcbss", 0);
- xdefine("egcbss", SRODATA, symaddr(sym) + sym->size);
- linklookup(ctxt, "egcbss", 0)->sect = sym->sect;
-
- xdefine("symtab", SRODATA, symtab->vaddr);
- xdefine("esymtab", SRODATA, symtab->vaddr + symtab->len);
- xdefine("pclntab", SRODATA, pclntab->vaddr);
- xdefine("epclntab", SRODATA, pclntab->vaddr + pclntab->len);
- xdefine("noptrdata", SNOPTRDATA, noptr->vaddr);
- xdefine("enoptrdata", SNOPTRDATA, noptr->vaddr + noptr->len);
- xdefine("bss", SBSS, bss->vaddr);
- xdefine("ebss", SBSS, bss->vaddr + bss->len);
- xdefine("data", SDATA, data->vaddr);
- xdefine("edata", SDATA, data->vaddr + data->len);
- xdefine("noptrbss", SNOPTRBSS, noptrbss->vaddr);
- xdefine("enoptrbss", SNOPTRBSS, noptrbss->vaddr + noptrbss->len);
- xdefine("end", SBSS, segdata.vaddr + segdata.len);
+ xdefine("runtime.text", STEXT, text->vaddr);
+ xdefine("runtime.etext", STEXT, text->vaddr + text->len);
+ xdefine("runtime.rodata", SRODATA, rodata->vaddr);
+ xdefine("runtime.erodata", SRODATA, rodata->vaddr + rodata->len);
+ xdefine("runtime.typelink", SRODATA, typelink->vaddr);
+ xdefine("runtime.etypelink", SRODATA, typelink->vaddr + typelink->len);
+
+ sym = linklookup(ctxt, "runtime.gcdata", 0);
+ xdefine("runtime.egcdata", SRODATA, symaddr(sym) + sym->size);
+ linklookup(ctxt, "runtime.egcdata", 0)->sect = sym->sect;
+
+ sym = linklookup(ctxt, "runtime.gcbss", 0);
+ xdefine("runtime.egcbss", SRODATA, symaddr(sym) + sym->size);
+ linklookup(ctxt, "runtime.egcbss", 0)->sect = sym->sect;
+
+ xdefine("runtime.symtab", SRODATA, symtab->vaddr);
+ xdefine("runtime.esymtab", SRODATA, symtab->vaddr + symtab->len);
+ xdefine("runtime.pclntab", SRODATA, pclntab->vaddr);
+ xdefine("runtime.epclntab", SRODATA, pclntab->vaddr + pclntab->len);
+ xdefine("runtime.noptrdata", SNOPTRDATA, noptr->vaddr);
+ xdefine("runtime.enoptrdata", SNOPTRDATA, noptr->vaddr + noptr->len);
+ xdefine("runtime.bss", SBSS, bss->vaddr);
+ xdefine("runtime.ebss", SBSS, bss->vaddr + bss->len);
+ xdefine("runtime.data", SDATA, data->vaddr);
+ xdefine("runtime.edata", SDATA, data->vaddr + data->len);
+ xdefine("runtime.noptrbss", SNOPTRBSS, noptrbss->vaddr);
+ xdefine("runtime.enoptrbss", SNOPTRBSS, noptrbss->vaddr + noptrbss->len);
+ xdefine("runtime.end", SBSS, segdata.vaddr + segdata.len);
}
diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c
index 5db41f9a71..7b997464de 100644
--- a/src/cmd/ld/lib.c
+++ b/src/cmd/ld/lib.c
@@ -1351,10 +1351,10 @@ genasmsym(void (*put)(LSym*, char*, int, vlong, vlong, int, LSym*))
// These symbols won't show up in the first loop below because we
// skip STEXT symbols. Normal STEXT symbols are emitted by walking textp.
- s = linklookup(ctxt, "text", 0);
+ s = linklookup(ctxt, "runtime.text", 0);
if(s->type == STEXT)
put(s, s->name, 'T', s->value, s->size, s->version, 0);
- s = linklookup(ctxt, "etext", 0);
+ s = linklookup(ctxt, "runtime.etext", 0);
if(s->type == STEXT)
put(s, s->name, 'T', s->value, s->size, s->version, 0);
@@ -1556,3 +1556,56 @@ diag(char *fmt, ...)
errorexit();
}
}
+
+void
+checkgo(void)
+{
+ LSym *s;
+ Reloc *r;
+ int i;
+ int changed;
+
+ if(!debug['C'])
+ return;
+
+ // TODO(rsc,khr): Eventually we want to get to no Go-called C functions at all,
+ // which would simplify this logic quite a bit.
+
+ // Mark every Go-called C function with cfunc=2, recursively.
+ do {
+ changed = 0;
+ for(s = ctxt->textp; s != nil; s = s->next) {
+ if(s->cfunc == 0 || (s->cfunc == 2 && s->nosplit)) {
+ for(i=0; i<s->nr; i++) {
+ r = &s->r[i];
+ if(r->sym == nil)
+ continue;
+ if((r->type == R_CALL || r->type == R_CALLARM) && r->sym->type == STEXT) {
+ if(r->sym->cfunc == 1) {
+ changed = 1;
+ r->sym->cfunc = 2;
+ }
+ }
+ }
+ }
+ }
+ }while(changed);
+
+ // Complain about Go-called C functions that can split the stack
+ // (that can be preempted for garbage collection or trigger a stack copy).
+ for(s = ctxt->textp; s != nil; s = s->next) {
+ if(s->cfunc == 0 || (s->cfunc == 2 && s->nosplit)) {
+ for(i=0; i<s->nr; i++) {
+ r = &s->r[i];
+ if(r->sym == nil)
+ continue;
+ if((r->type == R_CALL || r->type == R_CALLARM) && r->sym->type == STEXT) {
+ if(s->cfunc == 0 && r->sym->cfunc == 2 && !r->sym->nosplit)
+ print("Go %s calls C %s\n", s->name, r->sym->name);
+ else if(s->cfunc == 2 && s->nosplit && !r->sym->nosplit)
+ print("Go calls C %s calls %s\n", s->name, r->sym->name);
+ }
+ }
+ }
+ }
+}
diff --git a/src/cmd/ld/lib.h b/src/cmd/ld/lib.h
index 4094dfa6b1..067ffa0bcc 100644
--- a/src/cmd/ld/lib.h
+++ b/src/cmd/ld/lib.h
@@ -183,12 +183,13 @@ uint16 be16(uchar *b);
uint32 be32(uchar *b);
uint64 be64(uchar *b);
void callgraph(void);
+void checkgo(void);
void cflush(void);
-void codeblk(int32 addr, int32 size);
+void codeblk(int64 addr, int64 size);
vlong cpos(void);
void cseek(vlong p);
void cwrite(void *buf, int n);
-void datblk(int32 addr, int32 size);
+void datblk(int64 addr, int64 size);
int datcmp(LSym *s1, LSym *s2);
vlong datoff(vlong addr);
void deadcode(void);
diff --git a/src/cmd/ld/pcln.c b/src/cmd/ld/pcln.c
index 4c2ffa78e1..3cd9e65dad 100644
--- a/src/cmd/ld/pcln.c
+++ b/src/cmd/ld/pcln.c
@@ -90,7 +90,7 @@ renumberfiles(Link *ctxt, LSym **files, int nfiles, Pcdata *d)
}
dv = val - newval;
newval = val;
- v = (uint32)(dv<<1) ^ (uint32)(int32)(dv>>31);
+ v = ((uint32)dv<<1) ^ (uint32)(int32)(dv>>31);
addvarint(&out, v);
// pc delta
@@ -119,7 +119,7 @@ pclntab(void)
static Pcln zpcln;
funcdata_bytes = 0;
- ftab = linklookup(ctxt, "pclntab", 0);
+ ftab = linklookup(ctxt, "runtime.pclntab", 0);
ftab->type = SPCLNTAB;
ftab->reachable = 1;
diff --git a/src/cmd/ld/pobj.c b/src/cmd/ld/pobj.c
index d78dacd368..54c5ef2472 100644
--- a/src/cmd/ld/pobj.c
+++ b/src/cmd/ld/pobj.c
@@ -71,6 +71,7 @@ main(int argc, char *argv[])
if(thechar == '6')
flagcount("8", "assume 64-bit addresses", &debug['8']);
flagfn1("B", "info: define ELF NT_GNU_BUILD_ID note", addbuildinfo);
+ flagcount("C", "check Go calls to C code", &debug['C']);
flagint64("D", "addr: data address", &INITDAT);
flagstr("E", "sym: entry symbol", &INITENTRY);
if(thechar == '5')
@@ -162,6 +163,7 @@ main(int argc, char *argv[])
mark(linklookup(ctxt, "runtime.read_tls_fallback", 0));
}
+ checkgo();
deadcode();
callgraph();
paramspace = "SP"; /* (FP) now (SP) on output */
diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c
index 8149a67167..c3a72c3cf6 100644
--- a/src/cmd/ld/symtab.c
+++ b/src/cmd/ld/symtab.c
@@ -347,36 +347,36 @@ symtab(void)
// Define these so that they'll get put into the symbol table.
// data.c:/^address will provide the actual values.
- xdefine("text", STEXT, 0);
- xdefine("etext", STEXT, 0);
- xdefine("typelink", SRODATA, 0);
- xdefine("etypelink", SRODATA, 0);
- xdefine("rodata", SRODATA, 0);
- xdefine("erodata", SRODATA, 0);
- xdefine("noptrdata", SNOPTRDATA, 0);
- xdefine("enoptrdata", SNOPTRDATA, 0);
- xdefine("data", SDATA, 0);
- xdefine("edata", SDATA, 0);
- xdefine("bss", SBSS, 0);
- xdefine("ebss", SBSS, 0);
- xdefine("noptrbss", SNOPTRBSS, 0);
- xdefine("enoptrbss", SNOPTRBSS, 0);
- xdefine("end", SBSS, 0);
- xdefine("epclntab", SRODATA, 0);
- xdefine("esymtab", SRODATA, 0);
+ xdefine("runtime.text", STEXT, 0);
+ xdefine("runtime.etext", STEXT, 0);
+ xdefine("runtime.typelink", SRODATA, 0);
+ xdefine("runtime.etypelink", SRODATA, 0);
+ xdefine("runtime.rodata", SRODATA, 0);
+ xdefine("runtime.erodata", SRODATA, 0);
+ xdefine("runtime.noptrdata", SNOPTRDATA, 0);
+ xdefine("runtime.enoptrdata", SNOPTRDATA, 0);
+ xdefine("runtime.data", SDATA, 0);
+ xdefine("runtime.edata", SDATA, 0);
+ xdefine("runtime.bss", SBSS, 0);
+ xdefine("runtime.ebss", SBSS, 0);
+ xdefine("runtime.noptrbss", SNOPTRBSS, 0);
+ xdefine("runtime.enoptrbss", SNOPTRBSS, 0);
+ xdefine("runtime.end", SBSS, 0);
+ xdefine("runtime.epclntab", SRODATA, 0);
+ xdefine("runtime.esymtab", SRODATA, 0);
// garbage collection symbols
- s = linklookup(ctxt, "gcdata", 0);
+ s = linklookup(ctxt, "runtime.gcdata", 0);
s->type = SRODATA;
s->size = 0;
s->reachable = 1;
- xdefine("egcdata", SRODATA, 0);
+ xdefine("runtime.egcdata", SRODATA, 0);
- s = linklookup(ctxt, "gcbss", 0);
+ s = linklookup(ctxt, "runtime.gcbss", 0);
s->type = SRODATA;
s->size = 0;
s->reachable = 1;
- xdefine("egcbss", SRODATA, 0);
+ xdefine("runtime.egcbss", SRODATA, 0);
// pseudo-symbols to mark locations of type, string, and go string data.
s = linklookup(ctxt, "type.*", 0);
@@ -397,9 +397,9 @@ symtab(void)
s->reachable = 1;
symgofunc = s;
- symtypelink = linklookup(ctxt, "typelink", 0);
+ symtypelink = linklookup(ctxt, "runtime.typelink", 0);
- symt = linklookup(ctxt, "symtab", 0);
+ symt = linklookup(ctxt, "runtime.symtab", 0);
symt->type = SSYMTAB;
symt->size = 0;
symt->reachable = 1;
diff --git a/src/cmd/link/auto.go b/src/cmd/link/auto.go
index 8f0c39f8c0..f9228e8cab 100644
--- a/src/cmd/link/auto.go
+++ b/src/cmd/link/auto.go
@@ -18,26 +18,26 @@ import (
// linkerDefined lists the symbols supplied by other parts of the linker
// (runtime.go and layout.go).
var linkerDefined = map[string]bool{
- "bss": true,
- "data": true,
- "ebss": true,
- "edata": true,
- "efunctab": true,
- "end": true,
- "enoptrbss": true,
- "enoptrdata": true,
- "erodata": true,
- "etext": true,
- "etypelink": true,
- "functab": true,
- "gcbss": true,
- "gcdata": true,
- "noptrbss": true,
- "noptrdata": true,
- "pclntab": true,
- "rodata": true,
- "text": true,
- "typelink": true,
+ "runtime.bss": true,
+ "runtime.data": true,
+ "runtime.ebss": true,
+ "runtime.edata": true,
+ "runtime.efunctab": true,
+ "runtime.end": true,
+ "runtime.enoptrbss": true,
+ "runtime.enoptrdata": true,
+ "runtime.erodata": true,
+ "runtime.etext": true,
+ "runtime.etypelink": true,
+ "runtime.functab": true,
+ "runtime.gcbss": true,
+ "runtime.gcdata": true,
+ "runtime.noptrbss": true,
+ "runtime.noptrdata": true,
+ "runtime.pclntab": true,
+ "runtime.rodata": true,
+ "runtime.text": true,
+ "runtime.typelink": true,
}
// isAuto reports whether sym is an automatically-generated data or constant symbol.
diff --git a/src/cmd/link/layout.go b/src/cmd/link/layout.go
index 6477022444..149ebced0f 100644
--- a/src/cmd/link/layout.go
+++ b/src/cmd/link/layout.go
@@ -172,9 +172,9 @@ func (p *Prog) layout() {
start = sect.VirtAddr
end = sect.VirtAddr + sect.Size
}
- p.defineConst(name, start)
- p.defineConst("e"+name, end)
+ p.defineConst("runtime."+name, start)
+ p.defineConst("runtime.e"+name, end)
progEnd = end
}
- p.defineConst("end", progEnd)
+ p.defineConst("runtime.end", progEnd)
}
diff --git a/src/cmd/link/pclntab.go b/src/cmd/link/pclntab.go
index b0b19ad53c..232d586bf2 100644
--- a/src/cmd/link/pclntab.go
+++ b/src/cmd/link/pclntab.go
@@ -183,7 +183,7 @@ func (p *Prog) pclntab() {
pclntab := &Sym{
Sym: &goobj.Sym{
- SymID: goobj.SymID{Name: "pclntab"},
+ SymID: goobj.SymID{Name: "runtime.pclntab"},
Kind: goobj.SPCLNTAB,
Size: buf.Size(),
Reloc: buf.Reloc(),
@@ -437,7 +437,7 @@ func (it *PCIter) Next() {
return
}
it.start = false
- sv := int32(uv)>>1 ^ int32(uv)<<31>>31
+ sv := int32(uv>>1) ^ int32(uv<<31)>>31
it.Value += sv
// pc delta
diff --git a/src/cmd/link/pclntab_test.go b/src/cmd/link/pclntab_test.go
index 5696a0978d..19953f5797 100644
--- a/src/cmd/link/pclntab_test.go
+++ b/src/cmd/link/pclntab_test.go
@@ -141,7 +141,7 @@ func TestPclntab(t *testing.T) {
// It returns a symbol reader for pclntab, the offset of the function information
// within that symbol, and the args and frame values read out of the information.
func findFunc(t *testing.T, p *Prog, name string) (r *SymReader, off, args, frame int, ok bool) {
- tabsym := p.Syms[goobj.SymID{Name: "pclntab"}]
+ tabsym := p.Syms[goobj.SymID{Name: "runtime.pclntab"}]
if tabsym == nil {
t.Errorf("pclntab is missing in binary")
return
@@ -276,6 +276,12 @@ func checkPCData(t *testing.T, r *SymReader, name string, off, pc, pnum, val int
// readPCData reads the PCData table offset off
// to obtain and return the value associated with pc.
func readPCData(t *testing.T, r *SymReader, name, pcdataname string, pcoff uint32, pc int) (int, bool) {
+ // "If pcsp, pcfile, pcln, or any of the pcdata offsets is zero,
+ // that table is considered missing, and all PCs take value -1."
+ if pcoff == 0 {
+ return -1, true
+ }
+
var it PCIter
for it.Init(r.p, r.data[pcoff:]); !it.Done; it.Next() {
if it.PC <= uint32(pc) && uint32(pc) < it.NextPC {
diff --git a/src/cmd/link/runtime.go b/src/cmd/link/runtime.go
index 5d37015eb1..b0c1ac98a6 100644
--- a/src/cmd/link/runtime.go
+++ b/src/cmd/link/runtime.go
@@ -15,13 +15,13 @@ func (p *Prog) runtime() {
// TODO: Implement garbage collection data.
p.addSym(&Sym{
Sym: &goobj.Sym{
- SymID: goobj.SymID{Name: "gcdata"},
+ SymID: goobj.SymID{Name: "runtime.gcdata"},
Kind: goobj.SRODATA,
},
})
p.addSym(&Sym{
Sym: &goobj.Sym{
- SymID: goobj.SymID{Name: "gcbss"},
+ SymID: goobj.SymID{Name: "runtime.gcbss"},
Kind: goobj.SRODATA,
},
})
diff --git a/src/cmd/link/testdata/Makefile b/src/cmd/link/testdata/Makefile
index 3b1b15f731..e9651a03f6 100644
--- a/src/cmd/link/testdata/Makefile
+++ b/src/cmd/link/testdata/Makefile
@@ -9,7 +9,7 @@ ALL=\
all: $(ALL)
%.6: %.s
- go tool 6a $*.s
+ GOARCH=amd64 GOOS=darwin go tool 6a -trimpath=$(shell pwd) $*.s
pclntab.s: genpcln.go
go run genpcln.go >pclntab.s
diff --git a/src/cmd/link/testdata/autosection.6 b/src/cmd/link/testdata/autosection.6
index bc9d446e1a..386f422cf3 100644
--- a/src/cmd/link/testdata/autosection.6
+++ b/src/cmd/link/testdata/autosection.6
Binary files differ
diff --git a/src/cmd/link/testdata/autosection.s b/src/cmd/link/testdata/autosection.s
index 8a579b7027..e0cb21723e 100644
--- a/src/cmd/link/testdata/autosection.s
+++ b/src/cmd/link/testdata/autosection.s
@@ -4,7 +4,7 @@
// Test of section-named symbols.
-#include "../../ld/textflag.h"
+#include "textflag.h"
TEXT start(SB),7,$0
MOVQ $autotab(SB),AX
@@ -16,37 +16,37 @@ GLOBL zero(SB), $8
GLOBL zeronoptr(SB), NOPTR, $16
// text
-DATA autotab+0x00(SB)/8, $text(SB)
+DATA autotab+0x00(SB)/8, $runtime·text(SB)
DATA autotab+0x08(SB)/8, $start(SB)
-DATA autotab+0x10(SB)/8, $etext(SB)
+DATA autotab+0x10(SB)/8, $runtime·etext(SB)
DATA autotab+0x18(SB)/8, $start+16(SB)
// data
-DATA autotab+0x20(SB)/8, $data(SB)
+DATA autotab+0x20(SB)/8, $runtime·data(SB)
DATA autotab+0x28(SB)/8, $autotab(SB)
-DATA autotab+0x30(SB)/8, $edata(SB)
+DATA autotab+0x30(SB)/8, $runtime·edata(SB)
DATA autotab+0x38(SB)/8, $nonzero+4(SB)
// bss
-DATA autotab+0x40(SB)/8, $bss(SB)
+DATA autotab+0x40(SB)/8, $runtime·bss(SB)
DATA autotab+0x48(SB)/8, $zero(SB)
-DATA autotab+0x50(SB)/8, $ebss(SB)
+DATA autotab+0x50(SB)/8, $runtime·ebss(SB)
DATA autotab+0x58(SB)/8, $zero+8(SB)
// noptrdata
-DATA autotab+0x60(SB)/8, $noptrdata(SB)
+DATA autotab+0x60(SB)/8, $runtime·noptrdata(SB)
DATA autotab+0x68(SB)/8, $nonzeronoptr(SB)
-DATA autotab+0x70(SB)/8, $enoptrdata(SB)
+DATA autotab+0x70(SB)/8, $runtime·enoptrdata(SB)
DATA autotab+0x78(SB)/8, $nonzeronoptr+8(SB)
// noptrbss
-DATA autotab+0x80(SB)/8, $noptrbss(SB)
+DATA autotab+0x80(SB)/8, $runtime·noptrbss(SB)
DATA autotab+0x88(SB)/8, $zeronoptr(SB)
-DATA autotab+0x90(SB)/8, $enoptrbss(SB)
+DATA autotab+0x90(SB)/8, $runtime·enoptrbss(SB)
DATA autotab+0x98(SB)/8, $zeronoptr+16(SB)
// end
-DATA autotab+0xa0(SB)/8, $end(SB)
+DATA autotab+0xa0(SB)/8, $runtime·end(SB)
DATA autotab+0xa8(SB)/8, $zeronoptr+16(SB)
GLOBL autotab(SB), $0xb0
diff --git a/src/cmd/link/testdata/autoweak.6 b/src/cmd/link/testdata/autoweak.6
index 636a4d8446..5d74d4e2b5 100644
--- a/src/cmd/link/testdata/autoweak.6
+++ b/src/cmd/link/testdata/autoweak.6
Binary files differ
diff --git a/src/cmd/link/testdata/dead.6 b/src/cmd/link/testdata/dead.6
index bb77aafe86..9540adc1af 100644
--- a/src/cmd/link/testdata/dead.6
+++ b/src/cmd/link/testdata/dead.6
Binary files differ
diff --git a/src/cmd/link/testdata/dead.s b/src/cmd/link/testdata/dead.s
index 832ddaff60..86f31360fa 100644
--- a/src/cmd/link/testdata/dead.s
+++ b/src/cmd/link/testdata/dead.s
@@ -17,7 +17,7 @@ TEXT text1(SB),7,$0
RET
TEXT text2(SB),7,$0
- MOVQ $edata(SB),BX
+ MOVQ $runtime·edata(SB),BX
RET
DATA data1<>+0(SB)/8, $data2(SB)
@@ -46,4 +46,3 @@ GLOBL dead_data1(SB), $16
GLOBL dead_data2(SB), $1
GLOBL dead_data3(SB), $1
GLOBL dead_funcdata(SB), $8
-
diff --git a/src/cmd/link/testdata/genpcln.go b/src/cmd/link/testdata/genpcln.go
index 684cc07a27..c10eaeae91 100644
--- a/src/cmd/link/testdata/genpcln.go
+++ b/src/cmd/link/testdata/genpcln.go
@@ -107,6 +107,6 @@ func main() {
for f := 0; f < 3; f++ {
fmt.Printf("\tCALL func%d(SB)\n", f)
}
- fmt.Printf("\tMOVQ $pclntab(SB), AX\n")
+ fmt.Printf("\tMOVQ $runtime·pclntab(SB), AX\n")
fmt.Printf("\n\tRET\n")
}
diff --git a/src/cmd/link/testdata/hello.6 b/src/cmd/link/testdata/hello.6
index b129dc0299..67983f1a9f 100644
--- a/src/cmd/link/testdata/hello.6
+++ b/src/cmd/link/testdata/hello.6
Binary files differ
diff --git a/src/cmd/link/testdata/layout.6 b/src/cmd/link/testdata/layout.6
index 8cd5bd2cdf..db24ef3e55 100644
--- a/src/cmd/link/testdata/layout.6
+++ b/src/cmd/link/testdata/layout.6
Binary files differ
diff --git a/src/cmd/link/testdata/layout.s b/src/cmd/link/testdata/layout.s
index 0d492c5af2..c3e55ef49a 100644
--- a/src/cmd/link/testdata/layout.s
+++ b/src/cmd/link/testdata/layout.s
@@ -5,7 +5,7 @@
// Test of section assignment in layout.go.
// Each symbol should end up in the section named by the symbol name prefix (up to the underscore).
-#include "../../ld/textflag.h"
+#include "textflag.h"
TEXT text_start(SB),7,$0
MOVQ $rodata_sym(SB), AX
diff --git a/src/cmd/link/testdata/link.hello.darwin.amd64 b/src/cmd/link/testdata/link.hello.darwin.amd64
index b1f0a93b21..0bd475dd81 100644
--- a/src/cmd/link/testdata/link.hello.darwin.amd64
+++ b/src/cmd/link/testdata/link.hello.darwin.amd64
@@ -6,8 +6,8 @@
*
00000060 00 00 00 00 00 00 00 00 19 00 00 00 38 01 00 00 |............8...|
00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
-00000080 00 10 00 00 00 00 00 00 d6 10 00 00 00 00 00 00 |................|
-00000090 00 00 00 00 00 00 00 00 d6 10 00 00 00 00 00 00 |................|
+00000080 00 10 00 00 00 00 00 00 b0 10 00 00 00 00 00 00 |................|
+00000090 00 00 00 00 00 00 00 00 b0 10 00 00 00 00 00 00 |................|
000000a0 07 00 00 00 05 00 00 00 03 00 00 00 00 00 00 00 |................|
000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........|
000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
@@ -21,7 +21,7 @@
*
00000150 5f 5f 66 75 6e 63 74 61 62 00 00 00 00 00 00 00 |__functab.......|
00000160 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
-00000170 20 20 00 00 00 00 00 00 b6 00 00 00 00 00 00 00 | ..............|
+00000170 20 20 00 00 00 00 00 00 90 00 00 00 00 00 00 00 | ..............|
00000180 20 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ...............|
*
000001a0 19 00 00 00 98 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..|
@@ -48,10 +48,7 @@
00001070 00 00 00 00 00 00 00 00 5f 72 74 30 5f 67 6f 00 |........_rt0_go.|
00001080 02 20 00 04 20 00 06 05 02 05 02 05 02 05 02 02 |. .. ...........|
00001090 02 02 02 05 02 02 02 01 00 00 00 00 00 00 00 00 |................|
-000010a0 02 00 00 00 88 00 00 00 2f 55 73 65 72 73 2f 72 |......../Users/r|
-000010b0 73 63 2f 67 2f 67 6f 2f 73 72 63 2f 63 6d 64 2f |sc/g/go/src/cmd/|
-000010c0 6c 69 6e 6b 2f 74 65 73 74 64 61 74 61 2f 68 65 |link/testdata/he|
-000010d0 6c 6c 6f 2e 73 00 00 00 00 00 00 00 00 00 00 00 |llo.s...........|
+000010a0 02 00 00 00 88 00 00 00 68 65 6c 6c 6f 2e 73 00 |........hello.s.|
*
00002000 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a |hello world.|
0000200c
diff --git a/src/cmd/link/testdata/pclntab.6 b/src/cmd/link/testdata/pclntab.6
index 918411ca5b..9e7f9afdb0 100644
--- a/src/cmd/link/testdata/pclntab.6
+++ b/src/cmd/link/testdata/pclntab.6
Binary files differ
diff --git a/src/cmd/link/testdata/pclntab.s b/src/cmd/link/testdata/pclntab.s
index 22c4ee0dec..12dac70b0c 100644
--- a/src/cmd/link/testdata/pclntab.s
+++ b/src/cmd/link/testdata/pclntab.s
@@ -1746,6 +1746,6 @@ TEXT start(SB),7,$0
CALL func0(SB)
CALL func1(SB)
CALL func2(SB)
- MOVQ $pclntab(SB), AX
+ MOVQ $runtime·pclntab(SB), AX
RET
diff --git a/src/cmd/nm/nm.go b/src/cmd/nm/nm.go
index a4036184e4..3089e481be 100644
--- a/src/cmd/nm/nm.go
+++ b/src/cmd/nm/nm.go
@@ -6,13 +6,13 @@ package main
import (
"bufio"
- "bytes"
"flag"
"fmt"
- "io"
"log"
"os"
"sort"
+
+ "cmd/internal/objfile"
)
func usage() {
@@ -85,55 +85,22 @@ func errorf(format string, args ...interface{}) {
exitCode = 1
}
-type Sym struct {
- Addr uint64
- Size int64
- Code rune
- Name string
- Type string
-}
-
-var parsers = []struct {
- prefix []byte
- parse func(*os.File) []Sym
-}{
- {[]byte("!<arch>\n"), goobjSymbols},
- {[]byte("go object "), goobjSymbols},
- {[]byte("\x7FELF"), elfSymbols},
- {[]byte("\xFE\xED\xFA\xCE"), machoSymbols},
- {[]byte("\xFE\xED\xFA\xCF"), machoSymbols},
- {[]byte("\xCE\xFA\xED\xFE"), machoSymbols},
- {[]byte("\xCF\xFA\xED\xFE"), machoSymbols},
- {[]byte("MZ"), peSymbols},
- {[]byte("\x00\x00\x01\xEB"), plan9Symbols}, // 386
- {[]byte("\x00\x00\x04\x07"), plan9Symbols}, // mips
- {[]byte("\x00\x00\x06\x47"), plan9Symbols}, // arm
- {[]byte("\x00\x00\x8A\x97"), plan9Symbols}, // amd64
-}
-
func nm(file string) {
- f, err := os.Open(file)
+ f, err := objfile.Open(file)
if err != nil {
errorf("%v", err)
return
}
defer f.Close()
- buf := make([]byte, 16)
- io.ReadFull(f, buf)
- f.Seek(0, 0)
-
- var syms []Sym
- for _, p := range parsers {
- if bytes.HasPrefix(buf, p.prefix) {
- syms = p.parse(f)
- goto HaveSyms
- }
+ syms, err := f.Symbols()
+ if err != nil {
+ errorf("reading %s: %v", file, err)
+ }
+ if len(syms) == 0 {
+ errorf("reading %s: no symbols", file)
}
- errorf("%v: unknown file format", file)
- return
-HaveSyms:
switch *sortOrder {
case "address":
sort.Sort(byAddr(syms))
@@ -165,19 +132,19 @@ HaveSyms:
w.Flush()
}
-type byAddr []Sym
+type byAddr []objfile.Sym
func (x byAddr) Len() int { return len(x) }
func (x byAddr) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x byAddr) Less(i, j int) bool { return x[i].Addr < x[j].Addr }
-type byName []Sym
+type byName []objfile.Sym
func (x byName) Len() int { return len(x) }
func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x byName) Less(i, j int) bool { return x[i].Name < x[j].Name }
-type bySize []Sym
+type bySize []objfile.Sym
func (x bySize) Len() int { return len(x) }
func (x bySize) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
diff --git a/src/cmd/nm/nm_test.go b/src/cmd/nm/nm_test.go
index 74773877f3..f447e8e491 100644
--- a/src/cmd/nm/nm_test.go
+++ b/src/cmd/nm/nm_test.go
@@ -77,7 +77,7 @@ func TestNM(t *testing.T) {
"elf/testdata/gcc-amd64-linux-exec",
"macho/testdata/gcc-386-darwin-exec",
"macho/testdata/gcc-amd64-darwin-exec",
- "pe/testdata/gcc-amd64-mingw-exec",
+ // "pe/testdata/gcc-amd64-mingw-exec", // no symbols!
"pe/testdata/gcc-386-mingw-exec",
"plan9obj/testdata/amd64-plan9-exec",
"plan9obj/testdata/386-plan9-exec",
@@ -87,7 +87,7 @@ func TestNM(t *testing.T) {
cmd := exec.Command(testnmpath, exepath)
out, err := cmd.CombinedOutput()
if err != nil {
- t.Fatalf("go tool nm %v: %v\n%s", exepath, err, string(out))
+ t.Errorf("go tool nm %v: %v\n%s", exepath, err, string(out))
}
}
diff --git a/src/cmd/nm/pe.go b/src/cmd/nm/pe.go
deleted file mode 100644
index 52d05e51d0..0000000000
--- a/src/cmd/nm/pe.go
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Parsing of PE executables (Microsoft Windows).
-
-package main
-
-import (
- "debug/pe"
- "os"
- "sort"
-)
-
-func peSymbols(f *os.File) []Sym {
- p, err := pe.NewFile(f)
- if err != nil {
- errorf("parsing %s: %v", f.Name(), err)
- return nil
- }
-
- // Build sorted list of addresses of all symbols.
- // We infer the size of a symbol by looking at where the next symbol begins.
- var addrs []uint64
-
- var imageBase uint64
- switch oh := p.OptionalHeader.(type) {
- case *pe.OptionalHeader32:
- imageBase = uint64(oh.ImageBase)
- case *pe.OptionalHeader64:
- imageBase = oh.ImageBase
- default:
- errorf("parsing %s: file format not recognized", f.Name())
- return nil
- }
-
- var syms []Sym
- for _, s := range p.Symbols {
- const (
- N_UNDEF = 0 // An undefined (extern) symbol
- N_ABS = -1 // An absolute symbol (e_value is a constant, not an address)
- N_DEBUG = -2 // A debugging symbol
- )
- sym := Sym{Name: s.Name, Addr: uint64(s.Value), Code: '?'}
- switch s.SectionNumber {
- case N_UNDEF:
- sym.Code = 'U'
- case N_ABS:
- sym.Code = 'C'
- case N_DEBUG:
- sym.Code = '?'
- default:
- if s.SectionNumber < 0 {
- errorf("parsing %s: invalid section number %d", f.Name(), s.SectionNumber)
- return nil
- }
- if len(p.Sections) < int(s.SectionNumber) {
- errorf("parsing %s: section number %d is large then max %d", f.Name(), s.SectionNumber, len(p.Sections))
- return nil
- }
- sect := p.Sections[s.SectionNumber-1]
- const (
- text = 0x20
- data = 0x40
- bss = 0x80
- permX = 0x20000000
- permR = 0x40000000
- permW = 0x80000000
- )
- ch := sect.Characteristics
- switch {
- case ch&text != 0:
- sym.Code = 'T'
- case ch&data != 0:
- if ch&permW == 0 {
- sym.Code = 'R'
- } else {
- sym.Code = 'D'
- }
- case ch&bss != 0:
- sym.Code = 'B'
- }
- sym.Addr += imageBase + uint64(sect.VirtualAddress)
- }
- syms = append(syms, sym)
- addrs = append(addrs, sym.Addr)
- }
-
- sort.Sort(uint64s(addrs))
- for i := range syms {
- j := sort.Search(len(addrs), func(x int) bool { return addrs[x] > syms[i].Addr })
- if j < len(addrs) {
- syms[i].Size = int64(addrs[j] - syms[i].Addr)
- }
- }
-
- return syms
-}
diff --git a/src/cmd/nm/plan9obj.go b/src/cmd/nm/plan9obj.go
deleted file mode 100644
index 006c66ebfd..0000000000
--- a/src/cmd/nm/plan9obj.go
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Parsing of Plan 9 a.out executables.
-
-package main
-
-import (
- "debug/plan9obj"
- "os"
- "sort"
-)
-
-func plan9Symbols(f *os.File) []Sym {
- p, err := plan9obj.NewFile(f)
- if err != nil {
- errorf("parsing %s: %v", f.Name(), err)
- return nil
- }
-
- plan9Syms, err := p.Symbols()
- if err != nil {
- errorf("parsing %s: %v", f.Name(), err)
- return nil
- }
-
- // Build sorted list of addresses of all symbols.
- // We infer the size of a symbol by looking at where the next symbol begins.
- var addrs []uint64
- for _, s := range plan9Syms {
- addrs = append(addrs, s.Value)
- }
- sort.Sort(uint64s(addrs))
-
- var syms []Sym
-
- for _, s := range plan9Syms {
- sym := Sym{Addr: s.Value, Name: s.Name, Code: rune(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)
- }
- syms = append(syms, sym)
- }
-
- return syms
-}
diff --git a/src/cmd/objdump/main.go b/src/cmd/objdump/main.go
index 9922dcc023..1e4163296f 100644
--- a/src/cmd/objdump/main.go
+++ b/src/cmd/objdump/main.go
@@ -101,7 +101,7 @@ func main() {
keep := syms[:0]
for _, sym := range syms {
switch sym.Name {
- case "text", "_text", "etext", "_etext":
+ case "runtime.text", "text", "_text", "runtime.etext", "etext", "_etext":
// drop
default:
keep = append(keep, sym)
@@ -118,7 +118,7 @@ func main() {
i := sort.Search(len(syms), func(i int) bool { return syms[i].Addr > addr })
if i > 0 {
s := syms[i-1]
- if s.Addr <= addr && addr < s.Addr+uint64(s.Size) && s.Name != "etext" && s.Name != "_etext" {
+ if s.Addr <= addr && addr < s.Addr+uint64(s.Size) && s.Name != "runtime.etext" && s.Name != "etext" && s.Name != "_etext" {
return s.Name, s.Addr
}
}
@@ -355,11 +355,20 @@ func loadTables(f *os.File) (textStart uint64, textData, symtab, pclntab []byte,
textStart = imageBase + uint64(sect.VirtualAddress)
textData, _ = sect.Data()
}
- if pclntab, err = loadPETable(obj, "pclntab", "epclntab"); err != nil {
- return 0, nil, nil, nil, err
+ if pclntab, err = loadPETable(obj, "runtime.pclntab", "runtime.epclntab"); err != nil {
+ // We didn't find the symbols, so look for the names used in 1.3 and earlier.
+ // TODO: Remove code looking for the old symbols when we no longer care about 1.3.
+ var err2 error
+ if pclntab, err2 = loadPETable(obj, "pclntab", "epclntab"); err2 != nil {
+ return 0, nil, nil, nil, err
+ }
}
- if symtab, err = loadPETable(obj, "symtab", "esymtab"); err != nil {
- return 0, nil, nil, nil, err
+ if symtab, err = loadPETable(obj, "runtime.symtab", "runtime.esymtab"); err != nil {
+ // Same as above.
+ var err2 error
+ if symtab, err2 = loadPETable(obj, "symtab", "esymtab"); err2 != nil {
+ return 0, nil, nil, nil, err
+ }
}
return textStart, textData, symtab, pclntab, nil
}
@@ -369,11 +378,20 @@ func loadTables(f *os.File) (textStart uint64, textData, symtab, pclntab []byte,
if sect := obj.Section("text"); sect != nil {
textData, _ = sect.Data()
}
- if pclntab, err = loadPlan9Table(obj, "pclntab", "epclntab"); err != nil {
- return 0, nil, nil, nil, err
+ if pclntab, err = loadPlan9Table(obj, "runtime.pclntab", "runtime.epclntab"); err != nil {
+ // We didn't find the symbols, so look for the names used in 1.3 and earlier.
+ // TODO: Remove code looking for the old symbols when we no longer care about 1.3.
+ var err2 error
+ if pclntab, err2 = loadPlan9Table(obj, "pclntab", "epclntab"); err2 != nil {
+ return 0, nil, nil, nil, err
+ }
}
- if symtab, err = loadPlan9Table(obj, "symtab", "esymtab"); err != nil {
- return 0, nil, nil, nil, err
+ if symtab, err = loadPlan9Table(obj, "runtime.symtab", "runtime.esymtab"); err != nil {
+ // Same as above.
+ var err2 error
+ if symtab, err2 = loadPlan9Table(obj, "symtab", "esymtab"); err2 != nil {
+ return 0, nil, nil, nil, err
+ }
}
return textStart, textData, symtab, pclntab, nil
}
diff --git a/src/cmd/yacc/Makefile b/src/cmd/yacc/Makefile
deleted file mode 100644
index f8c8169bd1..0000000000
--- a/src/cmd/yacc/Makefile
+++ /dev/null
@@ -1,12 +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.
-
-TARG=expr$(shell go env GOEXE)
-
-$(TARG): yacc.go expr.y
- go run yacc.go -p expr expr.y
- go build -o $(TARG) y.go
-
-clean:
- rm -f y.go y.output $(TARG)
diff --git a/src/cmd/yacc/doc.go b/src/cmd/yacc/doc.go
index ceaaf2448d..702c9f0d25 100644
--- a/src/cmd/yacc/doc.go
+++ b/src/cmd/yacc/doc.go
@@ -20,8 +20,9 @@ written in C and documented at
Adepts of the original yacc will have no trouble adapting to this
form of the tool.
-The file expr.y in this directory is a yacc grammar for a very simple
-expression parser. It needs the flag "-p expr" (see below).
+The directory $GOROOT/cmd/yacc/testdata/expr is a yacc program
+for a very simple expression parser. See expr.y and main.go in that
+directory for examples of how to write and build yacc programs.
The generated parser is reentrant. Parse expects to be given an
argument that conforms to the following interface:
diff --git a/src/cmd/yacc/testdata/expr/README b/src/cmd/yacc/testdata/expr/README
new file mode 100644
index 0000000000..302ef57a7d
--- /dev/null
+++ b/src/cmd/yacc/testdata/expr/README
@@ -0,0 +1,20 @@
+This directory contains a simple program demonstrating how to use
+the Go version of yacc.
+
+To build it:
+
+ $ go generate
+ $ go build
+
+or
+
+ $ go generate
+ $ go run expr.go
+
+The file main.go contains the "go generate" command to run yacc to
+create expr.go from expr.y. It also has the package doc comment,
+as godoc will not scan the .y file.
+
+The actual implementation is in expr.y.
+
+The program is not installed in the binary distributions of Go.
diff --git a/src/cmd/yacc/expr.y b/src/cmd/yacc/testdata/expr/expr.y
index 77e9259dae..09451949ff 100644
--- a/src/cmd/yacc/expr.y
+++ b/src/cmd/yacc/testdata/expr/expr.y
@@ -11,11 +11,6 @@
%{
-// This tag will be copied to the generated file to prevent that file
-// confusing a future build.
-
-// +build ignore
-
package main
import (
diff --git a/src/cmd/yacc/testdata/expr/main.go b/src/cmd/yacc/testdata/expr/main.go
new file mode 100644
index 0000000000..8d5b6911f0
--- /dev/null
+++ b/src/cmd/yacc/testdata/expr/main.go
@@ -0,0 +1,15 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file holds the go generate command to run yacc on the grammar in expr.y.
+// To build expr:
+// % go generate
+// % go build
+
+//go:generate -command yacc go tool yacc
+//go:generate yacc -o expr.go -p "expr" expr.y
+
+// Expr is a simple expression evaluator that serves as a working example of
+// how to use Go's yacc implemenation.
+package main
diff --git a/src/liblink/asm5.c b/src/liblink/asm5.c
index 46aa1c1e3b..65c4ca199a 100644
--- a/src/liblink/asm5.c
+++ b/src/liblink/asm5.c
@@ -309,6 +309,7 @@ static Optab optab[] =
{ AUSEFIELD, C_ADDR, C_NONE, C_NONE, 0, 0, 0 },
{ APCDATA, C_LCON, C_NONE, C_LCON, 0, 0, 0 },
{ AFUNCDATA, C_LCON, C_NONE, C_ADDR, 0, 0, 0 },
+ { ANOP, C_NONE, C_NONE, C_NONE, 0, 0, 0 },
{ ADUFFZERO, C_NONE, C_NONE, C_SBRA, 5, 4, 0 }, // same as ABL
{ ADUFFCOPY, C_NONE, C_NONE, C_SBRA, 5, 4, 0 }, // same as ABL
@@ -686,7 +687,7 @@ span5(Link *ctxt, LSym *cursym)
continue;
}
}
- if(m == 0 && (p->as != AFUNCDATA && p->as != APCDATA && p->as != ADATABUNDLEEND)) {
+ if(m == 0 && (p->as != AFUNCDATA && p->as != APCDATA && p->as != ADATABUNDLEEND && p->as != ANOP)) {
ctxt->diag("zero-width instruction\n%P", p);
continue;
}
@@ -765,7 +766,7 @@ span5(Link *ctxt, LSym *cursym)
}
if(m/4 > nelem(out))
ctxt->diag("instruction size too large: %d > %d", m/4, nelem(out));
- if(m == 0 && (p->as != AFUNCDATA && p->as != APCDATA && p->as != ADATABUNDLEEND)) {
+ if(m == 0 && (p->as != AFUNCDATA && p->as != APCDATA && p->as != ADATABUNDLEEND && p->as != ANOP)) {
if(p->as == ATEXT) {
ctxt->autosize = p->to.offset + 4;
continue;
@@ -1479,6 +1480,7 @@ buildop(Link *ctxt)
case ACLZ:
case AFUNCDATA:
case APCDATA:
+ case ANOP:
case ADATABUNDLE:
case ADATABUNDLEEND:
break;
diff --git a/src/liblink/asm6.c b/src/liblink/asm6.c
index fa329777d0..999507650d 100644
--- a/src/liblink/asm6.c
+++ b/src/liblink/asm6.c
@@ -1302,7 +1302,7 @@ static Optab optab[] =
{ ASETNE, yscond, Pm, {0x95,(00)} },
{ ASETOC, yscond, Pm, {0x91,(00)} },
{ ASETOS, yscond, Pm, {0x90,(00)} },
- { ASETPC, yscond, Pm, {0x96,(00)} },
+ { ASETPC, yscond, Pm, {0x9b,(00)} },
{ ASETPL, yscond, Pm, {0x99,(00)} },
{ ASETPS, yscond, Pm, {0x9a,(00)} },
{ ASHLB, yshb, Pb, {0xd0,(04),0xc0,(04),0xd2,(04)} },
@@ -1535,6 +1535,19 @@ static Optab optab[] =
static Optab* opindex[ALAST+1];
static vlong vaddr(Link*, Addr*, Reloc*);
+// isextern reports whether s describes an external symbol that must avoid pc-relative addressing.
+// This happens on systems like Solaris that call .so functions instead of system calls.
+// It does not seem to be necessary for any other systems. This is probably working
+// around a Solaris-specific bug that should be fixed differently, but we don't know
+// what that bug is. And this does fix it.
+static int
+isextern(LSym *s)
+{
+ // All the Solaris dynamic imports from libc.so begin with "libc·", which
+ // the compiler rewrites to "libc." by the time liblink gets it.
+ return strncmp(s->name, "libc.", 5) == 0;
+}
+
// single-instruction no-ops of various lengths.
// constructed by hand and disassembled with gdb to verify.
// see http://www.agner.org/optimize/optimizing_assembly.pdf for discussion.
@@ -1932,10 +1945,9 @@ oclass(Link *ctxt, Addr *a)
switch(a->index) {
case D_EXTERN:
case D_STATIC:
- if(ctxt->flag_shared || ctxt->headtype == Hnacl)
- return Yiauto;
- else
- return Yi32; /* TO DO: Yi64 */
+ if(a->sym != nil && isextern(a->sym))
+ return Yi32;
+ return Yiauto; // use pc-relative addressing
case D_AUTO:
case D_PARAM:
return Yiauto;
@@ -2112,7 +2124,7 @@ oclass(Link *ctxt, Addr *a)
return Yi32; /* unsigned */
return Yi64;
}
- return Yi32; /* TO DO: D_ADDR as Yi64 */
+ return Yi32;
case D_BRANCH:
return Ybr;
@@ -2285,20 +2297,22 @@ vaddr(Link *ctxt, Addr *a, Reloc *r)
ctxt->diag("need reloc for %D", a);
sysfatal("reloc");
}
- r->siz = 4; // TODO: 8 for external symbols
+ if(isextern(s)) {
+ r->siz = 4;
+ r->type = R_ADDR;
+ } else {
+ r->siz = 4;
+ r->type = R_PCREL;
+ }
r->off = -1; // caller must fill in
r->sym = s;
r->add = v;
v = 0;
- if(ctxt->flag_shared || ctxt->headtype == Hnacl) {
- if(s->type == STLSBSS) {
- r->xadd = r->add - r->siz;
- r->type = R_TLS;
- r->xsym = s;
- } else
- r->type = R_PCREL;
- } else
- r->type = R_ADDR;
+ if(s->type == STLSBSS) {
+ r->xadd = r->add - r->siz;
+ r->type = R_TLS;
+ r->xsym = s;
+ }
break;
case D_INDIR+D_TLS:
@@ -2333,9 +2347,9 @@ asmandsz(Link *ctxt, Addr *a, int r, int rex, int m64)
switch(t) {
default:
goto bad;
- case D_STATIC:
case D_EXTERN:
- if(ctxt->flag_shared || ctxt->headtype == Hnacl)
+ case D_STATIC:
+ if(!isextern(a->sym))
goto bad;
t = D_NONE;
v = vaddr(ctxt, a, &rel);
@@ -2399,7 +2413,7 @@ asmandsz(Link *ctxt, Addr *a, int r, int rex, int m64)
ctxt->rexflag |= (regrex[t] & Rxb) | rex;
if(t == D_NONE || (D_CS <= t && t <= D_GS) || t == D_TLS) {
- if((ctxt->flag_shared || ctxt->headtype == Hnacl) && t == D_NONE && (a->type == D_STATIC || a->type == D_EXTERN) || ctxt->asmode != 64) {
+ if((a->sym == nil || !isextern(a->sym)) && t == D_NONE && (a->type == D_STATIC || a->type == D_EXTERN) || ctxt->asmode != 64) {
*ctxt->andptr++ = (0 << 6) | (5 << 0) | (r << 3);
goto putrelv;
}
diff --git a/src/liblink/asm8.c b/src/liblink/asm8.c
index f40b92bf84..6035017b2c 100644
--- a/src/liblink/asm8.c
+++ b/src/liblink/asm8.c
@@ -813,7 +813,7 @@ static Optab optab[] =
{ ASETNE, yscond, Pm, {0x95,(00)} },
{ ASETOC, yscond, Pm, {0x91,(00)} },
{ ASETOS, yscond, Pm, {0x90,(00)} },
- { ASETPC, yscond, Pm, {0x96,(00)} },
+ { ASETPC, yscond, Pm, {0x9b,(00)} },
{ ASETPL, yscond, Pm, {0x99,(00)} },
{ ASETPS, yscond, Pm, {0x9a,(00)} },
{ ACDQ, ynone, Px, {0x99} },
diff --git a/src/liblink/obj5.c b/src/liblink/obj5.c
index de920b029e..0b6b8deb5f 100644
--- a/src/liblink/obj5.c
+++ b/src/liblink/obj5.c
@@ -241,7 +241,7 @@ nocache(Prog *p)
static void
addstacksplit(Link *ctxt, LSym *cursym)
{
- Prog *p, *pl, *q, *q1, *q2;
+ Prog *p, *pl, *p1, *p2, *q, *q1, *q2;
int o;
int32 autosize, autoffset;
@@ -437,32 +437,89 @@ addstacksplit(Link *ctxt, LSym *cursym)
p->spadj = autosize;
if(cursym->text->reg & WRAPPER) {
- // g->panicwrap += autosize;
- // MOVW panicwrap_offset(g), R3
- // ADD $autosize, R3
- // MOVW R3 panicwrap_offset(g)
+ // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
+ //
+ // MOVW g_panic(g), R1
+ // CMP $0, R1
+ // B.EQ end
+ // MOVW panic_argp(R1), R2
+ // ADD $(autosize+4), R13, R3
+ // CMP R2, R3
+ // B.NE end
+ // ADD $4, R13, R4
+ // MOVW R4, panic_argp(R1)
+ // end:
+ // NOP
+ //
+ // The NOP is needed to give the jumps somewhere to land.
+ // It is a liblink NOP, not an ARM NOP: it encodes to 0 instruction bytes.
+
p = appendp(ctxt, p);
p->as = AMOVW;
p->from.type = D_OREG;
p->from.reg = REGG;
- p->from.offset = 2*ctxt->arch->ptrsize;
+ p->from.offset = 2*ctxt->arch->ptrsize; // G.panic
p->to.type = D_REG;
- p->to.reg = 3;
+ p->to.reg = 1;
+
+ p = appendp(ctxt, p);
+ p->as = ACMP;
+ p->from.type = D_CONST;
+ p->from.offset = 0;
+ p->reg = 1;
+
+ p = appendp(ctxt, p);
+ p->as = ABEQ;
+ p->to.type = D_BRANCH;
+ p1 = p;
+
+ p = appendp(ctxt, p);
+ p->as = AMOVW;
+ p->from.type = D_OREG;
+ p->from.reg = 1;
+ p->from.offset = 0; // Panic.argp
+ p->to.type = D_REG;
+ p->to.reg = 2;
p = appendp(ctxt, p);
p->as = AADD;
p->from.type = D_CONST;
- p->from.offset = autosize;
+ p->from.offset = autosize+4;
+ p->reg = 13;
p->to.type = D_REG;
p->to.reg = 3;
-
+
+ p = appendp(ctxt, p);
+ p->as = ACMP;
+ p->from.type = D_REG;
+ p->from.reg = 2;
+ p->reg = 3;
+
+ p = appendp(ctxt, p);
+ p->as = ABNE;
+ p->to.type = D_BRANCH;
+ p2 = p;
+
+ p = appendp(ctxt, p);
+ p->as = AADD;
+ p->from.type = D_CONST;
+ p->from.offset = 4;
+ p->reg = 13;
+ p->to.type = D_REG;
+ p->to.reg = 4;
+
p = appendp(ctxt, p);
p->as = AMOVW;
p->from.type = D_REG;
- p->from.reg = 3;
+ p->from.reg = 4;
p->to.type = D_OREG;
- p->to.reg = REGG;
- p->to.offset = 2*ctxt->arch->ptrsize;
+ p->to.reg = 1;
+ p->to.offset = 0; // Panic.argp
+
+ p = appendp(ctxt, p);
+ p->as = ANOP;
+ p1->pcond = p;
+ p2->pcond = p;
}
break;
@@ -483,44 +540,6 @@ addstacksplit(Link *ctxt, LSym *cursym)
}
}
- if(cursym->text->reg & WRAPPER) {
- int scond;
-
- // Preserve original RET's cond, to allow RET.EQ
- // in the implementation of reflect.call.
- scond = p->scond;
- p->scond = C_SCOND_NONE;
-
- // g->panicwrap -= autosize;
- // MOVW panicwrap_offset(g), R3
- // SUB $autosize, R3
- // MOVW R3 panicwrap_offset(g)
- p->as = AMOVW;
- p->from.type = D_OREG;
- p->from.reg = REGG;
- p->from.offset = 2*ctxt->arch->ptrsize;
- p->to.type = D_REG;
- p->to.reg = 3;
- p = appendp(ctxt, p);
-
- p->as = ASUB;
- p->from.type = D_CONST;
- p->from.offset = autosize;
- p->to.type = D_REG;
- p->to.reg = 3;
- p = appendp(ctxt, p);
-
- p->as = AMOVW;
- p->from.type = D_REG;
- p->from.reg = 3;
- p->to.type = D_OREG;
- p->to.reg = REGG;
- p->to.offset = 2*ctxt->arch->ptrsize;
- p = appendp(ctxt, p);
-
- p->scond = scond;
- }
-
p->as = AMOVW;
p->scond |= C_PBIT;
p->from.type = D_OREG;
diff --git a/src/liblink/obj6.c b/src/liblink/obj6.c
index eef3b4294a..3c62470be9 100644
--- a/src/liblink/obj6.c
+++ b/src/liblink/obj6.c
@@ -388,7 +388,7 @@ parsetextconst(vlong arg, vlong *textstksiz, vlong *textarg)
static void
addstacksplit(Link *ctxt, LSym *cursym)
{
- Prog *p, *q, *q1;
+ Prog *p, *q, *q1, *p1, *p2;
int32 autoffset, deltasp;
int a, pcsize;
uint32 i;
@@ -463,13 +463,86 @@ addstacksplit(Link *ctxt, LSym *cursym)
deltasp = autoffset;
if(cursym->text->from.scale & WRAPPER) {
- // g->panicwrap += autoffset + ctxt->arch->regsize;
+ // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
+ //
+ // MOVQ g_panic(CX), BX
+ // TESTQ BX, BX
+ // JEQ end
+ // LEAQ (autoffset+8)(SP), DI
+ // CMPQ panic_argp(BX), DI
+ // JNE end
+ // MOVQ SP, panic_argp(BX)
+ // end:
+ // NOP
+ //
+ // The NOP is needed to give the jumps somewhere to land.
+ // It is a liblink NOP, not an x86 NOP: it encodes to 0 instruction bytes.
+
p = appendp(ctxt, p);
- p->as = AADDL;
- p->from.type = D_CONST;
- p->from.offset = autoffset + ctxt->arch->regsize;
- indir_cx(ctxt, &p->to);
- p->to.offset = 2*ctxt->arch->ptrsize;
+ p->as = AMOVQ;
+ p->from.type = D_INDIR+D_CX;
+ p->from.offset = 2*ctxt->arch->ptrsize; // G.panic
+ p->to.type = D_BX;
+ if(ctxt->headtype == Hnacl) {
+ p->as = AMOVL;
+ p->from.type = D_INDIR+D_R15;
+ p->from.scale = 1;
+ p->from.index = D_CX;
+ }
+
+ p = appendp(ctxt, p);
+ p->as = ATESTQ;
+ p->from.type = D_BX;
+ p->to.type = D_BX;
+ if(ctxt->headtype == Hnacl)
+ p->as = ATESTL;
+
+ p = appendp(ctxt, p);
+ p->as = AJEQ;
+ p->to.type = D_BRANCH;
+ p1 = p;
+
+ p = appendp(ctxt, p);
+ p->as = ALEAQ;
+ p->from.type = D_INDIR+D_SP;
+ p->from.offset = autoffset+8;
+ p->to.type = D_DI;
+ if(ctxt->headtype == Hnacl)
+ p->as = ALEAL;
+
+ p = appendp(ctxt, p);
+ p->as = ACMPQ;
+ p->from.type = D_INDIR+D_BX;
+ p->from.offset = 0; // Panic.argp
+ p->to.type = D_DI;
+ if(ctxt->headtype == Hnacl) {
+ p->as = ACMPL;
+ p->from.type = D_INDIR+D_R15;
+ p->from.scale = 1;
+ p->from.index = D_BX;
+ }
+
+ p = appendp(ctxt, p);
+ p->as = AJNE;
+ p->to.type = D_BRANCH;
+ p2 = p;
+
+ p = appendp(ctxt, p);
+ p->as = AMOVQ;
+ p->from.type = D_SP;
+ p->to.type = D_INDIR+D_BX;
+ p->to.offset = 0; // Panic.argp
+ if(ctxt->headtype == Hnacl) {
+ p->as = AMOVL;
+ p->to.type = D_INDIR+D_R15;
+ p->to.scale = 1;
+ p->to.index = D_BX;
+ }
+
+ p = appendp(ctxt, p);
+ p->as = ANOP;
+ p1->pcond = p;
+ p2->pcond = p;
}
if(ctxt->debugstack > 1 && autoffset) {
@@ -589,19 +662,6 @@ addstacksplit(Link *ctxt, LSym *cursym)
if(autoffset != deltasp)
ctxt->diag("unbalanced PUSH/POP");
- if(cursym->text->from.scale & WRAPPER) {
- p = load_g_cx(ctxt, p);
- p = appendp(ctxt, p);
- // g->panicwrap -= autoffset + ctxt->arch->regsize;
- p->as = ASUBL;
- p->from.type = D_CONST;
- p->from.offset = autoffset + ctxt->arch->regsize;
- indir_cx(ctxt, &p->to);
- p->to.offset = 2*ctxt->arch->ptrsize;
- p = appendp(ctxt, p);
- p->as = ARET;
- }
-
if(autoffset) {
p->as = AADJSP;
p->from.type = D_CONST;
diff --git a/src/liblink/obj8.c b/src/liblink/obj8.c
index 50e6d8236d..fa1e1ca243 100644
--- a/src/liblink/obj8.c
+++ b/src/liblink/obj8.c
@@ -261,7 +261,7 @@ static Prog* stacksplit(Link*, Prog*, int32, int, Prog**);
static void
addstacksplit(Link *ctxt, LSym *cursym)
{
- Prog *p, *q;
+ Prog *p, *q, *p1, *p2;
int32 autoffset, deltasp;
int a;
@@ -317,13 +317,64 @@ addstacksplit(Link *ctxt, LSym *cursym)
deltasp = autoffset;
if(cursym->text->from.scale & WRAPPER) {
- // g->panicwrap += autoffset + ctxt->arch->ptrsize;
+ // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
+ //
+ // MOVL g_panic(CX), BX
+ // TESTL BX, BX
+ // JEQ end
+ // LEAL (autoffset+4)(SP), DI
+ // CMPL panic_argp(BX), DI
+ // JNE end
+ // MOVL SP, panic_argp(BX)
+ // end:
+ // NOP
+ //
+ // The NOP is needed to give the jumps somewhere to land.
+ // It is a liblink NOP, not an x86 NOP: it encodes to 0 instruction bytes.
+
p = appendp(ctxt, p);
- p->as = AADDL;
- p->from.type = D_CONST;
- p->from.offset = autoffset + ctxt->arch->ptrsize;
- p->to.type = D_INDIR+D_CX;
- p->to.offset = 2*ctxt->arch->ptrsize;
+ p->as = AMOVL;
+ p->from.type = D_INDIR+D_CX;
+ p->from.offset = 2*ctxt->arch->ptrsize; // G.panic
+ p->to.type = D_BX;
+
+ p = appendp(ctxt, p);
+ p->as = ATESTL;
+ p->from.type = D_BX;
+ p->to.type = D_BX;
+
+ p = appendp(ctxt, p);
+ p->as = AJEQ;
+ p->to.type = D_BRANCH;
+ p1 = p;
+
+ p = appendp(ctxt, p);
+ p->as = ALEAL;
+ p->from.type = D_INDIR+D_SP;
+ p->from.offset = autoffset+4;
+ p->to.type = D_DI;
+
+ p = appendp(ctxt, p);
+ p->as = ACMPL;
+ p->from.type = D_INDIR+D_BX;
+ p->from.offset = 0; // Panic.argp
+ p->to.type = D_DI;
+
+ p = appendp(ctxt, p);
+ p->as = AJNE;
+ p->to.type = D_BRANCH;
+ p2 = p;
+
+ p = appendp(ctxt, p);
+ p->as = AMOVL;
+ p->from.type = D_SP;
+ p->to.type = D_INDIR+D_BX;
+ p->to.offset = 0; // Panic.argp
+
+ p = appendp(ctxt, p);
+ p->as = ANOP;
+ p1->pcond = p;
+ p2->pcond = p;
}
if(ctxt->debugzerostack && autoffset && !(cursym->text->from.scale&NOSPLIT)) {
@@ -396,19 +447,6 @@ addstacksplit(Link *ctxt, LSym *cursym)
if(autoffset != deltasp)
ctxt->diag("unbalanced PUSH/POP");
- if(cursym->text->from.scale & WRAPPER) {
- p = load_g_cx(ctxt, p);
- p = appendp(ctxt, p);
- // g->panicwrap -= autoffset + ctxt->arch->ptrsize;
- p->as = ASUBL;
- p->from.type = D_CONST;
- p->from.offset = autoffset + ctxt->arch->ptrsize;
- p->to.type = D_INDIR+D_CX;
- p->to.offset = 2*ctxt->arch->ptrsize;
- p = appendp(ctxt, p);
- p->as = ARET;
- }
-
if(autoffset) {
p->as = AADJSP;
p->from.type = D_CONST;
diff --git a/src/liblink/objfile.c b/src/liblink/objfile.c
index 3b78571b74..394f2450bd 100644
--- a/src/liblink/objfile.c
+++ b/src/liblink/objfile.c
@@ -38,7 +38,8 @@
// - type [int]
// - name [string]
// - version [int]
-// - dupok [int]
+// - flags [int]
+// 1 dupok
// - size [int]
// - gotype [symbol reference]
// - p [data block]
@@ -50,7 +51,9 @@
// - args [int]
// - locals [int]
// - nosplit [int]
-// - leaf [int]
+// - flags [int]
+// 1 leaf
+// 2 C function
// - nlocal [int]
// - local [nlocal automatics]
// - pcln [pcln table]
@@ -291,6 +294,8 @@ writesym(Link *ctxt, Biobuf *b, LSym *s)
Bprint(ctxt->bso, "t=%d ", s->type);
if(s->dupok)
Bprint(ctxt->bso, "dupok ");
+ if(s->cfunc)
+ Bprint(ctxt->bso, "cfunc ");
if(s->nosplit)
Bprint(ctxt->bso, "nosplit ");
Bprint(ctxt->bso, "size=%lld value=%lld", (vlong)s->size, (vlong)s->value);
@@ -356,7 +361,7 @@ writesym(Link *ctxt, Biobuf *b, LSym *s)
wrint(b, s->args);
wrint(b, s->locals);
wrint(b, s->nosplit);
- wrint(b, s->leaf);
+ wrint(b, s->leaf | s->cfunc<<1);
n = 0;
for(a = s->autom; a != nil; a = a->link)
n++;
@@ -524,6 +529,7 @@ readsym(Link *ctxt, Biobuf *f, char *pkg, char *pn)
if(v != 0 && v != 1)
sysfatal("invalid symbol version %d", v);
dupok = rdint(f);
+ dupok &= 1;
size = rdint(f);
if(v != 0)
@@ -578,7 +584,9 @@ readsym(Link *ctxt, Biobuf *f, char *pkg, char *pn)
s->args = rdint(f);
s->locals = rdint(f);
s->nosplit = rdint(f);
- s->leaf = rdint(f);
+ v = rdint(f);
+ s->leaf = v&1;
+ s->cfunc = v&2;
n = rdint(f);
for(i=0; i<n; i++) {
a = emallocz(sizeof *a);
@@ -634,6 +642,8 @@ readsym(Link *ctxt, Biobuf *f, char *pkg, char *pn)
Bprint(ctxt->bso, "t=%d ", s->type);
if(s->dupok)
Bprint(ctxt->bso, "dupok ");
+ if(s->cfunc)
+ Bprint(ctxt->bso, "cfunc ");
if(s->nosplit)
Bprint(ctxt->bso, "nosplit ");
Bprint(ctxt->bso, "size=%lld value=%lld", (vlong)s->size, (vlong)s->value);
diff --git a/src/make.bat b/src/make.bat
index a1a6c14a40..fff1eb6011 100644
--- a/src/make.bat
+++ b/src/make.bat
@@ -60,7 +60,7 @@ echo # Building C bootstrap tool.
echo cmd/dist
if not exist ..\bin\tool mkdir ..\bin\tool
:: Windows has no glob expansion, so spell out cmd/dist/*.c.
-gcc -O2 -Wall -Werror -o cmd/dist/dist.exe -Icmd/dist %DEFGOROOT% cmd/dist/buf.c cmd/dist/build.c cmd/dist/buildgc.c cmd/dist/buildgo.c cmd/dist/buildruntime.c cmd/dist/goc2c.c cmd/dist/main.c cmd/dist/windows.c cmd/dist/arm.c
+gcc -O2 -Wall -Werror -o cmd/dist/dist.exe -Icmd/dist %DEFGOROOT% cmd/dist/buf.c cmd/dist/build.c cmd/dist/buildgc.c cmd/dist/buildgo.c cmd/dist/buildruntime.c cmd/dist/main.c cmd/dist/windows.c cmd/dist/arm.c
if errorlevel 1 goto fail
.\cmd\dist\dist env -wp >env.bat
if errorlevel 1 goto fail
diff --git a/src/pkg/archive/zip/writer.go b/src/pkg/archive/zip/writer.go
index 6c9800a78f..170beec0ee 100644
--- a/src/pkg/archive/zip/writer.go
+++ b/src/pkg/archive/zip/writer.go
@@ -34,6 +34,12 @@ func NewWriter(w io.Writer) *Writer {
return &Writer{cw: &countWriter{w: bufio.NewWriter(w)}}
}
+// Flush flushes any buffered data to the underlying writer.
+// Calling Flush is not normally necessary; calling Close is sufficient.
+func (w *Writer) Flush() error {
+ return w.cw.w.(*bufio.Writer).Flush()
+}
+
// Close finishes writing the zip file by writing the central directory.
// It does not (and can not) close the underlying writer.
func (w *Writer) Close() error {
diff --git a/src/pkg/archive/zip/writer_test.go b/src/pkg/archive/zip/writer_test.go
index 4bfa870809..184a7d96a7 100644
--- a/src/pkg/archive/zip/writer_test.go
+++ b/src/pkg/archive/zip/writer_test.go
@@ -6,6 +6,7 @@ package zip
import (
"bytes"
+ "io"
"io/ioutil"
"math/rand"
"os"
@@ -86,6 +87,24 @@ func TestWriter(t *testing.T) {
}
}
+func TestWriterFlush(t *testing.T) {
+ var buf bytes.Buffer
+ w := NewWriter(struct{ io.Writer }{&buf})
+ _, err := w.Create("foo")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if buf.Len() > 0 {
+ t.Fatalf("Unexpected %d bytes already in buffer", buf.Len())
+ }
+ if err := w.Flush(); err != nil {
+ t.Fatal(err)
+ }
+ if buf.Len() == 0 {
+ t.Fatal("No bytes written after Flush")
+ }
+}
+
func testCreate(t *testing.T, w *Writer, wt *WriteTest) {
header := &FileHeader{
Name: wt.Name,
diff --git a/src/pkg/bytes/bytes.go b/src/pkg/bytes/bytes.go
index d8b6f998b3..34c22bbfb1 100644
--- a/src/pkg/bytes/bytes.go
+++ b/src/pkg/bytes/bytes.go
@@ -605,6 +605,9 @@ func Runes(s []byte) []rune {
// Replace returns a copy of the slice s with the first n
// non-overlapping instances of old replaced by new.
+// If old is empty, it matches at the beginning of the slice
+// and after each UTF-8 sequence, yielding up to k+1 replacements
+// for a k-rune slice.
// If n < 0, there is no limit on the number of replacements.
func Replace(s, old, new []byte, n int) []byte {
m := 0
diff --git a/src/pkg/compress/bzip2/bzip2_test.go b/src/pkg/compress/bzip2/bzip2_test.go
index 6b8711b811..fb79d089eb 100644
--- a/src/pkg/compress/bzip2/bzip2_test.go
+++ b/src/pkg/compress/bzip2/bzip2_test.go
@@ -216,6 +216,44 @@ func TestOutOfRangeSelector(t *testing.T) {
ioutil.ReadAll(decompressor)
}
+func TestMTF(t *testing.T) {
+ mtf := newMTFDecoderWithRange(5)
+
+ // 0 1 2 3 4
+ expect := byte(1)
+ x := mtf.Decode(1)
+ if x != expect {
+ t.Errorf("expected %v, got %v", expect, x)
+ }
+
+ // 1 0 2 3 4
+ x = mtf.Decode(0)
+ if x != expect {
+ t.Errorf("expected %v, got %v", expect, x)
+ }
+
+ // 1 0 2 3 4
+ expect = byte(0)
+ x = mtf.Decode(1)
+ if x != expect {
+ t.Errorf("expected %v, got %v", expect, x)
+ }
+
+ // 0 1 2 3 4
+ expect = byte(4)
+ x = mtf.Decode(4)
+ if x != expect {
+ t.Errorf("expected %v, got %v", expect, x)
+ }
+
+ // 4 0 1 2 3
+ expect = byte(0)
+ x = mtf.Decode(1)
+ if x != expect {
+ t.Errorf("expected %v, got %v", expect, x)
+ }
+}
+
var bufferOverrunBase64 string = `
QlpoNTFBWSZTWTzyiGcACMP/////////////////////////////////3/7f3///
////4N/fCZODak2Xo44GIHZgkGzDRbFAuwAAKoFV7T6AO6qwA6APb6s2rOoAkAAD
diff --git a/src/pkg/compress/bzip2/move_to_front.go b/src/pkg/compress/bzip2/move_to_front.go
index b7e75a700a..526dfb34cc 100644
--- a/src/pkg/compress/bzip2/move_to_front.go
+++ b/src/pkg/compress/bzip2/move_to_front.go
@@ -11,88 +11,43 @@ package bzip2
// index into that list. When a symbol is referenced, it's moved to the front
// of the list. Thus, a repeated symbol ends up being encoded with many zeros,
// as the symbol will be at the front of the list after the first access.
-type moveToFrontDecoder struct {
- // Rather than actually keep the list in memory, the symbols are stored
- // as a circular, double linked list with the symbol indexed by head
- // at the front of the list.
- symbols [256]byte
- next [256]uint8
- prev [256]uint8
- head uint8
- len int
-}
+type moveToFrontDecoder []byte
// newMTFDecoder creates a move-to-front decoder with an explicit initial list
// of symbols.
-func newMTFDecoder(symbols []byte) *moveToFrontDecoder {
+func newMTFDecoder(symbols []byte) moveToFrontDecoder {
if len(symbols) > 256 {
panic("too many symbols")
}
-
- m := new(moveToFrontDecoder)
- copy(m.symbols[:], symbols)
- m.len = len(symbols)
- m.threadLinkedList()
- return m
+ return moveToFrontDecoder(symbols)
}
// newMTFDecoderWithRange creates a move-to-front decoder with an initial
// symbol list of 0...n-1.
-func newMTFDecoderWithRange(n int) *moveToFrontDecoder {
+func newMTFDecoderWithRange(n int) moveToFrontDecoder {
if n > 256 {
panic("newMTFDecoderWithRange: cannot have > 256 symbols")
}
- m := new(moveToFrontDecoder)
+ m := make([]byte, n)
for i := 0; i < n; i++ {
- m.symbols[byte(i)] = byte(i)
- }
- m.len = n
- m.threadLinkedList()
- return m
-}
-
-// threadLinkedList creates the initial linked-list pointers.
-func (m *moveToFrontDecoder) threadLinkedList() {
- if m.len == 0 {
- return
- }
-
- m.prev[0] = uint8(m.len - 1)
-
- for i := byte(0); int(i) < m.len-1; i++ {
- m.next[i] = uint8(i + 1)
- m.prev[i+1] = uint8(i)
+ m[i] = byte(i)
}
-
- m.next[m.len-1] = 0
+ return moveToFrontDecoder(m)
}
-func (m *moveToFrontDecoder) Decode(n int) (b byte) {
- // Most of the time, n will be zero so it's worth dealing with this
- // simple case.
- if n == 0 {
- return m.symbols[m.head]
- }
-
- i := m.head
- for j := 0; j < n; j++ {
- i = m.next[i]
- }
- b = m.symbols[i]
-
- m.next[m.prev[i]] = m.next[i]
- m.prev[m.next[i]] = m.prev[i]
- m.next[i] = m.head
- m.prev[i] = m.prev[m.head]
- m.next[m.prev[m.head]] = i
- m.prev[m.head] = i
- m.head = i
-
+func (m moveToFrontDecoder) Decode(n int) (b byte) {
+ // Implement move-to-front with a simple copy. This approach
+ // beats more sophisticated approaches in benchmarking, probably
+ // because it has high locality of reference inside of a
+ // single cache line (most move-to-front operations have n < 64).
+ b = m[n]
+ copy(m[1:], m[:n])
+ m[0] = b
return
}
// First returns the symbol at the front of the list.
-func (m *moveToFrontDecoder) First() byte {
- return m.symbols[m.head]
+func (m moveToFrontDecoder) First() byte {
+ return m[0]
}
diff --git a/src/pkg/compress/flate/fixedhuff.go b/src/pkg/compress/flate/fixedhuff.go
index 9be3d53495..7df8b9a293 100644
--- a/src/pkg/compress/flate/fixedhuff.go
+++ b/src/pkg/compress/flate/fixedhuff.go
@@ -4,7 +4,7 @@
package flate
-// autogenerated by gen.go, DO NOT EDIT
+// autogenerated by go run gen.go -output fixedhuff.go, DO NOT EDIT
var fixedHuffmanDecoder = huffmanDecoder{
7,
diff --git a/src/pkg/compress/flate/gen.go b/src/pkg/compress/flate/gen.go
index 1427557f80..6288ecddd0 100644
--- a/src/pkg/compress/flate/gen.go
+++ b/src/pkg/compress/flate/gen.go
@@ -7,14 +7,21 @@
// This program generates fixedhuff.go
// Invoke as
//
-// go run gen.go |gofmt >fixedhuff.go
+// go run gen.go -output fixedhuff.go
package main
import (
+ "bytes"
+ "flag"
"fmt"
+ "go/format"
+ "io/ioutil"
+ "log"
)
+var filename = flag.String("output", "fixedhuff.go", "output file name")
+
const maxCodeLen = 16
// Note: the definition of the huffmanDecoder struct is copied from
@@ -113,6 +120,8 @@ func (h *huffmanDecoder) init(bits []int) bool {
}
func main() {
+ flag.Parse()
+
var h huffmanDecoder
var bits [288]int
initReverseByte()
@@ -129,27 +138,43 @@ func main() {
bits[i] = 8
}
h.init(bits[:])
- fmt.Println("package flate")
- fmt.Println()
- fmt.Println("// autogenerated by gen.go, DO NOT EDIT")
- fmt.Println()
- fmt.Println("var fixedHuffmanDecoder = huffmanDecoder{")
- fmt.Printf("\t%d,\n", h.min)
- fmt.Println("\t[huffmanNumChunks]uint32{")
+
+ var buf bytes.Buffer
+
+ fmt.Fprintf(&buf, `// 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.`+"\n\n")
+
+ fmt.Fprintln(&buf, "package flate")
+ fmt.Fprintln(&buf)
+ fmt.Fprintln(&buf, "// autogenerated by go run gen.go -output fixedhuff.go, DO NOT EDIT")
+ fmt.Fprintln(&buf)
+ fmt.Fprintln(&buf, "var fixedHuffmanDecoder = huffmanDecoder{")
+ fmt.Fprintf(&buf, "\t%d,\n", h.min)
+ fmt.Fprintln(&buf, "\t[huffmanNumChunks]uint32{")
for i := 0; i < huffmanNumChunks; i++ {
if i&7 == 0 {
- fmt.Printf("\t\t")
+ fmt.Fprintf(&buf, "\t\t")
} else {
- fmt.Printf(" ")
+ fmt.Fprintf(&buf, " ")
}
- fmt.Printf("0x%04x,", h.chunks[i])
+ fmt.Fprintf(&buf, "0x%04x,", h.chunks[i])
if i&7 == 7 {
- fmt.Println()
+ fmt.Fprintln(&buf)
}
}
- fmt.Println("\t},")
- fmt.Println("\tnil, 0,")
- fmt.Println("}")
+ fmt.Fprintln(&buf, "\t},")
+ fmt.Fprintln(&buf, "\tnil, 0,")
+ fmt.Fprintln(&buf, "}")
+
+ data, err := format.Source(buf.Bytes())
+ if err != nil {
+ log.Fatal(err)
+ }
+ err = ioutil.WriteFile(*filename, data, 0644)
+ if err != nil {
+ log.Fatal(err)
+ }
}
var reverseByte [256]byte
diff --git a/src/pkg/compress/flate/inflate.go b/src/pkg/compress/flate/inflate.go
index ce4923eca3..769ef42997 100644
--- a/src/pkg/compress/flate/inflate.go
+++ b/src/pkg/compress/flate/inflate.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:generate go run gen.go -output fixedhuff.go
+
// Package flate implements the DEFLATE compressed data format, described in
// RFC 1951. The gzip and zlib packages implement access to DEFLATE-based file
// formats.
diff --git a/src/pkg/compress/gzip/gzip.go b/src/pkg/compress/gzip/gzip.go
index 3a0bf54e1b..5131d128e4 100644
--- a/src/pkg/compress/gzip/gzip.go
+++ b/src/pkg/compress/gzip/gzip.go
@@ -245,7 +245,8 @@ func (z *Writer) Flush() error {
return z.err
}
-// Close closes the Writer. It does not close the underlying io.Writer.
+// Close closes the Writer, flushing any unwritten data to the underlying
+// io.Writer, but does not close the underlying io.Writer.
func (z *Writer) Close() error {
if z.err != nil {
return z.err
diff --git a/src/pkg/compress/zlib/writer.go b/src/pkg/compress/zlib/writer.go
index fac7e15a7e..3b4313a8be 100644
--- a/src/pkg/compress/zlib/writer.go
+++ b/src/pkg/compress/zlib/writer.go
@@ -174,7 +174,8 @@ func (z *Writer) Flush() error {
return z.err
}
-// Calling Close does not close the wrapped io.Writer originally passed to NewWriter.
+// Close closes the Writer, flushing any unwritten data to the underlying
+// io.Writer, but does not close the underlying io.Writer.
func (z *Writer) Close() error {
if !z.wroteHeader {
z.err = z.writeHeader()
diff --git a/src/pkg/crypto/aes/asm_amd64.s b/src/pkg/crypto/aes/asm_amd64.s
index 5c22881e98..6a6e6ac4b9 100644
--- a/src/pkg/crypto/aes/asm_amd64.s
+++ b/src/pkg/crypto/aes/asm_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// func hasAsm() bool
// returns whether AES-NI is supported
diff --git a/src/pkg/crypto/cipher/cfb_test.go b/src/pkg/crypto/cipher/cfb_test.go
index ec708ab2be..9b544bb211 100644
--- a/src/pkg/crypto/cipher/cfb_test.go
+++ b/src/pkg/crypto/cipher/cfb_test.go
@@ -9,10 +9,85 @@ import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
+ "encoding/hex"
"testing"
)
-func TestCFB(t *testing.T) {
+// cfbTests contains the test vectors from
+// http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf, section
+// F.3.13.
+var cfbTests = []struct {
+ key, iv, plaintext, ciphertext string
+}{
+ {
+ "2b7e151628aed2a6abf7158809cf4f3c",
+ "000102030405060708090a0b0c0d0e0f",
+ "6bc1bee22e409f96e93d7e117393172a",
+ "3b3fd92eb72dad20333449f8e83cfb4a",
+ },
+ {
+ "2b7e151628aed2a6abf7158809cf4f3c",
+ "3B3FD92EB72DAD20333449F8E83CFB4A",
+ "ae2d8a571e03ac9c9eb76fac45af8e51",
+ "c8a64537a0b3a93fcde3cdad9f1ce58b",
+ },
+ {
+ "2b7e151628aed2a6abf7158809cf4f3c",
+ "C8A64537A0B3A93FCDE3CDAD9F1CE58B",
+ "30c81c46a35ce411e5fbc1191a0a52ef",
+ "26751f67a3cbb140b1808cf187a4f4df",
+ },
+ {
+ "2b7e151628aed2a6abf7158809cf4f3c",
+ "26751F67A3CBB140B1808CF187A4F4DF",
+ "f69f2445df4f9b17ad2b417be66c3710",
+ "c04b05357c5d1c0eeac4c66f9ff7f2e6",
+ },
+}
+
+func TestCFBVectors(t *testing.T) {
+ for i, test := range cfbTests {
+ key, err := hex.DecodeString(test.key)
+ if err != nil {
+ t.Fatal(err)
+ }
+ iv, err := hex.DecodeString(test.iv)
+ if err != nil {
+ t.Fatal(err)
+ }
+ plaintext, err := hex.DecodeString(test.plaintext)
+ if err != nil {
+ t.Fatal(err)
+ }
+ expected, err := hex.DecodeString(test.ciphertext)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ block, err := aes.NewCipher(key)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ ciphertext := make([]byte, len(plaintext))
+ cfb := cipher.NewCFBEncrypter(block, iv)
+ cfb.XORKeyStream(ciphertext, plaintext)
+
+ if !bytes.Equal(ciphertext, expected) {
+ t.Errorf("#%d: wrong output: got %x, expected %x", i, ciphertext, expected)
+ }
+
+ cfbdec := cipher.NewCFBDecrypter(block, iv)
+ plaintextCopy := make([]byte, len(ciphertext))
+ cfbdec.XORKeyStream(plaintextCopy, ciphertext)
+
+ if !bytes.Equal(plaintextCopy, plaintextCopy) {
+ t.Errorf("#%d: wrong plaintext: got %x, expected %x", i, plaintextCopy, plaintext)
+ }
+ }
+}
+
+func TestCFBInverse(t *testing.T) {
block, err := aes.NewCipher(commonKey128)
if err != nil {
t.Error(err)
diff --git a/src/pkg/crypto/crypto.go b/src/pkg/crypto/crypto.go
index 4b03628e69..5a91baca0e 100644
--- a/src/pkg/crypto/crypto.go
+++ b/src/pkg/crypto/crypto.go
@@ -7,6 +7,7 @@ package crypto
import (
"hash"
+ "io"
"strconv"
)
@@ -14,6 +15,11 @@ import (
// package.
type Hash uint
+// HashFunc simply returns the value of h so that Hash implements SignerOpts.
+func (h Hash) HashFunc() Hash {
+ return h
+}
+
const (
MD4 Hash = 1 + iota // import code.google.com/p/go.crypto/md4
MD5 // import crypto/md5
@@ -24,6 +30,10 @@ const (
SHA512 // import crypto/sha512
MD5SHA1 // no implementation; MD5+SHA1 used for TLS RSA
RIPEMD160 // import code.google.com/p/go.crypto/ripemd160
+ SHA3_224 // import code.google.com/p/go.crypto/sha3
+ SHA3_256 // import code.google.com/p/go.crypto/sha3
+ SHA3_384 // import code.google.com/p/go.crypto/sha3
+ SHA3_512 // import code.google.com/p/go.crypto/sha3
maxHash
)
@@ -35,6 +45,10 @@ var digestSizes = []uint8{
SHA256: 32,
SHA384: 48,
SHA512: 64,
+ SHA3_224: 28,
+ SHA3_256: 32,
+ SHA3_384: 48,
+ SHA3_512: 64,
MD5SHA1: 36,
RIPEMD160: 20,
}
@@ -83,3 +97,30 @@ type PublicKey interface{}
// PrivateKey represents a private key using an unspecified algorithm.
type PrivateKey interface{}
+
+// Signer is an interface for an opaque private key that can be used for
+// signing operations. For example, an RSA key kept in a hardware module.
+type Signer interface {
+ // Public returns the public key corresponding to the opaque,
+ // private key.
+ Public() PublicKey
+
+ // Sign signs msg with the private key, possibly using entropy from
+ // rand. For an RSA key, the resulting signature should be either a
+ // PKCS#1 v1.5 or PSS signature (as indicated by opts). For an (EC)DSA
+ // key, it should be a DER-serialised, ASN.1 signature structure.
+ //
+ // Hash implements the SignerOpts interface and, in most cases, one can
+ // simply pass in the hash function used as opts. Sign may also attempt
+ // to type assert opts to other types in order to obtain algorithm
+ // specific values. See the documentation in each package for details.
+ Sign(rand io.Reader, msg []byte, opts SignerOpts) (signature []byte, err error)
+}
+
+// SignerOpts contains options for signing with a Signer.
+type SignerOpts interface {
+ // HashFunc returns an identifier for the hash function used to produce
+ // the message passed to Signer.Sign, or else zero to indicate that no
+ // hashing was done.
+ HashFunc() Hash
+}
diff --git a/src/pkg/crypto/ecdsa/ecdsa.go b/src/pkg/crypto/ecdsa/ecdsa.go
index 1bec7437a5..d6135531bf 100644
--- a/src/pkg/crypto/ecdsa/ecdsa.go
+++ b/src/pkg/crypto/ecdsa/ecdsa.go
@@ -13,7 +13,9 @@ package ecdsa
// http://www.secg.org/download/aid-780/sec1-v2.pdf
import (
+ "crypto"
"crypto/elliptic"
+ "encoding/asn1"
"io"
"math/big"
)
@@ -30,6 +32,28 @@ type PrivateKey struct {
D *big.Int
}
+type ecdsaSignature struct {
+ R, S *big.Int
+}
+
+// Public returns the public key corresponding to priv.
+func (priv *PrivateKey) Public() crypto.PublicKey {
+ return &priv.PublicKey
+}
+
+// Sign signs msg with priv, reading randomness from rand. This method is
+// intended to support keys where the private part is kept in, for example, a
+// hardware module. Common uses should use the Sign function in this package
+// directly.
+func (priv *PrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ([]byte, error) {
+ r, s, err := Sign(rand, priv, msg)
+ if err != nil {
+ return nil, err
+ }
+
+ return asn1.Marshal(ecdsaSignature{r, s})
+}
+
var one = new(big.Int).SetInt64(1)
// randFieldElement returns a random element of the field underlying the given
diff --git a/src/pkg/crypto/md5/gen.go b/src/pkg/crypto/md5/gen.go
index 75295e4fcb..8cd0a6358e 100644
--- a/src/pkg/crypto/md5/gen.go
+++ b/src/pkg/crypto/md5/gen.go
@@ -7,7 +7,7 @@
// This program generates md5block.go
// Invoke as
//
-// go run gen.go [-full] |gofmt >md5block.go
+// go run gen.go [-full] -output md5block.go
//
// The -full flag causes the generated code to do a full
// (16x) unrolling instead of a 4x unrolling.
@@ -15,18 +15,33 @@
package main
import (
+ "bytes"
"flag"
+ "go/format"
+ "io/ioutil"
"log"
- "os"
"strings"
"text/template"
)
+var filename = flag.String("output", "md5block.go", "output file name")
+
func main() {
flag.Parse()
+ var buf bytes.Buffer
+
t := template.Must(template.New("main").Funcs(funcs).Parse(program))
- if err := t.Execute(os.Stdout, data); err != nil {
+ if err := t.Execute(&buf, data); err != nil {
+ log.Fatal(err)
+ }
+
+ data, err := format.Source(buf.Bytes())
+ if err != nil {
+ log.Fatal(err)
+ }
+ err = ioutil.WriteFile(*filename, data, 0644)
+ if err != nil {
log.Fatal(err)
}
}
@@ -165,7 +180,7 @@ var program = `// Copyright 2013 The Go Authors. All rights reserved.
// license that can be found in the LICENSE file.
// DO NOT EDIT.
-// Generate with: go run gen.go{{if .Full}} -full{{end}} | gofmt >md5block.go
+// Generate with: go run gen.go{{if .Full}} -full{{end}} -output md5block.go
package md5
diff --git a/src/pkg/crypto/md5/md5.go b/src/pkg/crypto/md5/md5.go
index 1a1f35fabc..8c50c6d0bf 100644
--- a/src/pkg/crypto/md5/md5.go
+++ b/src/pkg/crypto/md5/md5.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:generate go run gen.go -full -output md5block.go
+
// Package md5 implements the MD5 hash algorithm as defined in RFC 1321.
package md5
diff --git a/src/pkg/crypto/md5/md5block.go b/src/pkg/crypto/md5/md5block.go
index e2a1767775..64e1e7c1ef 100644
--- a/src/pkg/crypto/md5/md5block.go
+++ b/src/pkg/crypto/md5/md5block.go
@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
// DO NOT EDIT.
-// Generate with: go run gen.go -full | gofmt >md5block.go
+// Generate with: go run gen.go -full -output md5block.go
package md5
diff --git a/src/pkg/crypto/md5/md5block_386.s b/src/pkg/crypto/md5/md5block_386.s
index e5c27ac9aa..8e426d148f 100644
--- a/src/pkg/crypto/md5/md5block_386.s
+++ b/src/pkg/crypto/md5/md5block_386.s
@@ -6,7 +6,7 @@
// #defines generating 8a assembly, and adjusted for 386,
// by the Go Authors.
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// MD5 optimized for AMD64.
//
diff --git a/src/pkg/crypto/md5/md5block_amd64.s b/src/pkg/crypto/md5/md5block_amd64.s
index 178e49cd8e..a3ae7d97b2 100644
--- a/src/pkg/crypto/md5/md5block_amd64.s
+++ b/src/pkg/crypto/md5/md5block_amd64.s
@@ -5,7 +5,7 @@
// Translated from Perl generating GNU assembly into
// #defines generating 6a assembly by the Go Authors.
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// MD5 optimized for AMD64.
//
diff --git a/src/pkg/crypto/md5/md5block_amd64p32.s b/src/pkg/crypto/md5/md5block_amd64p32.s
index a78a3f6100..d918a67c51 100644
--- a/src/pkg/crypto/md5/md5block_amd64p32.s
+++ b/src/pkg/crypto/md5/md5block_amd64p32.s
@@ -9,7 +9,7 @@
// replace BP with R11, reloaded before use at return.
// replace R15 with R11.
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// MD5 optimized for AMD64.
//
diff --git a/src/pkg/crypto/md5/md5block_arm.s b/src/pkg/crypto/md5/md5block_arm.s
index e644bfcd61..3b26e549b9 100644
--- a/src/pkg/crypto/md5/md5block_arm.s
+++ b/src/pkg/crypto/md5/md5block_arm.s
@@ -4,7 +4,7 @@
//
// ARM version of md5block.go
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// Register definitions
table = 0 // Pointer to MD5 constants table
diff --git a/src/pkg/crypto/rand/rand_linux.go b/src/pkg/crypto/rand/rand_linux.go
new file mode 100644
index 0000000000..8cb59c75df
--- /dev/null
+++ b/src/pkg/crypto/rand/rand_linux.go
@@ -0,0 +1,39 @@
+// 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 rand
+
+import (
+ "internal/syscall"
+ "sync"
+)
+
+func init() {
+ altGetRandom = getRandomLinux
+}
+
+var (
+ once sync.Once
+ useSyscall bool
+)
+
+func pickStrategy() {
+ // Test whether we should use the system call or /dev/urandom.
+ // We'll fall back to urandom if:
+ // - the kernel is too old (before 3.17)
+ // - the machine has no entropy available (early boot + no hardware
+ // entropy source?) and we want to avoid blocking later.
+ var buf [1]byte
+ n, err := syscall.GetRandom(buf[:], syscall.GRND_NONBLOCK)
+ useSyscall = n == 1 && err == nil
+}
+
+func getRandomLinux(p []byte) (ok bool) {
+ once.Do(pickStrategy)
+ if !useSyscall {
+ return false
+ }
+ n, err := syscall.GetRandom(p, 0)
+ return n == len(p) && err == nil
+}
diff --git a/src/pkg/crypto/rand/rand_unix.go b/src/pkg/crypto/rand/rand_unix.go
index 1e741fda19..62d0fbdb35 100644
--- a/src/pkg/crypto/rand/rand_unix.go
+++ b/src/pkg/crypto/rand/rand_unix.go
@@ -20,6 +20,8 @@ import (
"time"
)
+const urandomDevice = "/dev/urandom"
+
// Easy implementation: read from /dev/urandom.
// This is sufficient on Linux, OS X, and FreeBSD.
@@ -27,7 +29,7 @@ func init() {
if runtime.GOOS == "plan9" {
Reader = newReader(nil)
} else {
- Reader = &devReader{name: "/dev/urandom"}
+ Reader = &devReader{name: urandomDevice}
}
}
@@ -38,7 +40,14 @@ type devReader struct {
mu sync.Mutex
}
+// altGetRandom if non-nil specifies an OS-specific function to get
+// urandom-style randomness.
+var altGetRandom func([]byte) (ok bool)
+
func (r *devReader) Read(b []byte) (n int, err error) {
+ if altGetRandom != nil && r.name == urandomDevice && altGetRandom(b) {
+ return len(b), nil
+ }
r.mu.Lock()
defer r.mu.Unlock()
if r.f == nil {
diff --git a/src/pkg/crypto/rc4/rc4_386.s b/src/pkg/crypto/rc4/rc4_386.s
index b04fc1fb83..54221036ba 100644
--- a/src/pkg/crypto/rc4/rc4_386.s
+++ b/src/pkg/crypto/rc4/rc4_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// func xorKeyStream(dst, src *byte, n int, state *[256]byte, i, j *uint8)
TEXT ·xorKeyStream(SB),NOSPLIT,$0
diff --git a/src/pkg/crypto/rc4/rc4_amd64.s b/src/pkg/crypto/rc4/rc4_amd64.s
index e3234b6c7e..57d941c8f3 100644
--- a/src/pkg/crypto/rc4/rc4_amd64.s
+++ b/src/pkg/crypto/rc4/rc4_amd64.s
@@ -2,7 +2,7 @@
// http://www.zorinaq.com/papers/rc4-amd64.html
// http://www.zorinaq.com/papers/rc4-amd64.tar.bz2
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// Local modifications:
//
diff --git a/src/pkg/crypto/rc4/rc4_amd64p32.s b/src/pkg/crypto/rc4/rc4_amd64p32.s
index 27d8495071..970b34e08e 100644
--- a/src/pkg/crypto/rc4/rc4_amd64p32.s
+++ b/src/pkg/crypto/rc4/rc4_amd64p32.s
@@ -2,7 +2,7 @@
// http://www.zorinaq.com/papers/rc4-amd64.html
// http://www.zorinaq.com/papers/rc4-amd64.tar.bz2
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// Local modifications:
//
diff --git a/src/pkg/crypto/rc4/rc4_arm.s b/src/pkg/crypto/rc4/rc4_arm.s
index b9ac72301c..51be3bf95b 100644
--- a/src/pkg/crypto/rc4/rc4_arm.s
+++ b/src/pkg/crypto/rc4/rc4_arm.s
@@ -4,7 +4,7 @@
// +build !nacl
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// Registers
dst = 0
diff --git a/src/pkg/crypto/rsa/pss.go b/src/pkg/crypto/rsa/pss.go
index 18eafbc05f..e9f2908250 100644
--- a/src/pkg/crypto/rsa/pss.go
+++ b/src/pkg/crypto/rsa/pss.go
@@ -222,6 +222,17 @@ type PSSOptions struct {
// signature. It can either be a number of bytes, or one of the special
// PSSSaltLength constants.
SaltLength int
+
+ // Hash, if not zero, overrides the hash function passed to SignPSS.
+ // This is the only way to specify the hash function when using the
+ // crypto.Signer interface.
+ Hash crypto.Hash
+}
+
+// HashFunc returns pssOpts.Hash so that PSSOptions implements
+// crypto.SignerOpts.
+func (pssOpts *PSSOptions) HashFunc() crypto.Hash {
+ return pssOpts.Hash
}
func (opts *PSSOptions) saltLength() int {
@@ -244,6 +255,10 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte,
saltLength = hash.Size()
}
+ if opts.Hash != 0 {
+ hash = opts.Hash
+ }
+
salt := make([]byte, saltLength)
if _, err = io.ReadFull(rand, salt); err != nil {
return
diff --git a/src/pkg/crypto/rsa/rsa.go b/src/pkg/crypto/rsa/rsa.go
index bce6ba4eba..2702311281 100644
--- a/src/pkg/crypto/rsa/rsa.go
+++ b/src/pkg/crypto/rsa/rsa.go
@@ -6,6 +6,7 @@
package rsa
import (
+ "crypto"
"crypto/rand"
"crypto/subtle"
"errors"
@@ -58,6 +59,24 @@ type PrivateKey struct {
Precomputed PrecomputedValues
}
+// Public returns the public key corresponding to priv.
+func (priv *PrivateKey) Public() crypto.PublicKey {
+ return &priv.PublicKey
+}
+
+// Sign signs msg with priv, reading randomness from rand. If opts is a
+// *PSSOptions then the PSS algorithm will be used, otherwise PKCS#1 v1.5 will
+// be used. This method is intended to support keys where the private part is
+// kept in, for example, a hardware module. Common uses should use the Sign*
+// functions in this package.
+func (priv *PrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ([]byte, error) {
+ if pssOpts, ok := opts.(*PSSOptions); ok {
+ return SignPSS(rand, priv, pssOpts.Hash, msg, pssOpts)
+ }
+
+ return SignPKCS1v15(rand, priv, opts.HashFunc(), msg)
+}
+
type PrecomputedValues struct {
Dp, Dq *big.Int // D mod (P-1) (or mod Q-1)
Qinv *big.Int // Q^-1 mod P
diff --git a/src/pkg/crypto/sha1/sha1block_386.s b/src/pkg/crypto/sha1/sha1block_386.s
index 688851c31e..a0adabc3c3 100644
--- a/src/pkg/crypto/sha1/sha1block_386.s
+++ b/src/pkg/crypto/sha1/sha1block_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// SHA1 block routine. See sha1block.go for Go equivalent.
//
diff --git a/src/pkg/crypto/sha1/sha1block_amd64.s b/src/pkg/crypto/sha1/sha1block_amd64.s
index 8ffb9d5d68..4319df63e1 100644
--- a/src/pkg/crypto/sha1/sha1block_amd64.s
+++ b/src/pkg/crypto/sha1/sha1block_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// SHA1 block routine. See sha1block.go for Go equivalent.
//
diff --git a/src/pkg/crypto/sha1/sha1block_amd64p32.s b/src/pkg/crypto/sha1/sha1block_amd64p32.s
index 3c589d94fe..d93fbf1ed3 100644
--- a/src/pkg/crypto/sha1/sha1block_amd64p32.s
+++ b/src/pkg/crypto/sha1/sha1block_amd64p32.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// SHA1 block routine. See sha1block.go for Go equivalent.
//
diff --git a/src/pkg/crypto/sha1/sha1block_arm.s b/src/pkg/crypto/sha1/sha1block_arm.s
index 5917e8b24b..f11f33dc33 100644
--- a/src/pkg/crypto/sha1/sha1block_arm.s
+++ b/src/pkg/crypto/sha1/sha1block_arm.s
@@ -4,7 +4,7 @@
//
// ARM version of md5block.go
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// SHA1 block routine. See sha1block.go for Go equivalent.
//
diff --git a/src/pkg/crypto/sha256/sha256block_amd64.s b/src/pkg/crypto/sha256/sha256block_amd64.s
index 95aebbe762..868eaed489 100644
--- a/src/pkg/crypto/sha256/sha256block_amd64.s
+++ b/src/pkg/crypto/sha256/sha256block_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// SHA256 block routine. See sha256block.go for Go equivalent.
//
diff --git a/src/pkg/crypto/sha512/sha512block_amd64.s b/src/pkg/crypto/sha512/sha512block_amd64.s
index 344d8d2c3e..2e10233de1 100644
--- a/src/pkg/crypto/sha512/sha512block_amd64.s
+++ b/src/pkg/crypto/sha512/sha512block_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// SHA512 block routine. See sha512block.go for Go equivalent.
//
diff --git a/src/pkg/crypto/tls/common.go b/src/pkg/crypto/tls/common.go
index 2b59136e65..776b70c93c 100644
--- a/src/pkg/crypto/tls/common.go
+++ b/src/pkg/crypto/tls/common.go
@@ -165,6 +165,14 @@ type ConnectionState struct {
ServerName string // server name requested by client, if any (server side only)
PeerCertificates []*x509.Certificate // certificate chain presented by remote peer
VerifiedChains [][]*x509.Certificate // verified chains built from PeerCertificates
+
+ // TLSUnique contains the "tls-unique" channel binding value (see RFC
+ // 5929, section 3). For resumed sessions this value will be nil
+ // because resumption does not include enough context (see
+ // https://secure-resumption.com/#channelbindings). This will change in
+ // future versions of Go once the TLS master-secret fix has been
+ // standardized and implemented.
+ TLSUnique []byte
}
// ClientAuthType declares the policy the server will follow for
@@ -479,7 +487,12 @@ func (c *Config) BuildNameToCertificate() {
// A Certificate is a chain of one or more certificates, leaf first.
type Certificate struct {
Certificate [][]byte
- PrivateKey crypto.PrivateKey // supported types: *rsa.PrivateKey, *ecdsa.PrivateKey
+ // PrivateKey contains the private key corresponding to the public key
+ // in Leaf. For a server, this must be a *rsa.PrivateKey or
+ // *ecdsa.PrivateKey. For a client doing client authentication, this
+ // can be any type that implements crypto.Signer (which includes RSA
+ // and ECDSA private keys).
+ PrivateKey crypto.PrivateKey
// OCSPStaple contains an optional OCSP response which will be served
// to clients that request it.
OCSPStaple []byte
diff --git a/src/pkg/crypto/tls/conn.go b/src/pkg/crypto/tls/conn.go
index 8f7d2c144f..ba8e4c22b7 100644
--- a/src/pkg/crypto/tls/conn.go
+++ b/src/pkg/crypto/tls/conn.go
@@ -42,6 +42,9 @@ type Conn struct {
verifiedChains [][]*x509.Certificate
// serverName contains the server name indicated by the client, if any.
serverName string
+ // firstFinished contains the first Finished hash sent during the
+ // handshake. This is the "tls-unique" channel binding value.
+ firstFinished [12]byte
clientProtocol string
clientProtocolFallback bool
@@ -994,6 +997,9 @@ func (c *Conn) ConnectionState() ConnectionState {
state.PeerCertificates = c.peerCertificates
state.VerifiedChains = c.verifiedChains
state.ServerName = c.serverName
+ if !c.didResume {
+ state.TLSUnique = c.firstFinished[:]
+ }
}
return state
diff --git a/src/pkg/crypto/tls/handshake_client.go b/src/pkg/crypto/tls/handshake_client.go
index 694a9a217f..7f662e9c9f 100644
--- a/src/pkg/crypto/tls/handshake_client.go
+++ b/src/pkg/crypto/tls/handshake_client.go
@@ -6,11 +6,11 @@ package tls
import (
"bytes"
+ "crypto"
"crypto/ecdsa"
"crypto/rsa"
"crypto/subtle"
"crypto/x509"
- "encoding/asn1"
"errors"
"fmt"
"io"
@@ -187,10 +187,10 @@ NextCipherSuite:
if err := hs.readSessionTicket(); err != nil {
return err
}
- if err := hs.readFinished(); err != nil {
+ if err := hs.readFinished(c.firstFinished[:]); err != nil {
return err
}
- if err := hs.sendFinished(); err != nil {
+ if err := hs.sendFinished(nil); err != nil {
return err
}
} else {
@@ -200,13 +200,13 @@ NextCipherSuite:
if err := hs.establishKeys(); err != nil {
return err
}
- if err := hs.sendFinished(); err != nil {
+ if err := hs.sendFinished(c.firstFinished[:]); err != nil {
return err
}
if err := hs.readSessionTicket(); err != nil {
return err
}
- if err := hs.readFinished(); err != nil {
+ if err := hs.readFinished(nil); err != nil {
return err
}
}
@@ -345,8 +345,8 @@ func (hs *clientHandshakeState) doFullHandshake() error {
}
// We need to search our list of client certs for one
- // where SignatureAlgorithm is RSA and the Issuer is in
- // certReq.certificateAuthorities
+ // where SignatureAlgorithm is acceptable to the server and the
+ // Issuer is in certReq.certificateAuthorities
findCert:
for i, chain := range c.config.Certificates {
if !rsaAvail && !ecdsaAvail {
@@ -373,7 +373,7 @@ func (hs *clientHandshakeState) doFullHandshake() error {
if len(certReq.certificateAuthorities) == 0 {
// they gave us an empty list, so just take the
- // first RSA cert from c.config.Certificates
+ // first cert from c.config.Certificates
chainToSend = &chain
break findCert
}
@@ -428,22 +428,24 @@ func (hs *clientHandshakeState) doFullHandshake() error {
hasSignatureAndHash: c.vers >= VersionTLS12,
}
- switch key := c.config.Certificates[0].PrivateKey.(type) {
- case *ecdsa.PrivateKey:
- digest, _, hashId := hs.finishedHash.hashForClientCertificate(signatureECDSA)
- r, s, err := ecdsa.Sign(c.config.rand(), key, digest)
- if err == nil {
- signed, err = asn1.Marshal(ecdsaSignature{r, s})
- }
+ key, ok := chainToSend.PrivateKey.(crypto.Signer)
+ if !ok {
+ c.sendAlert(alertInternalError)
+ return fmt.Errorf("tls: client certificate private key of type %T does not implement crypto.Signer", chainToSend.PrivateKey)
+ }
+ switch key.Public().(type) {
+ case *ecdsa.PublicKey:
+ digest, hashFunc, hashId := hs.finishedHash.hashForClientCertificate(signatureECDSA)
+ signed, err = key.Sign(c.config.rand(), digest, hashFunc)
certVerify.signatureAndHash.signature = signatureECDSA
certVerify.signatureAndHash.hash = hashId
- case *rsa.PrivateKey:
+ case *rsa.PublicKey:
digest, hashFunc, hashId := hs.finishedHash.hashForClientCertificate(signatureRSA)
- signed, err = rsa.SignPKCS1v15(c.config.rand(), key, hashFunc, digest)
+ signed, err = key.Sign(c.config.rand(), digest, hashFunc)
certVerify.signatureAndHash.signature = signatureRSA
certVerify.signatureAndHash.hash = hashId
default:
- err = errors.New("unknown private key type")
+ err = fmt.Errorf("tls: unknown client certificate key type: %T", key)
}
if err != nil {
c.sendAlert(alertInternalError)
@@ -530,7 +532,7 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) {
return false, nil
}
-func (hs *clientHandshakeState) readFinished() error {
+func (hs *clientHandshakeState) readFinished(out []byte) error {
c := hs.c
c.readRecord(recordTypeChangeCipherSpec)
@@ -555,6 +557,7 @@ func (hs *clientHandshakeState) readFinished() error {
return errors.New("tls: server's Finished message was incorrect")
}
hs.finishedHash.Write(serverFinished.marshal())
+ copy(out, verify)
return nil
}
@@ -586,7 +589,7 @@ func (hs *clientHandshakeState) readSessionTicket() error {
return nil
}
-func (hs *clientHandshakeState) sendFinished() error {
+func (hs *clientHandshakeState) sendFinished(out []byte) error {
c := hs.c
c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
@@ -605,6 +608,7 @@ func (hs *clientHandshakeState) sendFinished() error {
finished.verifyData = hs.finishedHash.clientSum(hs.masterSecret)
hs.finishedHash.Write(finished.marshal())
c.writeRecord(recordTypeHandshake, finished.marshal())
+ copy(out, finished.verifyData)
return nil
}
diff --git a/src/pkg/crypto/tls/handshake_client_test.go b/src/pkg/crypto/tls/handshake_client_test.go
index 432308b0a8..e5eaa7de20 100644
--- a/src/pkg/crypto/tls/handshake_client_test.go
+++ b/src/pkg/crypto/tls/handshake_client_test.go
@@ -205,7 +205,7 @@ func (test *clientTest) run(t *testing.T, write bool) {
if !write {
flows, err := test.loadData()
if err != nil {
- t.Fatalf("%s: failed to load data from %s", test.name, test.dataPath())
+ t.Fatalf("%s: failed to load data from %s: %v", test.name, test.dataPath(), err)
}
for i, b := range flows {
if i%2 == 1 {
diff --git a/src/pkg/crypto/tls/handshake_server.go b/src/pkg/crypto/tls/handshake_server.go
index 39eeb363cd..684ab288f0 100644
--- a/src/pkg/crypto/tls/handshake_server.go
+++ b/src/pkg/crypto/tls/handshake_server.go
@@ -57,10 +57,10 @@ func (c *Conn) serverHandshake() error {
if err := hs.establishKeys(); err != nil {
return err
}
- if err := hs.sendFinished(); err != nil {
+ if err := hs.sendFinished(c.firstFinished[:]); err != nil {
return err
}
- if err := hs.readFinished(); err != nil {
+ if err := hs.readFinished(nil); err != nil {
return err
}
c.didResume = true
@@ -73,13 +73,13 @@ func (c *Conn) serverHandshake() error {
if err := hs.establishKeys(); err != nil {
return err
}
- if err := hs.readFinished(); err != nil {
+ if err := hs.readFinished(c.firstFinished[:]); err != nil {
return err
}
if err := hs.sendSessionTicket(); err != nil {
return err
}
- if err := hs.sendFinished(); err != nil {
+ if err := hs.sendFinished(nil); err != nil {
return err
}
}
@@ -483,7 +483,7 @@ func (hs *serverHandshakeState) establishKeys() error {
return nil
}
-func (hs *serverHandshakeState) readFinished() error {
+func (hs *serverHandshakeState) readFinished(out []byte) error {
c := hs.c
c.readRecord(recordTypeChangeCipherSpec)
@@ -523,6 +523,7 @@ func (hs *serverHandshakeState) readFinished() error {
}
hs.finishedHash.Write(clientFinished.marshal())
+ copy(out, verify)
return nil
}
@@ -552,7 +553,7 @@ func (hs *serverHandshakeState) sendSessionTicket() error {
return nil
}
-func (hs *serverHandshakeState) sendFinished() error {
+func (hs *serverHandshakeState) sendFinished(out []byte) error {
c := hs.c
c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
@@ -563,6 +564,7 @@ func (hs *serverHandshakeState) sendFinished() error {
c.writeRecord(recordTypeHandshake, finished.marshal())
c.cipherSuite = hs.suite.id
+ copy(out, finished.verifyData)
return nil
}
diff --git a/src/pkg/crypto/tls/tls_test.go b/src/pkg/crypto/tls/tls_test.go
index f8c94ff35d..e82579eee9 100644
--- a/src/pkg/crypto/tls/tls_test.go
+++ b/src/pkg/crypto/tls/tls_test.go
@@ -5,6 +5,7 @@
package tls
import (
+ "bytes"
"fmt"
"io"
"net"
@@ -235,3 +236,47 @@ func testConnReadNonzeroAndEOF(t *testing.T, delay time.Duration) error {
}
return nil
}
+
+func TestTLSUniqueMatches(t *testing.T) {
+ ln := newLocalListener(t)
+ defer ln.Close()
+
+ serverTLSUniques := make(chan []byte)
+ go func() {
+ for i := 0; i < 2; i++ {
+ sconn, err := ln.Accept()
+ if err != nil {
+ t.Fatal(err)
+ }
+ serverConfig := *testConfig
+ srv := Server(sconn, &serverConfig)
+ if err := srv.Handshake(); err != nil {
+ t.Fatal(err)
+ }
+ serverTLSUniques <- srv.ConnectionState().TLSUnique
+ }
+ }()
+
+ clientConfig := *testConfig
+ clientConfig.ClientSessionCache = NewLRUClientSessionCache(1)
+ conn, err := Dial("tcp", ln.Addr().String(), &clientConfig)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(conn.ConnectionState().TLSUnique, <-serverTLSUniques) {
+ t.Error("client and server channel bindings differ")
+ }
+ conn.Close()
+
+ conn, err = Dial("tcp", ln.Addr().String(), &clientConfig)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer conn.Close()
+ if !conn.ConnectionState().DidResume {
+ t.Error("second session did not use resumption")
+ }
+ if !bytes.Equal(conn.ConnectionState().TLSUnique, <-serverTLSUniques) {
+ t.Error("client and server channel bindings differ when session resumption is used")
+ }
+}
diff --git a/src/pkg/crypto/x509/verify.go b/src/pkg/crypto/x509/verify.go
index 5fd8e37174..ec1981423d 100644
--- a/src/pkg/crypto/x509/verify.go
+++ b/src/pkg/crypto/x509/verify.go
@@ -116,10 +116,9 @@ func (e UnknownAuthorityError) Error() string {
}
// SystemRootsError results when we fail to load the system root certificates.
-type SystemRootsError struct {
-}
+type SystemRootsError struct{}
-func (e SystemRootsError) Error() string {
+func (SystemRootsError) Error() string {
return "x509: failed to load system roots and no roots provided"
}
@@ -206,6 +205,9 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V
// needed. If successful, it returns one or more chains where the first
// element of the chain is c and the last element is from opts.Roots.
//
+// If opts.Roots is nil and system roots are unavailable the returned error
+// will be of type SystemRootsError.
+//
// WARNING: this doesn't do any revocation checking.
func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error) {
// Use Windows's own verification and chain building.
diff --git a/src/pkg/database/sql/convert_test.go b/src/pkg/database/sql/convert_test.go
index 6e24830128..98af9fb64c 100644
--- a/src/pkg/database/sql/convert_test.go
+++ b/src/pkg/database/sql/convert_test.go
@@ -283,6 +283,26 @@ func TestValueConverters(t *testing.T) {
// Tests that assigning to RawBytes doesn't allocate (and also works).
func TestRawBytesAllocs(t *testing.T) {
+ var tests = []struct {
+ name string
+ in interface{}
+ want string
+ }{
+ {"uint64", uint64(12345678), "12345678"},
+ {"uint32", uint32(1234), "1234"},
+ {"uint16", uint16(12), "12"},
+ {"uint8", uint8(1), "1"},
+ {"uint", uint(123), "123"},
+ {"int", int(123), "123"},
+ {"int8", int8(1), "1"},
+ {"int16", int16(12), "12"},
+ {"int32", int32(1234), "1234"},
+ {"int64", int64(12345678), "12345678"},
+ {"float32", float32(1.5), "1.5"},
+ {"float64", float64(64), "64"},
+ {"bool", false, "false"},
+ }
+
buf := make(RawBytes, 10)
test := func(name string, in interface{}, want string) {
if err := convertAssign(&buf, in); err != nil {
@@ -301,20 +321,11 @@ func TestRawBytesAllocs(t *testing.T) {
t.Fatalf("%s: got %q (len %d); want %q (len %d)", name, buf, len(buf), want, len(want))
}
}
+
n := testing.AllocsPerRun(100, func() {
- test("uint64", uint64(12345678), "12345678")
- test("uint32", uint32(1234), "1234")
- test("uint16", uint16(12), "12")
- test("uint8", uint8(1), "1")
- test("uint", uint(123), "123")
- test("int", int(123), "123")
- test("int8", int8(1), "1")
- test("int16", int16(12), "12")
- test("int32", int32(1234), "1234")
- test("int64", int64(12345678), "12345678")
- test("float32", float32(1.5), "1.5")
- test("float64", float64(64), "64")
- test("bool", false, "false")
+ for _, tt := range tests {
+ test(tt.name, tt.in, tt.want)
+ }
})
// The numbers below are only valid for 64-bit interface word sizes,
diff --git a/src/pkg/database/sql/sql.go b/src/pkg/database/sql/sql.go
index 690fc80d68..90f813d823 100644
--- a/src/pkg/database/sql/sql.go
+++ b/src/pkg/database/sql/sql.go
@@ -13,7 +13,6 @@
package sql
import (
- "container/list"
"database/sql/driver"
"errors"
"fmt"
@@ -198,8 +197,8 @@ type DB struct {
dsn string
mu sync.Mutex // protects following fields
- freeConn *list.List // of *driverConn
- connRequests *list.List // of connRequest
+ freeConn []*driverConn
+ connRequests []chan connRequest
numOpen int
pendingOpens int
// Used to signal the need for new connections
@@ -232,9 +231,6 @@ type driverConn struct {
inUse bool
onPut []func() // code (with db.mu held) run when conn is next returned
dbmuClosed bool // same as closed, but guarded by db.mu, for connIfFree
- // This is the Element returned by db.freeConn.PushFront(conn).
- // It's used by connIfFree to remove the conn from the freeConn list.
- listElem *list.Element
}
func (dc *driverConn) releaseConn(err error) {
@@ -437,8 +433,6 @@ func Open(driverName, dataSourceName string) (*DB, error) {
openerCh: make(chan struct{}, connectionRequestQueueSize),
lastPut: make(map[*driverConn]string),
}
- db.freeConn = list.New()
- db.connRequests = list.New()
go db.connectionOpener()
return db, nil
}
@@ -469,17 +463,13 @@ func (db *DB) Close() error {
}
close(db.openerCh)
var err error
- fns := make([]func() error, 0, db.freeConn.Len())
- for db.freeConn.Front() != nil {
- dc := db.freeConn.Front().Value.(*driverConn)
- dc.listElem = nil
+ fns := make([]func() error, 0, len(db.freeConn))
+ for _, dc := range db.freeConn {
fns = append(fns, dc.closeDBLocked())
- db.freeConn.Remove(db.freeConn.Front())
}
+ db.freeConn = nil
db.closed = true
- for db.connRequests.Front() != nil {
- req := db.connRequests.Front().Value.(connRequest)
- db.connRequests.Remove(db.connRequests.Front())
+ for _, req := range db.connRequests {
close(req)
}
db.mu.Unlock()
@@ -527,11 +517,11 @@ func (db *DB) SetMaxIdleConns(n int) {
db.maxIdle = db.maxOpen
}
var closing []*driverConn
- for db.freeConn.Len() > db.maxIdleConnsLocked() {
- dc := db.freeConn.Back().Value.(*driverConn)
- dc.listElem = nil
- db.freeConn.Remove(db.freeConn.Back())
- closing = append(closing, dc)
+ idleCount := len(db.freeConn)
+ maxIdle := db.maxIdleConnsLocked()
+ if idleCount > maxIdle {
+ closing = db.freeConn[maxIdle:]
+ db.freeConn = db.freeConn[:maxIdle]
}
db.mu.Unlock()
for _, c := range closing {
@@ -564,7 +554,7 @@ func (db *DB) SetMaxOpenConns(n int) {
// If there are connRequests and the connection limit hasn't been reached,
// then tell the connectionOpener to open new connections.
func (db *DB) maybeOpenNewConnections() {
- numRequests := db.connRequests.Len() - db.pendingOpens
+ numRequests := len(db.connRequests) - db.pendingOpens
if db.maxOpen > 0 {
numCanOpen := db.maxOpen - (db.numOpen + db.pendingOpens)
if numRequests > numCanOpen {
@@ -616,7 +606,10 @@ func (db *DB) openNewConnection() {
// connRequest represents one request for a new connection
// When there are no idle connections available, DB.conn will create
// a new connRequest and put it on the db.connRequests list.
-type connRequest chan<- interface{} // takes either a *driverConn or an error
+type connRequest struct {
+ conn *driverConn
+ err error
+}
var errDBClosed = errors.New("sql: database is closed")
@@ -630,32 +623,21 @@ func (db *DB) conn() (*driverConn, error) {
// If db.maxOpen > 0 and the number of open connections is over the limit
// and there are no free connection, make a request and wait.
- if db.maxOpen > 0 && db.numOpen >= db.maxOpen && db.freeConn.Len() == 0 {
+ if db.maxOpen > 0 && db.numOpen >= db.maxOpen && len(db.freeConn) == 0 {
// Make the connRequest channel. It's buffered so that the
// connectionOpener doesn't block while waiting for the req to be read.
- ch := make(chan interface{}, 1)
- req := connRequest(ch)
- db.connRequests.PushBack(req)
+ req := make(chan connRequest, 1)
+ db.connRequests = append(db.connRequests, req)
db.maybeOpenNewConnections()
db.mu.Unlock()
- ret, ok := <-ch
- if !ok {
- return nil, errDBClosed
- }
- switch ret.(type) {
- case *driverConn:
- return ret.(*driverConn), nil
- case error:
- return nil, ret.(error)
- default:
- panic("sql: Unexpected type passed through connRequest.ch")
- }
+ ret := <-req
+ return ret.conn, ret.err
}
- if f := db.freeConn.Front(); f != nil {
- conn := f.Value.(*driverConn)
- conn.listElem = nil
- db.freeConn.Remove(f)
+ if c := len(db.freeConn); c > 0 {
+ conn := db.freeConn[0]
+ copy(db.freeConn, db.freeConn[1:])
+ db.freeConn = db.freeConn[:c-1]
conn.inUse = true
db.mu.Unlock()
return conn, nil
@@ -702,9 +684,15 @@ func (db *DB) connIfFree(wanted *driverConn) (*driverConn, error) {
if wanted.inUse {
return nil, errConnBusy
}
- if wanted.listElem != nil {
- db.freeConn.Remove(wanted.listElem)
- wanted.listElem = nil
+ idx := -1
+ for ii, v := range db.freeConn {
+ if v == wanted {
+ idx = ii
+ break
+ }
+ }
+ if idx >= 0 {
+ db.freeConn = append(db.freeConn[:idx], db.freeConn[idx+1:]...)
wanted.inUse = true
return wanted, nil
}
@@ -793,18 +781,23 @@ func (db *DB) putConn(dc *driverConn, err error) {
// If a connRequest was fulfilled or the *driverConn was placed in the
// freeConn list, then true is returned, otherwise false is returned.
func (db *DB) putConnDBLocked(dc *driverConn, err error) bool {
- if db.connRequests.Len() > 0 {
- req := db.connRequests.Front().Value.(connRequest)
- db.connRequests.Remove(db.connRequests.Front())
- if err != nil {
- req <- err
- } else {
+ if c := len(db.connRequests); c > 0 {
+ req := db.connRequests[0]
+ // This copy is O(n) but in practice faster than a linked list.
+ // TODO: consider compacting it down less often and
+ // moving the base instead?
+ copy(db.connRequests, db.connRequests[1:])
+ db.connRequests = db.connRequests[:c-1]
+ if err == nil {
dc.inUse = true
- req <- dc
+ }
+ req <- connRequest{
+ conn: dc,
+ err: err,
}
return true
- } else if err == nil && !db.closed && db.maxIdleConnsLocked() > db.freeConn.Len() {
- dc.listElem = db.freeConn.PushFront(dc)
+ } else if err == nil && !db.closed && db.maxIdleConnsLocked() > len(db.freeConn) {
+ db.freeConn = append(db.freeConn, dc)
return true
}
return false
@@ -1333,15 +1326,12 @@ func (s *Stmt) connStmt() (ci *driverConn, releaseConn func(error), si driver.St
return ci, releaseConn, s.txsi.si, nil
}
- var cs connStmt
- match := false
for i := 0; i < len(s.css); i++ {
v := s.css[i]
_, err := s.db.connIfFree(v.dc)
if err == nil {
- match = true
- cs = v
- break
+ s.mu.Unlock()
+ return v.dc, v.dc.releaseConn, v.si, nil
}
if err == errConnClosed {
// Lazily remove dead conn from our freelist.
@@ -1353,28 +1343,41 @@ func (s *Stmt) connStmt() (ci *driverConn, releaseConn func(error), si driver.St
}
s.mu.Unlock()
- // Make a new conn if all are busy.
- // TODO(bradfitz): or wait for one? make configurable later?
- if !match {
- dc, err := s.db.conn()
- if err != nil {
- return nil, nil, nil, err
- }
- dc.Lock()
- si, err := dc.prepareLocked(s.query)
- dc.Unlock()
- if err != nil {
- s.db.putConn(dc, err)
- return nil, nil, nil, err
+ // If all connections are busy, either wait for one to become available (if
+ // we've already hit the maximum number of open connections) or create a
+ // new one.
+ //
+ // TODO(bradfitz): or always wait for one? make configurable later?
+ dc, err := s.db.conn()
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ // Do another pass over the list to see whether this statement has
+ // already been prepared on the connection assigned to us.
+ s.mu.Lock()
+ for _, v := range s.css {
+ if v.dc == dc {
+ s.mu.Unlock()
+ return dc, dc.releaseConn, v.si, nil
}
- s.mu.Lock()
- cs = connStmt{dc, si}
- s.css = append(s.css, cs)
- s.mu.Unlock()
}
+ s.mu.Unlock()
+
+ // No luck; we need to prepare the statement on this connection
+ dc.Lock()
+ si, err = dc.prepareLocked(s.query)
+ dc.Unlock()
+ if err != nil {
+ s.db.putConn(dc, err)
+ return nil, nil, nil, err
+ }
+ s.mu.Lock()
+ cs := connStmt{dc, si}
+ s.css = append(s.css, cs)
+ s.mu.Unlock()
- conn := cs.dc
- return conn, conn.releaseConn, cs.si, nil
+ return dc, dc.releaseConn, si, nil
}
// Query executes a prepared query statement with the given arguments
diff --git a/src/pkg/database/sql/sql_test.go b/src/pkg/database/sql/sql_test.go
index 71c81d6f76..12e5a6fd6f 100644
--- a/src/pkg/database/sql/sql_test.go
+++ b/src/pkg/database/sql/sql_test.go
@@ -24,7 +24,14 @@ func init() {
}
freedFrom := make(map[dbConn]string)
putConnHook = func(db *DB, c *driverConn) {
- if c.listElem != nil {
+ idx := -1
+ for i, v := range db.freeConn {
+ if v == c {
+ idx = i
+ break
+ }
+ }
+ if idx >= 0 {
// print before panic, as panic may get lost due to conflicting panic
// (all goroutines asleep) elsewhere, since we might not unlock
// the mutex in freeConn here.
@@ -79,15 +86,14 @@ func closeDB(t testing.TB, db *DB) {
t.Errorf("Error closing fakeConn: %v", err)
}
})
- for node, i := db.freeConn.Front(), 0; node != nil; node, i = node.Next(), i+1 {
- dc := node.Value.(*driverConn)
+ for i, dc := range db.freeConn {
if n := len(dc.openStmt); n > 0 {
// Just a sanity check. This is legal in
// general, but if we make the tests clean up
// their statements first, then we can safely
// verify this is always zero here, and any
// other value is a leak.
- t.Errorf("while closing db, freeConn %d/%d had %d open stmts; want 0", i, db.freeConn.Len(), n)
+ t.Errorf("while closing db, freeConn %d/%d had %d open stmts; want 0", i, len(db.freeConn), n)
}
}
err := db.Close()
@@ -105,10 +111,10 @@ func closeDB(t testing.TB, db *DB) {
// numPrepares assumes that db has exactly 1 idle conn and returns
// its count of calls to Prepare
func numPrepares(t *testing.T, db *DB) int {
- if n := db.freeConn.Len(); n != 1 {
+ if n := len(db.freeConn); n != 1 {
t.Fatalf("free conns = %d; want 1", n)
}
- return (db.freeConn.Front().Value.(*driverConn)).ci.(*fakeConn).numPrepare
+ return db.freeConn[0].ci.(*fakeConn).numPrepare
}
func (db *DB) numDeps() int {
@@ -133,7 +139,7 @@ func (db *DB) numDepsPollUntil(want int, d time.Duration) int {
func (db *DB) numFreeConns() int {
db.mu.Lock()
defer db.mu.Unlock()
- return db.freeConn.Len()
+ return len(db.freeConn)
}
func (db *DB) dumpDeps(t *testing.T) {
@@ -650,10 +656,10 @@ func TestQueryRowClosingStmt(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- if db.freeConn.Len() != 1 {
+ if len(db.freeConn) != 1 {
t.Fatalf("expected 1 free conn")
}
- fakeConn := (db.freeConn.Front().Value.(*driverConn)).ci.(*fakeConn)
+ fakeConn := db.freeConn[0].ci.(*fakeConn)
if made, closed := fakeConn.stmtsMade, fakeConn.stmtsClosed; made != closed {
t.Errorf("statement close mismatch: made %d, closed %d", made, closed)
}
@@ -878,13 +884,13 @@ func TestMaxIdleConns(t *testing.T) {
t.Fatal(err)
}
tx.Commit()
- if got := db.freeConn.Len(); got != 1 {
+ if got := len(db.freeConn); got != 1 {
t.Errorf("freeConns = %d; want 1", got)
}
db.SetMaxIdleConns(0)
- if got := db.freeConn.Len(); got != 0 {
+ if got := len(db.freeConn); got != 0 {
t.Errorf("freeConns after set to zero = %d; want 0", got)
}
@@ -893,7 +899,7 @@ func TestMaxIdleConns(t *testing.T) {
t.Fatal(err)
}
tx.Commit()
- if got := db.freeConn.Len(); got != 0 {
+ if got := len(db.freeConn); got != 0 {
t.Errorf("freeConns = %d; want 0", got)
}
}
@@ -1180,10 +1186,10 @@ func TestCloseConnBeforeStmts(t *testing.T) {
t.Fatal(err)
}
- if db.freeConn.Len() != 1 {
- t.Fatalf("expected 1 freeConn; got %d", db.freeConn.Len())
+ if len(db.freeConn) != 1 {
+ t.Fatalf("expected 1 freeConn; got %d", len(db.freeConn))
}
- dc := db.freeConn.Front().Value.(*driverConn)
+ dc := db.freeConn[0]
if dc.closed {
t.Errorf("conn shouldn't be closed")
}
@@ -1342,6 +1348,11 @@ func TestErrBadConnReconnect(t *testing.T) {
return nil
})
+ // Provide a way to force a re-prepare of a statement on next execution
+ forcePrepare := func(stmt *Stmt) {
+ stmt.css = nil
+ }
+
// stmt.Exec
stmt1, err := db.Prepare("INSERT|t1|name=?,age=?,dead=?")
if err != nil {
@@ -1349,9 +1360,7 @@ func TestErrBadConnReconnect(t *testing.T) {
}
defer stmt1.Close()
// make sure we must prepare the stmt first
- for _, cs := range stmt1.css {
- cs.dc.inUse = true
- }
+ forcePrepare(stmt1)
stmtExec := func() error {
_, err := stmt1.Exec("Gopher", 3, false)
@@ -1367,9 +1376,7 @@ func TestErrBadConnReconnect(t *testing.T) {
}
defer stmt2.Close()
// make sure we must prepare the stmt first
- for _, cs := range stmt2.css {
- cs.dc.inUse = true
- }
+ forcePrepare(stmt2)
stmtQuery := func() error {
rows, err := stmt2.Query()
diff --git a/src/pkg/debug/dwarf/type.go b/src/pkg/debug/dwarf/type.go
index e59737b0a4..fa40b2bef1 100644
--- a/src/pkg/debug/dwarf/type.go
+++ b/src/pkg/debug/dwarf/type.go
@@ -88,7 +88,7 @@ type AddrType struct {
BasicType
}
-// A UnspecifiedType represents implicit, unknown, ambiguous or nonexistent type.
+// An UnspecifiedType represents an implicit, unknown, ambiguous or nonexistent type.
type UnspecifiedType struct {
BasicType
}
@@ -118,7 +118,12 @@ func (t *ArrayType) String() string {
return "[" + strconv.FormatInt(t.Count, 10) + "]" + t.Type.String()
}
-func (t *ArrayType) Size() int64 { return t.Count * t.Type.Size() }
+func (t *ArrayType) Size() int64 {
+ if t.Count == -1 {
+ return 0
+ }
+ return t.Count * t.Type.Size()
+}
// A VoidType represents the C void type.
type VoidType struct {
@@ -369,7 +374,7 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
t.StrideBitSize, _ = e.Val(AttrStrideSize).(int64)
// Accumulate dimensions,
- ndim := 0
+ var dims []int64
for kid := next(); kid != nil; kid = next() {
// TODO(rsc): Can also be TagEnumerationType
// but haven't seen that in the wild yet.
@@ -381,26 +386,24 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
count, ok = kid.Val(AttrUpperBound).(int64)
if ok {
count++ // Length is one more than upper bound.
- } else {
+ } else if len(dims) == 0 {
count = -1 // As in x[].
}
}
- if ndim == 0 {
- t.Count = count
- } else {
- // Multidimensional array.
- // Create new array type underneath this one.
- t.Type = &ArrayType{Type: t.Type, Count: count}
- }
- ndim++
+ dims = append(dims, count)
case TagEnumerationType:
err = DecodeError{name, kid.Offset, "cannot handle enumeration type as array bound"}
goto Error
}
}
- if ndim == 0 {
+ if len(dims) == 0 {
// LLVM generates this for x[].
- t.Count = -1
+ dims = []int64{-1}
+ }
+
+ t.Count = dims[0]
+ for i := len(dims) - 1; i >= 1; i-- {
+ t.Type = &ArrayType{Type: t.Type, Count: dims[i]}
}
case TagBaseType:
@@ -476,7 +479,7 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
t.StructName, _ = e.Val(AttrName).(string)
t.Incomplete = e.Val(AttrDeclaration) != nil
t.Field = make([]*StructField, 0, 8)
- var lastFieldType Type
+ var lastFieldType *Type
var lastFieldBitOffset int64
for kid := next(); kid != nil; kid = next() {
if kid.Tag == TagMember {
@@ -518,7 +521,7 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
// (DWARF writes out 0-length arrays as if they were 1-length arrays.)
zeroArray(lastFieldType)
}
- lastFieldType = f.Type
+ lastFieldType = &f.Type
lastFieldBitOffset = bito
}
}
@@ -667,13 +670,16 @@ Error:
return nil, err
}
-func zeroArray(t Type) {
- for {
- at, ok := t.(*ArrayType)
- if !ok {
- break
- }
- at.Count = 0
- t = at.Type
+func zeroArray(t *Type) {
+ if t == nil {
+ return
+ }
+ at, ok := (*t).(*ArrayType)
+ if !ok || at.Type.Size() == 0 {
+ return
}
+ // Make a copy to avoid invalidating typeCache.
+ tt := *at
+ tt.Count = 0
+ *t = &tt
}
diff --git a/src/pkg/debug/elf/elf.go b/src/pkg/debug/elf/elf.go
index 51319c0ddc..46e9d5735d 100644
--- a/src/pkg/debug/elf/elf.go
+++ b/src/pkg/debug/elf/elf.go
@@ -11,6 +11,7 @@
* $FreeBSD: src/sys/i386/include/elf.h,v 1.16 2004/08/02 19:12:17 dfr Exp $
* $FreeBSD: src/sys/powerpc/include/elf.h,v 1.7 2004/11/02 09:47:01 ssouhlal Exp $
* $FreeBSD: src/sys/sparc64/include/elf.h,v 1.12 2003/09/25 01:10:26 peter Exp $
+ * "ELF for the ARM® 64-bit Architecture (AArch64)" (ARM IHI 0056B)
*
* Copyright (c) 1996-1998 John D. Polstra. All rights reserved.
* Copyright (c) 2001 David E. O'Brien
@@ -192,49 +193,50 @@ func (i Type) GoString() string { return stringName(uint32(i), typeStrings, true
type Machine uint16
const (
- EM_NONE Machine = 0 /* Unknown machine. */
- EM_M32 Machine = 1 /* AT&T WE32100. */
- EM_SPARC Machine = 2 /* Sun SPARC. */
- EM_386 Machine = 3 /* Intel i386. */
- EM_68K Machine = 4 /* Motorola 68000. */
- EM_88K Machine = 5 /* Motorola 88000. */
- EM_860 Machine = 7 /* Intel i860. */
- EM_MIPS Machine = 8 /* MIPS R3000 Big-Endian only. */
- EM_S370 Machine = 9 /* IBM System/370. */
- EM_MIPS_RS3_LE Machine = 10 /* MIPS R3000 Little-Endian. */
- EM_PARISC Machine = 15 /* HP PA-RISC. */
- EM_VPP500 Machine = 17 /* Fujitsu VPP500. */
- EM_SPARC32PLUS Machine = 18 /* SPARC v8plus. */
- EM_960 Machine = 19 /* Intel 80960. */
- EM_PPC Machine = 20 /* PowerPC 32-bit. */
- EM_PPC64 Machine = 21 /* PowerPC 64-bit. */
- EM_S390 Machine = 22 /* IBM System/390. */
- EM_V800 Machine = 36 /* NEC V800. */
- EM_FR20 Machine = 37 /* Fujitsu FR20. */
- EM_RH32 Machine = 38 /* TRW RH-32. */
- EM_RCE Machine = 39 /* Motorola RCE. */
- EM_ARM Machine = 40 /* ARM. */
- EM_SH Machine = 42 /* Hitachi SH. */
- EM_SPARCV9 Machine = 43 /* SPARC v9 64-bit. */
- EM_TRICORE Machine = 44 /* Siemens TriCore embedded processor. */
- EM_ARC Machine = 45 /* Argonaut RISC Core. */
- EM_H8_300 Machine = 46 /* Hitachi H8/300. */
- EM_H8_300H Machine = 47 /* Hitachi H8/300H. */
- EM_H8S Machine = 48 /* Hitachi H8S. */
- EM_H8_500 Machine = 49 /* Hitachi H8/500. */
- EM_IA_64 Machine = 50 /* Intel IA-64 Processor. */
- EM_MIPS_X Machine = 51 /* Stanford MIPS-X. */
- EM_COLDFIRE Machine = 52 /* Motorola ColdFire. */
- EM_68HC12 Machine = 53 /* Motorola M68HC12. */
- EM_MMA Machine = 54 /* Fujitsu MMA. */
- EM_PCP Machine = 55 /* Siemens PCP. */
- EM_NCPU Machine = 56 /* Sony nCPU. */
- EM_NDR1 Machine = 57 /* Denso NDR1 microprocessor. */
- EM_STARCORE Machine = 58 /* Motorola Star*Core processor. */
- EM_ME16 Machine = 59 /* Toyota ME16 processor. */
- EM_ST100 Machine = 60 /* STMicroelectronics ST100 processor. */
- EM_TINYJ Machine = 61 /* Advanced Logic Corp. TinyJ processor. */
- EM_X86_64 Machine = 62 /* Advanced Micro Devices x86-64 */
+ EM_NONE Machine = 0 /* Unknown machine. */
+ EM_M32 Machine = 1 /* AT&T WE32100. */
+ EM_SPARC Machine = 2 /* Sun SPARC. */
+ EM_386 Machine = 3 /* Intel i386. */
+ EM_68K Machine = 4 /* Motorola 68000. */
+ EM_88K Machine = 5 /* Motorola 88000. */
+ EM_860 Machine = 7 /* Intel i860. */
+ EM_MIPS Machine = 8 /* MIPS R3000 Big-Endian only. */
+ EM_S370 Machine = 9 /* IBM System/370. */
+ EM_MIPS_RS3_LE Machine = 10 /* MIPS R3000 Little-Endian. */
+ EM_PARISC Machine = 15 /* HP PA-RISC. */
+ EM_VPP500 Machine = 17 /* Fujitsu VPP500. */
+ EM_SPARC32PLUS Machine = 18 /* SPARC v8plus. */
+ EM_960 Machine = 19 /* Intel 80960. */
+ EM_PPC Machine = 20 /* PowerPC 32-bit. */
+ EM_PPC64 Machine = 21 /* PowerPC 64-bit. */
+ EM_S390 Machine = 22 /* IBM System/390. */
+ EM_V800 Machine = 36 /* NEC V800. */
+ EM_FR20 Machine = 37 /* Fujitsu FR20. */
+ EM_RH32 Machine = 38 /* TRW RH-32. */
+ EM_RCE Machine = 39 /* Motorola RCE. */
+ EM_ARM Machine = 40 /* ARM. */
+ EM_SH Machine = 42 /* Hitachi SH. */
+ EM_SPARCV9 Machine = 43 /* SPARC v9 64-bit. */
+ EM_TRICORE Machine = 44 /* Siemens TriCore embedded processor. */
+ EM_ARC Machine = 45 /* Argonaut RISC Core. */
+ EM_H8_300 Machine = 46 /* Hitachi H8/300. */
+ EM_H8_300H Machine = 47 /* Hitachi H8/300H. */
+ EM_H8S Machine = 48 /* Hitachi H8S. */
+ EM_H8_500 Machine = 49 /* Hitachi H8/500. */
+ EM_IA_64 Machine = 50 /* Intel IA-64 Processor. */
+ EM_MIPS_X Machine = 51 /* Stanford MIPS-X. */
+ EM_COLDFIRE Machine = 52 /* Motorola ColdFire. */
+ EM_68HC12 Machine = 53 /* Motorola M68HC12. */
+ EM_MMA Machine = 54 /* Fujitsu MMA. */
+ EM_PCP Machine = 55 /* Siemens PCP. */
+ EM_NCPU Machine = 56 /* Sony nCPU. */
+ EM_NDR1 Machine = 57 /* Denso NDR1 microprocessor. */
+ EM_STARCORE Machine = 58 /* Motorola Star*Core processor. */
+ EM_ME16 Machine = 59 /* Toyota ME16 processor. */
+ EM_ST100 Machine = 60 /* STMicroelectronics ST100 processor. */
+ EM_TINYJ Machine = 61 /* Advanced Logic Corp. TinyJ processor. */
+ EM_X86_64 Machine = 62 /* Advanced Micro Devices x86-64 */
+ EM_AARCH64 Machine = 183 /* ARM 64-bit Architecture (AArch64) */
/* Non-standard or deprecated. */
EM_486 Machine = 6 /* Intel i486. */
@@ -774,6 +776,256 @@ var rx86_64Strings = []intName{
func (i R_X86_64) String() string { return stringName(uint32(i), rx86_64Strings, false) }
func (i R_X86_64) GoString() string { return stringName(uint32(i), rx86_64Strings, true) }
+// Relocation types for AArch64 (aka arm64)
+type R_AARCH64 int
+
+const (
+ R_AARCH64_NONE R_AARCH64 = 0
+ R_AARCH64_P32_ABS32 R_AARCH64 = 1
+ R_AARCH64_P32_ABS16 R_AARCH64 = 2
+ R_AARCH64_P32_PREL32 R_AARCH64 = 3
+ R_AARCH64_P32_PREL16 R_AARCH64 = 4
+ R_AARCH64_P32_MOVW_UABS_G0 R_AARCH64 = 5
+ R_AARCH64_P32_MOVW_UABS_G0_NC R_AARCH64 = 6
+ R_AARCH64_P32_MOVW_UABS_G1 R_AARCH64 = 7
+ R_AARCH64_P32_MOVW_SABS_G0 R_AARCH64 = 8
+ R_AARCH64_P32_LD_PREL_LO19 R_AARCH64 = 9
+ R_AARCH64_P32_ADR_PREL_LO21 R_AARCH64 = 10
+ R_AARCH64_P32_ADR_PREL_PG_HI21 R_AARCH64 = 11
+ R_AARCH64_P32_ADD_ABS_LO12_NC R_AARCH64 = 12
+ R_AARCH64_P32_LDST8_ABS_LO12_NC R_AARCH64 = 13
+ R_AARCH64_P32_LDST16_ABS_LO12_NC R_AARCH64 = 14
+ R_AARCH64_P32_LDST32_ABS_LO12_NC R_AARCH64 = 15
+ R_AARCH64_P32_LDST64_ABS_LO12_NC R_AARCH64 = 16
+ R_AARCH64_P32_LDST128_ABS_LO12_NC R_AARCH64 = 17
+ R_AARCH64_P32_TSTBR14 R_AARCH64 = 18
+ R_AARCH64_P32_CONDBR19 R_AARCH64 = 19
+ R_AARCH64_P32_JUMP26 R_AARCH64 = 20
+ R_AARCH64_P32_CALL26 R_AARCH64 = 21
+ R_AARCH64_P32_GOT_LD_PREL19 R_AARCH64 = 25
+ R_AARCH64_P32_ADR_GOT_PAGE R_AARCH64 = 26
+ R_AARCH64_P32_LD32_GOT_LO12_NC R_AARCH64 = 27
+ R_AARCH64_P32_TLSGD_ADR_PAGE21 R_AARCH64 = 81
+ R_AARCH64_P32_TLSGD_ADD_LO12_NC R_AARCH64 = 82
+ R_AARCH64_P32_TLSIE_ADR_GOTTPREL_PAGE21 R_AARCH64 = 103
+ R_AARCH64_P32_TLSIE_LD32_GOTTPREL_LO12_NC R_AARCH64 = 104
+ R_AARCH64_P32_TLSIE_LD_GOTTPREL_PREL19 R_AARCH64 = 105
+ R_AARCH64_P32_TLSLE_MOVW_TPREL_G1 R_AARCH64 = 106
+ R_AARCH64_P32_TLSLE_MOVW_TPREL_G0 R_AARCH64 = 107
+ R_AARCH64_P32_TLSLE_MOVW_TPREL_G0_NC R_AARCH64 = 108
+ R_AARCH64_P32_TLSLE_ADD_TPREL_HI12 R_AARCH64 = 109
+ R_AARCH64_P32_TLSLE_ADD_TPREL_LO12 R_AARCH64 = 110
+ R_AARCH64_P32_TLSLE_ADD_TPREL_LO12_NC R_AARCH64 = 111
+ R_AARCH64_P32_TLSDESC_LD_PREL19 R_AARCH64 = 122
+ R_AARCH64_P32_TLSDESC_ADR_PREL21 R_AARCH64 = 123
+ R_AARCH64_P32_TLSDESC_ADR_PAGE21 R_AARCH64 = 124
+ R_AARCH64_P32_TLSDESC_LD32_LO12_NC R_AARCH64 = 125
+ R_AARCH64_P32_TLSDESC_ADD_LO12_NC R_AARCH64 = 126
+ R_AARCH64_P32_TLSDESC_CALL R_AARCH64 = 127
+ R_AARCH64_P32_COPY R_AARCH64 = 180
+ R_AARCH64_P32_GLOB_DAT R_AARCH64 = 181
+ R_AARCH64_P32_JUMP_SLOT R_AARCH64 = 182
+ R_AARCH64_P32_RELATIVE R_AARCH64 = 183
+ R_AARCH64_P32_TLS_DTPMOD R_AARCH64 = 184
+ R_AARCH64_P32_TLS_DTPREL R_AARCH64 = 185
+ R_AARCH64_P32_TLS_TPREL R_AARCH64 = 186
+ R_AARCH64_P32_TLSDESC R_AARCH64 = 187
+ R_AARCH64_P32_IRELATIVE R_AARCH64 = 188
+ R_AARCH64_NULL R_AARCH64 = 256
+ R_AARCH64_ABS64 R_AARCH64 = 257
+ R_AARCH64_ABS32 R_AARCH64 = 258
+ R_AARCH64_ABS16 R_AARCH64 = 259
+ R_AARCH64_PREL64 R_AARCH64 = 260
+ R_AARCH64_PREL32 R_AARCH64 = 261
+ R_AARCH64_PREL16 R_AARCH64 = 262
+ R_AARCH64_MOVW_UABS_G0 R_AARCH64 = 263
+ R_AARCH64_MOVW_UABS_G0_NC R_AARCH64 = 264
+ R_AARCH64_MOVW_UABS_G1 R_AARCH64 = 265
+ R_AARCH64_MOVW_UABS_G1_NC R_AARCH64 = 266
+ R_AARCH64_MOVW_UABS_G2 R_AARCH64 = 267
+ R_AARCH64_MOVW_UABS_G2_NC R_AARCH64 = 268
+ R_AARCH64_MOVW_UABS_G3 R_AARCH64 = 269
+ R_AARCH64_MOVW_SABS_G0 R_AARCH64 = 270
+ R_AARCH64_MOVW_SABS_G1 R_AARCH64 = 271
+ R_AARCH64_MOVW_SABS_G2 R_AARCH64 = 272
+ R_AARCH64_LD_PREL_LO19 R_AARCH64 = 273
+ R_AARCH64_ADR_PREL_LO21 R_AARCH64 = 274
+ R_AARCH64_ADR_PREL_PG_HI21 R_AARCH64 = 275
+ R_AARCH64_ADR_PREL_PG_HI21_NC R_AARCH64 = 276
+ R_AARCH64_ADD_ABS_LO12_NC R_AARCH64 = 277
+ R_AARCH64_LDST8_ABS_LO12_NC R_AARCH64 = 278
+ R_AARCH64_TSTBR14 R_AARCH64 = 279
+ R_AARCH64_CONDBR19 R_AARCH64 = 280
+ R_AARCH64_JUMP26 R_AARCH64 = 282
+ R_AARCH64_CALL26 R_AARCH64 = 283
+ R_AARCH64_LDST16_ABS_LO12_NC R_AARCH64 = 284
+ R_AARCH64_LDST32_ABS_LO12_NC R_AARCH64 = 285
+ R_AARCH64_LDST64_ABS_LO12_NC R_AARCH64 = 286
+ R_AARCH64_LDST128_ABS_LO12_NC R_AARCH64 = 299
+ R_AARCH64_GOT_LD_PREL19 R_AARCH64 = 309
+ R_AARCH64_ADR_GOT_PAGE R_AARCH64 = 311
+ R_AARCH64_LD64_GOT_LO12_NC R_AARCH64 = 312
+ R_AARCH64_TLSGD_ADR_PAGE21 R_AARCH64 = 513
+ R_AARCH64_TLSGD_ADD_LO12_NC R_AARCH64 = 514
+ R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 R_AARCH64 = 539
+ R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC R_AARCH64 = 540
+ R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 R_AARCH64 = 541
+ R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC R_AARCH64 = 542
+ R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 R_AARCH64 = 543
+ R_AARCH64_TLSLE_MOVW_TPREL_G2 R_AARCH64 = 544
+ R_AARCH64_TLSLE_MOVW_TPREL_G1 R_AARCH64 = 545
+ R_AARCH64_TLSLE_MOVW_TPREL_G1_NC R_AARCH64 = 546
+ R_AARCH64_TLSLE_MOVW_TPREL_G0 R_AARCH64 = 547
+ R_AARCH64_TLSLE_MOVW_TPREL_G0_NC R_AARCH64 = 548
+ R_AARCH64_TLSLE_ADD_TPREL_HI12 R_AARCH64 = 549
+ R_AARCH64_TLSLE_ADD_TPREL_LO12 R_AARCH64 = 550
+ R_AARCH64_TLSLE_ADD_TPREL_LO12_NC R_AARCH64 = 551
+ R_AARCH64_TLSDESC_LD_PREL19 R_AARCH64 = 560
+ R_AARCH64_TLSDESC_ADR_PREL21 R_AARCH64 = 561
+ R_AARCH64_TLSDESC_ADR_PAGE21 R_AARCH64 = 562
+ R_AARCH64_TLSDESC_LD64_LO12_NC R_AARCH64 = 563
+ R_AARCH64_TLSDESC_ADD_LO12_NC R_AARCH64 = 564
+ R_AARCH64_TLSDESC_OFF_G1 R_AARCH64 = 565
+ R_AARCH64_TLSDESC_OFF_G0_NC R_AARCH64 = 566
+ R_AARCH64_TLSDESC_LDR R_AARCH64 = 567
+ R_AARCH64_TLSDESC_ADD R_AARCH64 = 568
+ R_AARCH64_TLSDESC_CALL R_AARCH64 = 569
+ R_AARCH64_COPY R_AARCH64 = 1024
+ R_AARCH64_GLOB_DAT R_AARCH64 = 1025
+ R_AARCH64_JUMP_SLOT R_AARCH64 = 1026
+ R_AARCH64_RELATIVE R_AARCH64 = 1027
+ R_AARCH64_TLS_DTPMOD64 R_AARCH64 = 1028
+ R_AARCH64_TLS_DTPREL64 R_AARCH64 = 1029
+ R_AARCH64_TLS_TPREL64 R_AARCH64 = 1030
+ R_AARCH64_TLSDESC R_AARCH64 = 1031
+ R_AARCH64_IRELATIVE R_AARCH64 = 1032
+)
+
+var raarch64Strings = []intName{
+ {0, "R_AARCH64_NONE"},
+ {1, "R_AARCH64_P32_ABS32"},
+ {2, "R_AARCH64_P32_ABS16"},
+ {3, "R_AARCH64_P32_PREL32"},
+ {4, "R_AARCH64_P32_PREL16"},
+ {5, "R_AARCH64_P32_MOVW_UABS_G0"},
+ {6, "R_AARCH64_P32_MOVW_UABS_G0_NC"},
+ {7, "R_AARCH64_P32_MOVW_UABS_G1"},
+ {8, "R_AARCH64_P32_MOVW_SABS_G0"},
+ {9, "R_AARCH64_P32_LD_PREL_LO19"},
+ {10, "R_AARCH64_P32_ADR_PREL_LO21"},
+ {11, "R_AARCH64_P32_ADR_PREL_PG_HI21"},
+ {12, "R_AARCH64_P32_ADD_ABS_LO12_NC"},
+ {13, "R_AARCH64_P32_LDST8_ABS_LO12_NC"},
+ {14, "R_AARCH64_P32_LDST16_ABS_LO12_NC"},
+ {15, "R_AARCH64_P32_LDST32_ABS_LO12_NC"},
+ {16, "R_AARCH64_P32_LDST64_ABS_LO12_NC"},
+ {17, "R_AARCH64_P32_LDST128_ABS_LO12_NC"},
+ {18, "R_AARCH64_P32_TSTBR14"},
+ {19, "R_AARCH64_P32_CONDBR19"},
+ {20, "R_AARCH64_P32_JUMP26"},
+ {21, "R_AARCH64_P32_CALL26"},
+ {25, "R_AARCH64_P32_GOT_LD_PREL19"},
+ {26, "R_AARCH64_P32_ADR_GOT_PAGE"},
+ {27, "R_AARCH64_P32_LD32_GOT_LO12_NC"},
+ {81, "R_AARCH64_P32_TLSGD_ADR_PAGE21"},
+ {82, "R_AARCH64_P32_TLSGD_ADD_LO12_NC"},
+ {103, "R_AARCH64_P32_TLSIE_ADR_GOTTPREL_PAGE21"},
+ {104, "R_AARCH64_P32_TLSIE_LD32_GOTTPREL_LO12_NC"},
+ {105, "R_AARCH64_P32_TLSIE_LD_GOTTPREL_PREL19"},
+ {106, "R_AARCH64_P32_TLSLE_MOVW_TPREL_G1"},
+ {107, "R_AARCH64_P32_TLSLE_MOVW_TPREL_G0"},
+ {108, "R_AARCH64_P32_TLSLE_MOVW_TPREL_G0_NC"},
+ {109, "R_AARCH64_P32_TLSLE_ADD_TPREL_HI12"},
+ {110, "R_AARCH64_P32_TLSLE_ADD_TPREL_LO12"},
+ {111, "R_AARCH64_P32_TLSLE_ADD_TPREL_LO12_NC"},
+ {122, "R_AARCH64_P32_TLSDESC_LD_PREL19"},
+ {123, "R_AARCH64_P32_TLSDESC_ADR_PREL21"},
+ {124, "R_AARCH64_P32_TLSDESC_ADR_PAGE21"},
+ {125, "R_AARCH64_P32_TLSDESC_LD32_LO12_NC"},
+ {126, "R_AARCH64_P32_TLSDESC_ADD_LO12_NC"},
+ {127, "R_AARCH64_P32_TLSDESC_CALL"},
+ {180, "R_AARCH64_P32_COPY"},
+ {181, "R_AARCH64_P32_GLOB_DAT"},
+ {182, "R_AARCH64_P32_JUMP_SLOT"},
+ {183, "R_AARCH64_P32_RELATIVE"},
+ {184, "R_AARCH64_P32_TLS_DTPMOD"},
+ {185, "R_AARCH64_P32_TLS_DTPREL"},
+ {186, "R_AARCH64_P32_TLS_TPREL"},
+ {187, "R_AARCH64_P32_TLSDESC"},
+ {188, "R_AARCH64_P32_IRELATIVE"},
+ {256, "R_AARCH64_NULL"},
+ {257, "R_AARCH64_ABS64"},
+ {258, "R_AARCH64_ABS32"},
+ {259, "R_AARCH64_ABS16"},
+ {260, "R_AARCH64_PREL64"},
+ {261, "R_AARCH64_PREL32"},
+ {262, "R_AARCH64_PREL16"},
+ {263, "R_AARCH64_MOVW_UABS_G0"},
+ {264, "R_AARCH64_MOVW_UABS_G0_NC"},
+ {265, "R_AARCH64_MOVW_UABS_G1"},
+ {266, "R_AARCH64_MOVW_UABS_G1_NC"},
+ {267, "R_AARCH64_MOVW_UABS_G2"},
+ {268, "R_AARCH64_MOVW_UABS_G2_NC"},
+ {269, "R_AARCH64_MOVW_UABS_G3"},
+ {270, "R_AARCH64_MOVW_SABS_G0"},
+ {271, "R_AARCH64_MOVW_SABS_G1"},
+ {272, "R_AARCH64_MOVW_SABS_G2"},
+ {273, "R_AARCH64_LD_PREL_LO19"},
+ {274, "R_AARCH64_ADR_PREL_LO21"},
+ {275, "R_AARCH64_ADR_PREL_PG_HI21"},
+ {276, "R_AARCH64_ADR_PREL_PG_HI21_NC"},
+ {277, "R_AARCH64_ADD_ABS_LO12_NC"},
+ {278, "R_AARCH64_LDST8_ABS_LO12_NC"},
+ {279, "R_AARCH64_TSTBR14"},
+ {280, "R_AARCH64_CONDBR19"},
+ {282, "R_AARCH64_JUMP26"},
+ {283, "R_AARCH64_CALL26"},
+ {284, "R_AARCH64_LDST16_ABS_LO12_NC"},
+ {285, "R_AARCH64_LDST32_ABS_LO12_NC"},
+ {286, "R_AARCH64_LDST64_ABS_LO12_NC"},
+ {299, "R_AARCH64_LDST128_ABS_LO12_NC"},
+ {309, "R_AARCH64_GOT_LD_PREL19"},
+ {311, "R_AARCH64_ADR_GOT_PAGE"},
+ {312, "R_AARCH64_LD64_GOT_LO12_NC"},
+ {513, "R_AARCH64_TLSGD_ADR_PAGE21"},
+ {514, "R_AARCH64_TLSGD_ADD_LO12_NC"},
+ {539, "R_AARCH64_TLSIE_MOVW_GOTTPREL_G1"},
+ {540, "R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC"},
+ {541, "R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21"},
+ {542, "R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC"},
+ {543, "R_AARCH64_TLSIE_LD_GOTTPREL_PREL19"},
+ {544, "R_AARCH64_TLSLE_MOVW_TPREL_G2"},
+ {545, "R_AARCH64_TLSLE_MOVW_TPREL_G1"},
+ {546, "R_AARCH64_TLSLE_MOVW_TPREL_G1_NC"},
+ {547, "R_AARCH64_TLSLE_MOVW_TPREL_G0"},
+ {548, "R_AARCH64_TLSLE_MOVW_TPREL_G0_NC"},
+ {549, "R_AARCH64_TLSLE_ADD_TPREL_HI12"},
+ {550, "R_AARCH64_TLSLE_ADD_TPREL_LO12"},
+ {551, "R_AARCH64_TLSLE_ADD_TPREL_LO12_NC"},
+ {560, "R_AARCH64_TLSDESC_LD_PREL19"},
+ {561, "R_AARCH64_TLSDESC_ADR_PREL21"},
+ {562, "R_AARCH64_TLSDESC_ADR_PAGE21"},
+ {563, "R_AARCH64_TLSDESC_LD64_LO12_NC"},
+ {564, "R_AARCH64_TLSDESC_ADD_LO12_NC"},
+ {565, "R_AARCH64_TLSDESC_OFF_G1"},
+ {566, "R_AARCH64_TLSDESC_OFF_G0_NC"},
+ {567, "R_AARCH64_TLSDESC_LDR"},
+ {568, "R_AARCH64_TLSDESC_ADD"},
+ {569, "R_AARCH64_TLSDESC_CALL"},
+ {1024, "R_AARCH64_COPY"},
+ {1025, "R_AARCH64_GLOB_DAT"},
+ {1026, "R_AARCH64_JUMP_SLOT"},
+ {1027, "R_AARCH64_RELATIVE"},
+ {1028, "R_AARCH64_TLS_DTPMOD64"},
+ {1029, "R_AARCH64_TLS_DTPREL64"},
+ {1030, "R_AARCH64_TLS_TPREL64"},
+ {1031, "R_AARCH64_TLSDESC"},
+ {1032, "R_AARCH64_IRELATIVE"},
+}
+
+func (i R_AARCH64) String() string { return stringName(uint32(i), raarch64Strings, false) }
+func (i R_AARCH64) GoString() string { return stringName(uint32(i), raarch64Strings, true) }
+
// Relocation types for Alpha.
type R_ALPHA int
diff --git a/src/pkg/debug/elf/file.go b/src/pkg/debug/elf/file.go
index 5e1c47a04c..49060b7356 100644
--- a/src/pkg/debug/elf/file.go
+++ b/src/pkg/debug/elf/file.go
@@ -529,6 +529,9 @@ func (f *File) applyRelocations(dst []byte, rels []byte) error {
if f.Class == ELFCLASS32 && f.Machine == EM_386 {
return f.applyRelocations386(dst, rels)
}
+ if f.Class == ELFCLASS64 && f.Machine == EM_AARCH64 {
+ return f.applyRelocationsARM64(dst, rels)
+ }
if f.Class == ELFCLASS64 && f.Machine == EM_PPC64 {
return f.applyRelocationsPPC64(dst, rels)
}
@@ -618,6 +621,51 @@ func (f *File) applyRelocations386(dst []byte, rels []byte) error {
return nil
}
+func (f *File) applyRelocationsARM64(dst []byte, rels []byte) error {
+ // 24 is the size of Rela64.
+ if len(rels)%24 != 0 {
+ return errors.New("length of relocation section is not a multiple of 24")
+ }
+
+ symbols, _, err := f.getSymbols(SHT_SYMTAB)
+ if err != nil {
+ return err
+ }
+
+ b := bytes.NewReader(rels)
+ var rela Rela64
+
+ for b.Len() > 0 {
+ binary.Read(b, f.ByteOrder, &rela)
+ symNo := rela.Info >> 32
+ t := R_AARCH64(rela.Info & 0xffff)
+
+ if symNo == 0 || symNo > uint64(len(symbols)) {
+ continue
+ }
+ sym := &symbols[symNo-1]
+ if SymType(sym.Info&0xf) != STT_SECTION {
+ // We don't handle non-section relocations for now.
+ continue
+ }
+
+ switch t {
+ case R_AARCH64_ABS64:
+ if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
+ continue
+ }
+ f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend))
+ case R_AARCH64_ABS32:
+ if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
+ continue
+ }
+ f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend))
+ }
+ }
+
+ return nil
+}
+
func (f *File) applyRelocationsPPC64(dst []byte, rels []byte) error {
// 24 is the size of Rela64.
if len(rels)%24 != 0 {
@@ -685,7 +733,7 @@ func (f *File) DWARF() (*dwarf.Data, error) {
// If there's a relocation table for .debug_info, we have to process it
// now otherwise the data in .debug_info is invalid for x86-64 objects.
rela := f.Section(".rela.debug_info")
- if rela != nil && rela.Type == SHT_RELA && (f.Machine == EM_X86_64 || f.Machine == EM_PPC64) {
+ if rela != nil && rela.Type == SHT_RELA && (f.Machine == EM_X86_64 || f.Machine == EM_AARCH64 || f.Machine == EM_PPC64) {
data, err := rela.Data()
if err != nil {
return nil, err
diff --git a/src/pkg/debug/elf/file_test.go b/src/pkg/debug/elf/file_test.go
index db9a7476c3..d57aaab0ec 100644
--- a/src/pkg/debug/elf/file_test.go
+++ b/src/pkg/debug/elf/file_test.go
@@ -261,6 +261,12 @@ var relocationTests = []relocationTest{
},
},
{
+ "testdata/go-relocation-test-gcc482-aarch64.obj",
+ []relocationTestEntry{
+ {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.8.2 -g -fstack-protector"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc482.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: int64(0x24)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}},
+ },
+ },
+ {
"testdata/go-relocation-test-gcc482-ppc64le.obj",
[]relocationTestEntry{
{0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{dwarf.Field{Attr: dwarf.AttrProducer, Val: "GNU C 4.8.2 -Asystem=linux -Asystem=unix -Asystem=posix -msecure-plt -mtune=power8 -mcpu=power7 -gdwarf-2 -fstack-protector"}, dwarf.Field{Attr: dwarf.AttrLanguage, Val: int64(1)}, dwarf.Field{Attr: dwarf.AttrName, Val: "go-relocation-test-gcc482-ppc64le.c"}, dwarf.Field{Attr: dwarf.AttrCompDir, Val: "/tmp"}, dwarf.Field{Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, dwarf.Field{Attr: dwarf.AttrHighpc, Val: uint64(0x24)}, dwarf.Field{Attr: dwarf.AttrStmtList, Val: int64(0)}}}},
diff --git a/src/pkg/debug/elf/testdata/go-relocation-test-gcc482-aarch64.obj b/src/pkg/debug/elf/testdata/go-relocation-test-gcc482-aarch64.obj
new file mode 100644
index 0000000000..849e2644ec
--- /dev/null
+++ b/src/pkg/debug/elf/testdata/go-relocation-test-gcc482-aarch64.obj
Binary files differ
diff --git a/src/pkg/debug/goobj/read.go b/src/pkg/debug/goobj/read.go
index c95fe1e47f..79a83e59a6 100644
--- a/src/pkg/debug/goobj/read.go
+++ b/src/pkg/debug/goobj/read.go
@@ -602,7 +602,8 @@ func (r *objReader) parseObject(prefix []byte) error {
s := &Sym{SymID: r.readSymID()}
r.p.Syms = append(r.p.Syms, s)
s.Kind = SymKind(typ)
- s.DupOK = r.readInt() != 0
+ flags := r.readInt()
+ s.DupOK = flags&1 != 0
s.Size = r.readInt()
s.Type = r.readSymID()
s.Data = r.readData()
@@ -623,7 +624,8 @@ func (r *objReader) parseObject(prefix []byte) error {
s.Func = f
f.Args = r.readInt()
f.Frame = r.readInt()
- f.Leaf = r.readInt() != 0
+ flags := r.readInt()
+ f.Leaf = flags&1 != 0
f.NoSplit = r.readInt() != 0
f.Var = make([]Var, r.readInt())
for i := range f.Var {
diff --git a/src/pkg/debug/gosym/symtab.go b/src/pkg/debug/gosym/symtab.go
index 3864e3cb4f..ee18499d11 100644
--- a/src/pkg/debug/gosym/symtab.go
+++ b/src/pkg/debug/gosym/symtab.go
@@ -402,7 +402,7 @@ func NewTable(symtab []byte, pcln *LineTable) (*Table, error) {
if n := len(t.Funcs); n > 0 {
t.Funcs[n-1].End = sym.Value
}
- if sym.Name == "etext" {
+ if sym.Name == "runtime.etext" || sym.Name == "etext" {
continue
}
diff --git a/src/pkg/debug/pe/file_test.go b/src/pkg/debug/pe/file_test.go
index ddbb271744..0d73969bca 100644
--- a/src/pkg/debug/pe/file_test.go
+++ b/src/pkg/debug/pe/file_test.go
@@ -125,9 +125,9 @@ var fileTests = []fileTest{
},
{
"testdata/gcc-amd64-mingw-exec",
- FileHeader{0x8664, 0x9, 0x53472993, 0x0, 0x0, 0xf0, 0x22f},
+ FileHeader{0x8664, 0x11, 0x53e4364f, 0x39600, 0x6fc, 0xf0, 0x27},
&OptionalHeader64{
- 0x20b, 0x2, 0x16, 0x6a00, 0x2400, 0x1600, 0x14e0, 0x1000, 0x400000, 0x1000, 0x200, 0x4, 0x0, 0x0, 0x0, 0x5, 0x2, 0x0, 0x11000, 0x400, 0x1841e, 0x3, 0x0, 0x200000, 0x1000, 0x100000, 0x1000, 0x0, 0x10,
+ 0x20b, 0x2, 0x16, 0x6a00, 0x2400, 0x1600, 0x14e0, 0x1000, 0x400000, 0x1000, 0x200, 0x4, 0x0, 0x0, 0x0, 0x5, 0x2, 0x0, 0x45000, 0x600, 0x46f19, 0x3, 0x0, 0x200000, 0x1000, 0x100000, 0x1000, 0x0, 0x10,
[16]DataDirectory{
{0x0, 0x0},
{0xe000, 0x990},
@@ -145,18 +145,25 @@ var fileTests = []fileTest{
{0x0, 0x0},
{0x0, 0x0},
{0x0, 0x0},
- },
- },
+ }},
[]*SectionHeader{
- {".text", 0x6860, 0x1000, 0x6a00, 0x400, 0x0, 0x0, 0x0, 0x0, 0x60500020},
- {".data", 0xe0, 0x8000, 0x200, 0x6e00, 0x0, 0x0, 0x0, 0x0, 0xc0500040},
- {".rdata", 0x6b0, 0x9000, 0x800, 0x7000, 0x0, 0x0, 0x0, 0x0, 0x40600040},
- {".pdata", 0x498, 0xa000, 0x600, 0x7800, 0x0, 0x0, 0x0, 0x0, 0x40300040},
- {".xdata", 0x488, 0xb000, 0x600, 0x7e00, 0x0, 0x0, 0x0, 0x0, 0x40300040},
+ {".text", 0x6860, 0x1000, 0x6a00, 0x600, 0x0, 0x0, 0x0, 0x0, 0x60500020},
+ {".data", 0xe0, 0x8000, 0x200, 0x7000, 0x0, 0x0, 0x0, 0x0, 0xc0500040},
+ {".rdata", 0x6b0, 0x9000, 0x800, 0x7200, 0x0, 0x0, 0x0, 0x0, 0x40600040},
+ {".pdata", 0x498, 0xa000, 0x600, 0x7a00, 0x0, 0x0, 0x0, 0x0, 0x40300040},
+ {".xdata", 0x488, 0xb000, 0x600, 0x8000, 0x0, 0x0, 0x0, 0x0, 0x40300040},
{".bss", 0x1410, 0xc000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0600080},
- {".idata", 0x990, 0xe000, 0xa00, 0x8400, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
- {".CRT", 0x68, 0xf000, 0x200, 0x8e00, 0x0, 0x0, 0x0, 0x0, 0xc0400040},
- {".tls", 0x48, 0x10000, 0x200, 0x9000, 0x0, 0x0, 0x0, 0x0, 0xc0600040},
+ {".idata", 0x990, 0xe000, 0xa00, 0x8600, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
+ {".CRT", 0x68, 0xf000, 0x200, 0x9000, 0x0, 0x0, 0x0, 0x0, 0xc0400040},
+ {".tls", 0x48, 0x10000, 0x200, 0x9200, 0x0, 0x0, 0x0, 0x0, 0xc0600040},
+ {".debug_aranges", 0x600, 0x11000, 0x600, 0x9400, 0x0, 0x0, 0x0, 0x0, 0x42500040},
+ {".debug_info", 0x1316e, 0x12000, 0x13200, 0x9a00, 0x0, 0x0, 0x0, 0x0, 0x42100040},
+ {".debug_abbrev", 0x2ccb, 0x26000, 0x2e00, 0x1cc00, 0x0, 0x0, 0x0, 0x0, 0x42100040},
+ {".debug_line", 0x3c4d, 0x29000, 0x3e00, 0x1fa00, 0x0, 0x0, 0x0, 0x0, 0x42100040},
+ {".debug_frame", 0x18b8, 0x2d000, 0x1a00, 0x23800, 0x0, 0x0, 0x0, 0x0, 0x42400040},
+ {".debug_str", 0x396, 0x2f000, 0x400, 0x25200, 0x0, 0x0, 0x0, 0x0, 0x42100040},
+ {".debug_loc", 0x13240, 0x30000, 0x13400, 0x25600, 0x0, 0x0, 0x0, 0x0, 0x42100040},
+ {".debug_ranges", 0xa70, 0x44000, 0xc00, 0x38a00, 0x0, 0x0, 0x0, 0x0, 0x42100040},
},
[]*Symbol{},
},
diff --git a/src/pkg/debug/pe/testdata/gcc-amd64-mingw-exec b/src/pkg/debug/pe/testdata/gcc-amd64-mingw-exec
index 78d4e5fed9..ce6feb6b7b 100644
--- a/src/pkg/debug/pe/testdata/gcc-amd64-mingw-exec
+++ b/src/pkg/debug/pe/testdata/gcc-amd64-mingw-exec
Binary files differ
diff --git a/src/pkg/encoding/gob/timing_test.go b/src/pkg/encoding/gob/timing_test.go
index acfb065b12..ec55c4d63d 100644
--- a/src/pkg/encoding/gob/timing_test.go
+++ b/src/pkg/encoding/gob/timing_test.go
@@ -19,33 +19,57 @@ type Bench struct {
D []byte
}
-func benchmarkEndToEnd(r io.Reader, w io.Writer, b *testing.B) {
- b.StopTimer()
- enc := NewEncoder(w)
- dec := NewDecoder(r)
- bench := &Bench{7, 3.2, "now is the time", bytes.Repeat([]byte("for all good men"), 100)}
- b.StartTimer()
- for i := 0; i < b.N; i++ {
- if enc.Encode(bench) != nil {
- panic("encode error")
+func benchmarkEndToEnd(b *testing.B, ctor func() interface{}, pipe func() (r io.Reader, w io.Writer, err error)) {
+ b.RunParallel(func(pb *testing.PB) {
+ r, w, err := pipe()
+ if err != nil {
+ b.Fatal("can't get pipe:", err)
}
- if dec.Decode(bench) != nil {
- panic("decode error")
+ v := ctor()
+ enc := NewEncoder(w)
+ dec := NewDecoder(r)
+ for pb.Next() {
+ if err := enc.Encode(v); err != nil {
+ b.Fatal("encode error:", err)
+ }
+ if err := dec.Decode(v); err != nil {
+ b.Fatal("decode error:", err)
+ }
}
- }
+ })
}
func BenchmarkEndToEndPipe(b *testing.B) {
- r, w, err := os.Pipe()
- if err != nil {
- b.Fatal("can't get pipe:", err)
- }
- benchmarkEndToEnd(r, w, b)
+ benchmarkEndToEnd(b, func() interface{} {
+ return &Bench{7, 3.2, "now is the time", bytes.Repeat([]byte("for all good men"), 100)}
+ }, func() (r io.Reader, w io.Writer, err error) {
+ r, w, err = os.Pipe()
+ return
+ })
}
func BenchmarkEndToEndByteBuffer(b *testing.B) {
- var buf bytes.Buffer
- benchmarkEndToEnd(&buf, &buf, b)
+ benchmarkEndToEnd(b, func() interface{} {
+ return &Bench{7, 3.2, "now is the time", bytes.Repeat([]byte("for all good men"), 100)}
+ }, func() (r io.Reader, w io.Writer, err error) {
+ var buf bytes.Buffer
+ return &buf, &buf, nil
+ })
+}
+
+func BenchmarkEndToEndSliceByteBuffer(b *testing.B) {
+ benchmarkEndToEnd(b, func() interface{} {
+ v := &Bench{7, 3.2, "now is the time", nil}
+ Register(v)
+ arr := make([]interface{}, 100)
+ for i := range arr {
+ arr[i] = v
+ }
+ return &arr
+ }, func() (r io.Reader, w io.Writer, err error) {
+ var buf bytes.Buffer
+ return &buf, &buf, nil
+ })
}
func TestCountEncodeMallocs(t *testing.T) {
diff --git a/src/pkg/encoding/json/encode.go b/src/pkg/encoding/json/encode.go
index 741ddd89cb..b63538c922 100644
--- a/src/pkg/encoding/json/encode.go
+++ b/src/pkg/encoding/json/encode.go
@@ -40,8 +40,8 @@ import (
//
// Floating point, integer, and Number values encode as JSON numbers.
//
-// String values encode as JSON strings. InvalidUTF8Error will be returned
-// if an invalid UTF-8 sequence is encountered.
+// String values encode as JSON strings coerced to valid UTF-8,
+// replacing invalid bytes with the Unicode replacement rune.
// 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.
@@ -696,12 +696,12 @@ type ptrEncoder struct {
elemEnc encoderFunc
}
-func (pe *ptrEncoder) encode(e *encodeState, v reflect.Value, _ bool) {
+func (pe *ptrEncoder) encode(e *encodeState, v reflect.Value, quoted bool) {
if v.IsNil() {
e.WriteString("null")
return
}
- pe.elemEnc(e, v.Elem(), false)
+ pe.elemEnc(e, v.Elem(), quoted)
}
func newPtrEncoder(t reflect.Type) encoderFunc {
diff --git a/src/pkg/encoding/json/encode_test.go b/src/pkg/encoding/json/encode_test.go
index 2e89a78eb9..eb84cbae14 100644
--- a/src/pkg/encoding/json/encode_test.go
+++ b/src/pkg/encoding/json/encode_test.go
@@ -452,3 +452,29 @@ func TestHTMLEscape(t *testing.T) {
t.Errorf("HTMLEscape(&b, []byte(m)) = %s; want %s", b.Bytes(), want.Bytes())
}
}
+
+// golang.org/issue/8582
+func TestEncodePointerString(t *testing.T) {
+ type stringPointer struct {
+ N *int64 `json:"n,string"`
+ }
+ var n int64 = 42
+ b, err := Marshal(stringPointer{N: &n})
+ if err != nil {
+ t.Fatalf("Marshal: %v", err)
+ }
+ if got, want := string(b), `{"n":"42"}`; got != want {
+ t.Errorf("Marshal = %s, want %s", got, want)
+ }
+ var back stringPointer
+ err = Unmarshal(b, &back)
+ if err != nil {
+ t.Fatalf("Unmarshal: %v", err)
+ }
+ if back.N == nil {
+ t.Fatalf("Unmarshalled nil N field")
+ }
+ if *back.N != 42 {
+ t.Fatalf("*N = %d; want 42", *back.N)
+ }
+}
diff --git a/src/pkg/encoding/xml/xml.go b/src/pkg/encoding/xml/xml.go
index b473cb8458..a4cd4e29e0 100644
--- a/src/pkg/encoding/xml/xml.go
+++ b/src/pkg/encoding/xml/xml.go
@@ -29,6 +29,7 @@ import (
type SyntaxError struct {
Msg string
Line int
+ Byte int64 // byte offset from start of stream
}
func (e *SyntaxError) Error() string {
@@ -196,6 +197,7 @@ type Decoder struct {
ns map[string]string
err error
line int
+ offset int64
unmarshalDepth int
}
@@ -859,9 +861,17 @@ func (d *Decoder) getc() (b byte, ok bool) {
if b == '\n' {
d.line++
}
+ d.offset++
return b, true
}
+// InputOffset returns the input stream byte offset of the current decoder position.
+// The offset gives the location of the end of the most recently returned token
+// and the beginning of the next token.
+func (d *Decoder) InputOffset() int64 {
+ return d.offset
+}
+
// Return saved offset.
// If we did ungetc (nextByte >= 0), have to back up one.
func (d *Decoder) savedOffset() int {
@@ -891,6 +901,7 @@ func (d *Decoder) ungetc(b byte) {
d.line--
}
d.nextByte = int(b)
+ d.offset--
}
var entity = map[string]int{
diff --git a/src/pkg/encoding/xml/xml_test.go b/src/pkg/encoding/xml/xml_test.go
index 7723ab1c9f..be995c0d52 100644
--- a/src/pkg/encoding/xml/xml_test.go
+++ b/src/pkg/encoding/xml/xml_test.go
@@ -170,7 +170,7 @@ var xmlInput = []string{
func TestRawToken(t *testing.T) {
d := NewDecoder(strings.NewReader(testInput))
d.Entity = testEntity
- testRawToken(t, d, rawTokens)
+ testRawToken(t, d, testInput, rawTokens)
}
const nonStrictInput = `
@@ -225,7 +225,7 @@ var nonStrictTokens = []Token{
func TestNonStrictRawToken(t *testing.T) {
d := NewDecoder(strings.NewReader(nonStrictInput))
d.Strict = false
- testRawToken(t, d, nonStrictTokens)
+ testRawToken(t, d, nonStrictInput, nonStrictTokens)
}
type downCaser struct {
@@ -254,7 +254,7 @@ func TestRawTokenAltEncoding(t *testing.T) {
}
return &downCaser{t, input.(io.ByteReader)}, nil
}
- testRawToken(t, d, rawTokensAltEncoding)
+ testRawToken(t, d, testInputAltEncoding, rawTokensAltEncoding)
}
func TestRawTokenAltEncodingNoConverter(t *testing.T) {
@@ -280,9 +280,12 @@ func TestRawTokenAltEncodingNoConverter(t *testing.T) {
}
}
-func testRawToken(t *testing.T, d *Decoder, rawTokens []Token) {
+func testRawToken(t *testing.T, d *Decoder, raw string, rawTokens []Token) {
+ lastEnd := int64(0)
for i, want := range rawTokens {
+ start := d.InputOffset()
have, err := d.RawToken()
+ end := d.InputOffset()
if err != nil {
t.Fatalf("token %d: unexpected error: %s", i, err)
}
@@ -300,6 +303,26 @@ func testRawToken(t *testing.T, d *Decoder, rawTokens []Token) {
}
t.Errorf("token %d = %s, want %s", i, shave, swant)
}
+
+ // Check that InputOffset returned actual token.
+ switch {
+ case start < lastEnd:
+ t.Errorf("token %d: position [%d,%d) for %T is before previous token", i, start, end, have)
+ case start >= end:
+ // Special case: EndElement can be synthesized.
+ if start == end && end == lastEnd {
+ break
+ }
+ t.Errorf("token %d: position [%d,%d) for %T is empty", i, start, end, have)
+ case end > int64(len(raw)):
+ t.Errorf("token %d: position [%d,%d) for %T extends beyond input", i, start, end, have)
+ default:
+ text := raw[start:end]
+ if strings.ContainsAny(text, "<>") && (!strings.HasPrefix(text, "<") || !strings.HasSuffix(text, ">")) {
+ t.Errorf("token %d: misaligned raw token %#q for %T", i, text, have)
+ }
+ }
+ lastEnd = end
}
}
diff --git a/src/pkg/flag/flag.go b/src/pkg/flag/flag.go
index fa7760550c..de2d91f8b1 100644
--- a/src/pkg/flag/flag.go
+++ b/src/pkg/flag/flag.go
@@ -73,7 +73,8 @@ import (
"time"
)
-// ErrHelp is the error returned if the flag -help is invoked but no such flag is defined.
+// ErrHelp is the error returned if the -help or -h flag is invoked
+// but no such flag is defined.
var ErrHelp = errors.New("flag: help requested")
// -- bool Value
@@ -788,7 +789,7 @@ func (f *FlagSet) parseOne() (bool, error) {
// Parse parses flag definitions from the argument list, which should not
// include the command name. Must be called after all flags in the FlagSet
// are defined and before flags are accessed by the program.
-// The return value will be ErrHelp if -help was set but not defined.
+// The return value will be ErrHelp if -help or -h were set but not defined.
func (f *FlagSet) Parse(arguments []string) error {
f.parsed = true
f.args = arguments
diff --git a/src/pkg/fmt/fmt_test.go b/src/pkg/fmt/fmt_test.go
index 89227cce80..8c577949a1 100644
--- a/src/pkg/fmt/fmt_test.go
+++ b/src/pkg/fmt/fmt_test.go
@@ -108,6 +108,20 @@ func (p *P) String() string {
var barray = [5]renamedUint8{1, 2, 3, 4, 5}
var bslice = barray[:]
+type byteStringer byte
+
+func (byteStringer) String() string { return "X" }
+
+var byteStringerSlice = []byteStringer{97, 98, 99, 100}
+
+type byteFormatter byte
+
+func (byteFormatter) Format(f State, _ rune) {
+ Fprint(f, "X")
+}
+
+var byteFormatterSlice = []byteFormatter{97, 98, 99, 100}
+
var b byte
var fmtTests = []struct {
@@ -629,6 +643,21 @@ var fmtTests = []struct {
{"%+010.2f", -104.66 + 440.51i, "(-000104.66+000440.51i)"},
{"%+010.2f", +104.66 - 440.51i, "(+000104.66-000440.51i)"},
{"%+010.2f", -104.66 - 440.51i, "(-000104.66-000440.51i)"},
+
+ // []T where type T is a byte with a Stringer method.
+ {"%v", byteStringerSlice, "[X X X X]"},
+ {"%s", byteStringerSlice, "abcd"},
+ {"%q", byteStringerSlice, "\"abcd\""},
+ {"%x", byteStringerSlice, "61626364"},
+ {"%#v", byteStringerSlice, "[]fmt_test.byteStringer{0x61, 0x62, 0x63, 0x64}"},
+
+ // And the same for Formatter.
+ {"%v", byteFormatterSlice, "[X X X X]"},
+ {"%s", byteFormatterSlice, "abcd"},
+ {"%q", byteFormatterSlice, "\"abcd\""},
+ {"%x", byteFormatterSlice, "61626364"},
+ // This next case seems wrong, but the docs say the Formatter wins here.
+ {"%#v", byteFormatterSlice, "[]fmt_test.byteFormatter{X, X, X, X}"},
}
// zeroFill generates zero-filled strings of the specified width. The length
diff --git a/src/pkg/fmt/print.go b/src/pkg/fmt/print.go
index 302661f4c8..679c577dbd 100644
--- a/src/pkg/fmt/print.go
+++ b/src/pkg/fmt/print.go
@@ -832,6 +832,8 @@ func (p *pp) printValue(value reflect.Value, verb rune, plus, goSyntax bool, dep
return p.printReflectValue(value, verb, plus, goSyntax, depth)
}
+var byteType = reflect.TypeOf(byte(0))
+
// printReflectValue is the fallback for both printArg and printValue.
// It uses reflect to print the value.
func (p *pp) printReflectValue(value reflect.Value, verb rune, plus, goSyntax bool, depth int) (wasString bool) {
@@ -925,8 +927,12 @@ BigSwitch:
wasString = p.printValue(value, verb, plus, goSyntax, depth+1)
}
case reflect.Array, reflect.Slice:
- // Byte slices are special.
- if typ := f.Type(); typ.Elem().Kind() == reflect.Uint8 {
+ // Byte slices are special:
+ // - Handle []byte (== []uint8) with fmtBytes.
+ // - Handle []T, where T is a named byte type, with fmtBytes only
+ // for the s, q, an x verbs. For other verbs, T might be a
+ // Stringer, so we use printValue to print each element.
+ if typ := f.Type(); typ.Elem().Kind() == reflect.Uint8 && (typ.Elem() == byteType || verb == 's' || verb == 'q' || verb == 'x') {
var bytes []byte
if f.Kind() == reflect.Slice {
bytes = f.Bytes()
diff --git a/src/pkg/fmt/scan.go b/src/pkg/fmt/scan.go
index 8a337e479d..d7befeae43 100644
--- a/src/pkg/fmt/scan.go
+++ b/src/pkg/fmt/scan.go
@@ -360,6 +360,7 @@ func (r *readRune) ReadRune() (rr rune, size int, err error) {
}
if r.buf[0] < utf8.RuneSelf { // fast check for common ASCII case
rr = rune(r.buf[0])
+ size = 1 // Known to be 1.
return
}
var n int
diff --git a/src/pkg/fmt/scan_test.go b/src/pkg/fmt/scan_test.go
index d903f0c3ff..541e12df21 100644
--- a/src/pkg/fmt/scan_test.go
+++ b/src/pkg/fmt/scan_test.go
@@ -842,6 +842,38 @@ func TestLineByLineFscanf(t *testing.T) {
}
}
+// TestScanStateCount verifies the correct byte count is returned. Issue 8512.
+
+// runeScanner implements the Scanner interface for TestScanStateCount.
+type runeScanner struct {
+ rune rune
+ size int
+}
+
+func (rs *runeScanner) Scan(state ScanState, verb rune) error {
+ r, size, err := state.ReadRune()
+ rs.rune = r
+ rs.size = size
+ return err
+}
+
+func TestScanStateCount(t *testing.T) {
+ var a, b, c runeScanner
+ n, err := Sscanf("12➂", "%c%c%c", &a, &b, &c)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if n != 3 {
+ t.Fatalf("expected 3 items consumed, got %d")
+ }
+ if a.rune != '1' || b.rune != '2' || c.rune != '➂' {
+ t.Errorf("bad scan rune: %q %q %q should be '1' '2' '➂'", a.rune, b.rune, c.rune)
+ }
+ if a.size != 1 || b.size != 1 || c.size != 3 {
+ t.Errorf("bad scan size: %q %q %q should be 1 1 3", a.size, b.size, c.size)
+ }
+}
+
// RecursiveInt accepts a string matching %d.%d.%d....
// and parses it into a linked list.
// It allows us to benchmark recursive descent style scanners.
diff --git a/src/pkg/go/build/build.go b/src/pkg/go/build/build.go
index e8bfc4a61e..1002851e84 100644
--- a/src/pkg/go/build/build.go
+++ b/src/pkg/go/build/build.go
@@ -23,6 +23,7 @@ import (
"strconv"
"strings"
"unicode"
+ "unicode/utf8"
)
// A Context specifies the supporting context for a build.
@@ -337,22 +338,29 @@ const (
// If AllowBinary is set, Import can be satisfied by a compiled
// package object without corresponding sources.
AllowBinary
+
+ // If ImportComment is set, parse import comments on package statements.
+ // Import returns an error if it finds a comment it cannot understand
+ // or finds conflicting comments in multiple source files.
+ // See golang.org/s/go14customimport for more information.
+ ImportComment
)
// A Package describes the Go package found in a directory.
type Package struct {
- Dir string // directory containing package sources
- Name string // package name
- Doc string // documentation synopsis
- ImportPath string // import path of package ("" if unknown)
- Root string // root of Go tree where this package lives
- SrcRoot string // package source root directory ("" if unknown)
- PkgRoot string // package install root directory ("" if unknown)
- BinDir string // command install directory ("" if unknown)
- Goroot bool // package found in Go root
- PkgObj string // installed .a file
- AllTags []string // tags that can influence file selection in this directory
- ConflictDir string // this directory shadows Dir in $GOPATH
+ Dir string // directory containing package sources
+ Name string // package name
+ ImportComment string // path in import comment on package statement
+ Doc string // documentation synopsis
+ ImportPath string // import path of package ("" if unknown)
+ Root string // root of Go tree where this package lives
+ SrcRoot string // package source root directory ("" if unknown)
+ PkgRoot string // package install root directory ("" if unknown)
+ BinDir string // command install directory ("" if unknown)
+ Goroot bool // package found in Go root
+ PkgObj string // installed .a file
+ AllTags []string // tags that can influence file selection in this directory
+ ConflictDir string // this directory shadows Dir in $GOPATH
// Source files
GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
@@ -521,7 +529,12 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa
// Determine directory from import path.
if ctxt.GOROOT != "" {
- dir := ctxt.joinPath(ctxt.GOROOT, "src", "pkg", path)
+ var dir string
+ if strings.HasPrefix(path, "cmd/") {
+ dir = ctxt.joinPath(ctxt.GOROOT, "src", path)
+ } else {
+ dir = ctxt.joinPath(ctxt.GOROOT, "src", "pkg", path)
+ }
isDir := ctxt.isDir(dir)
binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(ctxt.GOROOT, pkga))
if isDir || binaryOnly {
@@ -592,7 +605,7 @@ Found:
}
var Sfiles []string // files with ".S" (capital S)
- var firstFile string
+ var firstFile, firstCommentFile string
imported := make(map[string][]token.Position)
testImported := make(map[string][]token.Position)
xTestImported := make(map[string][]token.Position)
@@ -679,6 +692,22 @@ Found:
p.Doc = doc.Synopsis(pf.Doc.Text())
}
+ if mode&ImportComment != 0 {
+ qcom, line := findImportComment(data)
+ if line != 0 {
+ com, err := strconv.Unquote(qcom)
+ if err != nil {
+ return p, fmt.Errorf("%s:%d: cannot parse import comment", filename, line)
+ }
+ if p.ImportComment == "" {
+ p.ImportComment = com
+ firstCommentFile = name
+ } else if p.ImportComment != com {
+ return p, fmt.Errorf("found import comments %q (%s) and %q (%s) in %s", p.ImportComment, firstCommentFile, com, name, p.Dir)
+ }
+ }
+ }
+
// Record imports and information about cgo.
isCgo := false
for _, decl := range pf.Decls {
@@ -759,6 +788,117 @@ Found:
return p, pkgerr
}
+func findImportComment(data []byte) (s string, line int) {
+ // expect keyword package
+ word, data := parseWord(data)
+ if string(word) != "package" {
+ return "", 0
+ }
+
+ // expect package name
+ _, data = parseWord(data)
+
+ // now ready for import comment, a // or /* */ comment
+ // beginning and ending on the current line.
+ for len(data) > 0 && (data[0] == ' ' || data[0] == '\t' || data[0] == '\r') {
+ data = data[1:]
+ }
+
+ var comment []byte
+ switch {
+ case bytes.HasPrefix(data, slashSlash):
+ i := bytes.Index(data, newline)
+ if i < 0 {
+ i = len(data)
+ }
+ comment = data[2:i]
+ case bytes.HasPrefix(data, slashStar):
+ data = data[2:]
+ i := bytes.Index(data, starSlash)
+ if i < 0 {
+ // malformed comment
+ return "", 0
+ }
+ comment = data[:i]
+ if bytes.Contains(comment, newline) {
+ return "", 0
+ }
+ }
+ comment = bytes.TrimSpace(comment)
+
+ // split comment into `import`, `"pkg"`
+ word, arg := parseWord(comment)
+ if string(word) != "import" {
+ return "", 0
+ }
+
+ line = 1 + bytes.Count(data[:cap(data)-cap(arg)], newline)
+ return strings.TrimSpace(string(arg)), line
+}
+
+var (
+ slashSlash = []byte("//")
+ slashStar = []byte("/*")
+ starSlash = []byte("*/")
+ newline = []byte("\n")
+)
+
+// skipSpaceOrComment returns data with any leading spaces or comments removed.
+func skipSpaceOrComment(data []byte) []byte {
+ for len(data) > 0 {
+ switch data[0] {
+ case ' ', '\t', '\r', '\n':
+ data = data[1:]
+ continue
+ case '/':
+ if bytes.HasPrefix(data, slashSlash) {
+ i := bytes.Index(data, newline)
+ if i < 0 {
+ return nil
+ }
+ data = data[i+1:]
+ continue
+ }
+ if bytes.HasPrefix(data, slashStar) {
+ data = data[2:]
+ i := bytes.Index(data, starSlash)
+ if i < 0 {
+ return nil
+ }
+ data = data[i+2:]
+ continue
+ }
+ }
+ break
+ }
+ return data
+}
+
+// parseWord skips any leading spaces or comments in data
+// and then parses the beginning of data as an identifier or keyword,
+// returning that word and what remains after the word.
+func parseWord(data []byte) (word, rest []byte) {
+ data = skipSpaceOrComment(data)
+
+ // Parse past leading word characters.
+ rest = data
+ for {
+ r, size := utf8.DecodeRune(rest)
+ if unicode.IsLetter(r) || '0' <= r && r <= '9' || r == '_' {
+ rest = rest[size:]
+ continue
+ }
+ break
+ }
+
+ word = data[:len(data)-len(rest)]
+ if len(word) == 0 {
+ return nil, nil
+ }
+
+ return word, rest
+}
+
// MatchFile reports whether the file with the given name in the given directory
// matches the context and would be included in a Package created by ImportDir
// of that directory.
diff --git a/src/pkg/go/build/build_test.go b/src/pkg/go/build/build_test.go
index f0d243cd53..0040101134 100644
--- a/src/pkg/go/build/build_test.go
+++ b/src/pkg/go/build/build_test.go
@@ -193,3 +193,13 @@ func TestMatchFile(t *testing.T) {
}
}
}
+
+func TestImportCmd(t *testing.T) {
+ p, err := Import("cmd/internal/objfile", "", 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !strings.HasSuffix(filepath.ToSlash(p.Dir), "src/cmd/internal/objfile") {
+ t.Fatalf("Import cmd/internal/objfile returned Dir=%q, want %q", filepath.ToSlash(p.Dir), ".../src/cmd/internal/objfile")
+ }
+}
diff --git a/src/pkg/go/build/deps_test.go b/src/pkg/go/build/deps_test.go
index 99b985b51d..b74595ea83 100644
--- a/src/pkg/go/build/deps_test.go
+++ b/src/pkg/go/build/deps_test.go
@@ -279,12 +279,12 @@ var pkgDeps = map[string][]string{
// Random byte, number generation.
// This would be part of core crypto except that it imports
// math/big, which imports fmt.
- "crypto/rand": {"L4", "CRYPTO", "OS", "math/big", "syscall"},
+ "crypto/rand": {"L4", "CRYPTO", "OS", "math/big", "syscall", "internal/syscall"},
// Mathematical crypto: dependencies on fmt (L4) and math/big.
// We could avoid some of the fmt, but math/big imports fmt anyway.
"crypto/dsa": {"L4", "CRYPTO", "math/big"},
- "crypto/ecdsa": {"L4", "CRYPTO", "crypto/elliptic", "math/big"},
+ "crypto/ecdsa": {"L4", "CRYPTO", "crypto/elliptic", "math/big", "encoding/asn1"},
"crypto/elliptic": {"L4", "CRYPTO", "math/big"},
"crypto/rsa": {"L4", "CRYPTO", "crypto/rand", "math/big"},
diff --git a/src/pkg/go/doc/headscan.go b/src/pkg/go/doc/headscan.go
index f559347638..1ccaa15819 100644
--- a/src/pkg/go/doc/headscan.go
+++ b/src/pkg/go/doc/headscan.go
@@ -24,6 +24,7 @@ import (
"go/token"
"os"
"path/filepath"
+ "regexp"
"runtime"
"strings"
)
@@ -33,10 +34,10 @@ var (
verbose = flag.Bool("v", false, "verbose mode")
)
-const (
- html_h = "<h3>"
- html_endh = "</h3>\n"
-)
+// ToHTML in comment.go assigns a (possibly blank) ID to each heading
+var html_h = regexp.MustCompile(`<h3 id="[^"]*">`)
+
+const html_endh = "</h3>\n"
func isGoFile(fi os.FileInfo) bool {
return strings.HasSuffix(fi.Name(), ".go") &&
@@ -47,11 +48,11 @@ func appendHeadings(list []string, comment string) []string {
var buf bytes.Buffer
doc.ToHTML(&buf, comment, nil)
for s := buf.String(); ; {
- i := strings.Index(s, html_h)
- if i < 0 {
+ loc := html_h.FindStringIndex(s)
+ if len(loc) == 0 {
break
}
- i += len(html_h)
+ i := loc[1]
j := strings.Index(s, html_endh)
if j < 0 {
list = append(list, s[i:]) // incorrect HTML
diff --git a/src/pkg/go/parser/error_test.go b/src/pkg/go/parser/error_test.go
index 8506077cee..48fb53e5b0 100644
--- a/src/pkg/go/parser/error_test.go
+++ b/src/pkg/go/parser/error_test.go
@@ -34,7 +34,7 @@ import (
const testdata = "testdata"
-var fsetErrs *token.FileSet
+var fsetErrs = token.NewFileSet()
// getFile assumes that each filename occurs at most once
func getFile(filename string) (file *token.File) {
@@ -169,7 +169,6 @@ func checkErrors(t *testing.T, filename string, input interface{}) {
}
func TestErrors(t *testing.T) {
- fsetErrs = token.NewFileSet()
list, err := ioutil.ReadDir(testdata)
if err != nil {
t.Fatal(err)
diff --git a/src/pkg/go/parser/parser.go b/src/pkg/go/parser/parser.go
index 8291f3f42d..9c62076f25 100644
--- a/src/pkg/go/parser/parser.go
+++ b/src/pkg/go/parser/parser.go
@@ -823,9 +823,10 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [
// parameter or result variable is the function body.
p.declare(field, nil, scope, ast.Var, idents...)
p.resolve(typ)
- if p.tok == token.COMMA {
- p.next()
+ if !p.atComma("parameter list") {
+ return
}
+ p.next()
for p.tok != token.RPAREN && p.tok != token.EOF {
idents := p.parseIdentList()
typ := p.parseVarType(ellipsisOk)
@@ -840,15 +841,15 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [
}
p.next()
}
- } else {
- // Type { "," Type } (anonymous parameters)
- params = make([]*ast.Field, len(list))
- for i, typ := range list {
- p.resolve(typ)
- params[i] = &ast.Field{Type: typ}
- }
+ return
}
+ // Type { "," Type } (anonymous parameters)
+ params = make([]*ast.Field, len(list))
+ for i, typ := range list {
+ p.resolve(typ)
+ params[i] = &ast.Field{Type: typ}
+ }
return
}
@@ -2307,36 +2308,6 @@ func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.Gen
}
}
-func (p *parser) parseReceiver(scope *ast.Scope) *ast.FieldList {
- if p.trace {
- defer un(trace(p, "Receiver"))
- }
-
- par := p.parseParameters(scope, false)
-
- // must have exactly one receiver
- if par.NumFields() != 1 {
- p.errorExpected(par.Opening, "exactly one receiver")
- par.List = []*ast.Field{{Type: &ast.BadExpr{From: par.Opening, To: par.Closing + 1}}}
- return par
- }
-
- // recv type must be of the form ["*"] identifier, possibly using parentheses
- recv := par.List[0]
- base := unparen(deref(unparen(recv.Type)))
- if _, isIdent := base.(*ast.Ident); !isIdent {
- if _, isBad := base.(*ast.BadExpr); !isBad {
- // only report error if it's a new one
- p.errorExpected(base.Pos(), "(unqualified) identifier")
- }
- par.List = []*ast.Field{
- {Type: &ast.BadExpr{From: recv.Pos(), To: p.safePos(recv.End())}},
- }
- }
-
- return par
-}
-
func (p *parser) parseFuncDecl() *ast.FuncDecl {
if p.trace {
defer un(trace(p, "FunctionDecl"))
@@ -2348,7 +2319,7 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl {
var recv *ast.FieldList
if p.tok == token.LPAREN {
- recv = p.parseReceiver(scope)
+ recv = p.parseParameters(scope, false)
}
ident := p.parseIdent()
diff --git a/src/pkg/go/parser/short_test.go b/src/pkg/go/parser/short_test.go
index 8a3c33868b..f861086ddb 100644
--- a/src/pkg/go/parser/short_test.go
+++ b/src/pkg/go/parser/short_test.go
@@ -93,6 +93,7 @@ var invalids = []string{
`package p; func f() { go f /* ERROR HERE "function must be invoked" */ }`,
`package p; func f() { defer func() {} /* ERROR HERE "function must be invoked" */ }`,
`package p; func f() { go func() { func() { f(x func /* ERROR "expected '\)'" */ (){}) } } }`,
+ `package p; func f() (a b string /* ERROR "expected '\)'" */ , ok bool) // issue 8656`,
}
func TestInvalid(t *testing.T) {
diff --git a/src/pkg/go/token/position.go b/src/pkg/go/token/position.go
index e6f0ae6a67..82d90eeb72 100644
--- a/src/pkg/go/token/position.go
+++ b/src/pkg/go/token/position.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.
-// TODO(gri) consider making this a separate package outside the go directory.
-
package token
import (
@@ -184,6 +182,7 @@ func (f *File) SetLines(lines []int) bool {
}
// SetLinesForContent sets the line offsets for the given file content.
+// It ignores position-altering //line comments.
func (f *File) SetLinesForContent(content []byte) {
var lines []int
line := 0
@@ -255,7 +254,6 @@ func (f *File) Offset(p Pos) int {
// p must be a Pos value in that file or NoPos.
//
func (f *File) Line(p Pos) int {
- // TODO(gri) this can be implemented much more efficiently
return f.Position(p).Line
}
@@ -263,13 +261,16 @@ func searchLineInfos(a []lineInfo, x int) int {
return sort.Search(len(a), func(i int) bool { return a[i].Offset > x }) - 1
}
-// info returns the file name, line, and column number for a file offset.
-func (f *File) info(offset int) (filename string, line, column int) {
+// unpack returns the filename and line and column number for a file offset.
+// If adjusted is set, unpack will return the filename and line information
+// possibly adjusted by //line comments; otherwise those comments are ignored.
+//
+func (f *File) unpack(offset int, adjusted bool) (filename string, line, column int) {
filename = f.name
if i := searchInts(f.lines, offset); i >= 0 {
line, column = i+1, offset-f.lines[i]+1
}
- if len(f.infos) > 0 {
+ if adjusted && len(f.infos) > 0 {
// almost no files have extra line infos
if i := searchLineInfos(f.infos, offset); i >= 0 {
alt := &f.infos[i]
@@ -282,26 +283,35 @@ func (f *File) info(offset int) (filename string, line, column int) {
return
}
-func (f *File) position(p Pos) (pos Position) {
+func (f *File) position(p Pos, adjusted bool) (pos Position) {
offset := int(p) - f.base
pos.Offset = offset
- pos.Filename, pos.Line, pos.Column = f.info(offset)
+ pos.Filename, pos.Line, pos.Column = f.unpack(offset, adjusted)
return
}
-// Position returns the Position value for the given file position p;
-// p must be a Pos value in that file or NoPos.
+// PositionFor returns the Position value for the given file position p.
+// If adjusted is set, the position may be adjusted by position-altering
+// //line comments; otherwise those comments are ignored.
+// p must be a Pos value in f or NoPos.
//
-func (f *File) Position(p Pos) (pos Position) {
+func (f *File) PositionFor(p Pos, adjusted bool) (pos Position) {
if p != NoPos {
if int(p) < f.base || int(p) > f.base+f.size {
panic("illegal Pos value")
}
- pos = f.position(p)
+ pos = f.position(p, adjusted)
}
return
}
+// Position returns the Position value for the given file position p.
+// Calling f.Position(p) is equivalent to calling f.PositionFor(p, true).
+//
+func (f *File) Position(p Pos) (pos Position) {
+ return f.PositionFor(p, true)
+}
+
// -----------------------------------------------------------------------------
// FileSet
@@ -427,16 +437,27 @@ func (s *FileSet) File(p Pos) (f *File) {
return
}
-// Position converts a Pos in the fileset into a general Position.
-func (s *FileSet) Position(p Pos) (pos Position) {
+// PositionFor converts a Pos p in the fileset into a Position value.
+// If adjusted is set, the position may be adjusted by position-altering
+// //line comments; otherwise those comments are ignored.
+// p must be a Pos value in s or NoPos.
+//
+func (s *FileSet) PositionFor(p Pos, adjusted bool) (pos Position) {
if p != NoPos {
if f := s.file(p); f != nil {
- pos = f.position(p)
+ pos = f.position(p, adjusted)
}
}
return
}
+// Position converts a Pos p in the fileset into a Position value.
+// Calling s.Position(p) is equivalent to calling s.PositionFor(p, true).
+//
+func (s *FileSet) Position(p Pos) (pos Position) {
+ return s.PositionFor(p, true)
+}
+
// -----------------------------------------------------------------------------
// Helper functions
diff --git a/src/pkg/go/token/position_test.go b/src/pkg/go/token/position_test.go
index ef6cfd93c2..d26939ce27 100644
--- a/src/pkg/go/token/position_test.go
+++ b/src/pkg/go/token/position_test.go
@@ -11,18 +11,18 @@ import (
"testing"
)
-func checkPos(t *testing.T, msg string, p, q Position) {
- if p.Filename != q.Filename {
- t.Errorf("%s: expected filename = %q; got %q", msg, q.Filename, p.Filename)
+func checkPos(t *testing.T, msg string, got, want Position) {
+ if got.Filename != want.Filename {
+ t.Errorf("%s: got filename = %q; want %q", msg, got.Filename, want.Filename)
}
- if p.Offset != q.Offset {
- t.Errorf("%s: expected offset = %d; got %d", msg, q.Offset, p.Offset)
+ if got.Offset != want.Offset {
+ t.Errorf("%s: got offset = %d; want %d", msg, got.Offset, want.Offset)
}
- if p.Line != q.Line {
- t.Errorf("%s: expected line = %d; got %d", msg, q.Line, p.Line)
+ if got.Line != want.Line {
+ t.Errorf("%s: got line = %d; want %d", msg, got.Line, want.Line)
}
- if p.Column != q.Column {
- t.Errorf("%s: expected column = %d; got %d", msg, q.Column, p.Column)
+ if got.Column != want.Column {
+ t.Errorf("%s: got column = %d; want %d", msg, got.Column, want.Column)
}
}
@@ -68,7 +68,7 @@ func verifyPositions(t *testing.T, fset *FileSet, f *File, lines []int) {
p := f.Pos(offs)
offs2 := f.Offset(p)
if offs2 != offs {
- t.Errorf("%s, Offset: expected offset %d; got %d", f.Name(), offs, offs2)
+ t.Errorf("%s, Offset: got offset %d; want %d", f.Name(), offs2, offs)
}
line, col := linecol(lines, offs)
msg := fmt.Sprintf("%s (offs = %d, p = %d)", f.Name(), offs, p)
@@ -93,16 +93,16 @@ func TestPositions(t *testing.T) {
for _, test := range tests {
// verify consistency of test case
if test.source != nil && len(test.source) != test.size {
- t.Errorf("%s: inconsistent test case: expected file size %d; got %d", test.filename, test.size, len(test.source))
+ t.Errorf("%s: inconsistent test case: got file size %d; want %d", test.filename, len(test.source), test.size)
}
// add file and verify name and size
f := fset.AddFile(test.filename, fset.Base()+delta, test.size)
if f.Name() != test.filename {
- t.Errorf("expected filename %q; got %q", test.filename, f.Name())
+ t.Errorf("got filename %q; want %q", f.Name(), test.filename)
}
if f.Size() != test.size {
- t.Errorf("%s: expected file size %d; got %d", f.Name(), test.size, f.Size())
+ t.Errorf("%s: got file size %d; want %d", f.Name(), f.Size(), test.size)
}
if fset.File(f.Pos(0)) != f {
t.Errorf("%s: f.Pos(0) was not found in f", f.Name())
@@ -112,12 +112,12 @@ func TestPositions(t *testing.T) {
for i, offset := range test.lines {
f.AddLine(offset)
if f.LineCount() != i+1 {
- t.Errorf("%s, AddLine: expected line count %d; got %d", f.Name(), i+1, f.LineCount())
+ t.Errorf("%s, AddLine: got line count %d; want %d", f.Name(), f.LineCount(), i+1)
}
// adding the same offset again should be ignored
f.AddLine(offset)
if f.LineCount() != i+1 {
- t.Errorf("%s, AddLine: expected unchanged line count %d; got %d", f.Name(), i+1, f.LineCount())
+ t.Errorf("%s, AddLine: got unchanged line count %d; want %d", f.Name(), f.LineCount(), i+1)
}
verifyPositions(t, fset, f, test.lines[0:i+1])
}
@@ -127,7 +127,7 @@ func TestPositions(t *testing.T) {
t.Errorf("%s: SetLines failed", f.Name())
}
if f.LineCount() != len(test.lines) {
- t.Errorf("%s, SetLines: expected line count %d; got %d", f.Name(), len(test.lines), f.LineCount())
+ t.Errorf("%s, SetLines: got line count %d; want %d", f.Name(), f.LineCount(), len(test.lines))
}
verifyPositions(t, fset, f, test.lines)
@@ -139,7 +139,7 @@ func TestPositions(t *testing.T) {
}
f.SetLinesForContent(src)
if f.LineCount() != len(test.lines) {
- t.Errorf("%s, SetLinesForContent: expected line count %d; got %d", f.Name(), len(test.lines), f.LineCount())
+ t.Errorf("%s, SetLinesForContent: got line count %d; want %d", f.Name(), f.LineCount(), len(test.lines))
}
verifyPositions(t, fset, f, test.lines)
}
@@ -177,13 +177,13 @@ func TestFiles(t *testing.T) {
j := 0
fset.Iterate(func(f *File) bool {
if f.Name() != tests[j].filename {
- t.Errorf("expected filename = %s; got %s", tests[j].filename, f.Name())
+ t.Errorf("got filename = %s; want %s", f.Name(), tests[j].filename)
}
j++
return true
})
if j != i+1 {
- t.Errorf("expected %d files; got %d", i+1, j)
+ t.Errorf("got %d files; want %d", j, i+1)
}
}
}
@@ -195,7 +195,7 @@ func TestFileSetPastEnd(t *testing.T) {
fset.AddFile(test.filename, fset.Base(), test.size)
}
if f := fset.File(Pos(fset.Base())); f != nil {
- t.Errorf("expected nil, got %v", f)
+ t.Errorf("got %v, want nil", f)
}
}
@@ -209,7 +209,7 @@ func TestFileSetCacheUnlikely(t *testing.T) {
for file, pos := range offsets {
f := fset.File(Pos(pos))
if f.Name() != file {
- t.Errorf("expecting %q at position %d, got %q", file, pos, f.Name())
+ t.Errorf("got %q at position %d, want %q", f.Name(), pos, file)
}
}
}
@@ -236,3 +236,62 @@ func TestFileSetRace(t *testing.T) {
}
stop.Wait()
}
+
+func TestPositionFor(t *testing.T) {
+ src := []byte(`
+foo
+b
+ar
+//line :100
+foobar
+//line bar:3
+done
+`)
+
+ const filename = "foo"
+ fset := NewFileSet()
+ f := fset.AddFile(filename, fset.Base(), len(src))
+ f.SetLinesForContent(src)
+
+ // verify position info
+ for i, offs := range f.lines {
+ got1 := f.PositionFor(f.Pos(offs), false)
+ got2 := f.PositionFor(f.Pos(offs), true)
+ got3 := f.Position(f.Pos(offs))
+ want := Position{filename, offs, i + 1, 1}
+ checkPos(t, "1. PositionFor unadjusted", got1, want)
+ checkPos(t, "1. PositionFor adjusted", got2, want)
+ checkPos(t, "1. Position", got3, want)
+ }
+
+ // manually add //line info on lines l1, l2
+ const l1, l2 = 5, 7
+ f.AddLineInfo(f.lines[l1-1], "", 100)
+ f.AddLineInfo(f.lines[l2-1], "bar", 3)
+
+ // unadjusted position info must remain unchanged
+ for i, offs := range f.lines {
+ got1 := f.PositionFor(f.Pos(offs), false)
+ want := Position{filename, offs, i + 1, 1}
+ checkPos(t, "2. PositionFor unadjusted", got1, want)
+ }
+
+ // adjusted position info should have changed
+ for i, offs := range f.lines {
+ got2 := f.PositionFor(f.Pos(offs), true)
+ got3 := f.Position(f.Pos(offs))
+ want := Position{filename, offs, i + 1, 1}
+ // manually compute wanted filename and line
+ line := want.Line
+ if i+1 >= l1 {
+ want.Filename = ""
+ want.Line = line - l1 + 100
+ }
+ if i+1 >= l2 {
+ want.Filename = "bar"
+ want.Line = line - l2 + 3
+ }
+ checkPos(t, "3. PositionFor adjusted", got2, want)
+ checkPos(t, "3. Position", got3, want)
+ }
+}
diff --git a/src/pkg/hash/crc32/crc32_amd64.s b/src/pkg/hash/crc32/crc32_amd64.s
index 95dc8bf41b..30b0d0691c 100644
--- a/src/pkg/hash/crc32/crc32_amd64.s
+++ b/src/pkg/hash/crc32/crc32_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// func castagnoliSSE42(crc uint32, p []byte) uint32
TEXT ·castagnoliSSE42(SB),NOSPLIT,$0
diff --git a/src/pkg/hash/crc32/crc32_amd64p32.s b/src/pkg/hash/crc32/crc32_amd64p32.s
index e34f208677..b6770eba3e 100644
--- a/src/pkg/hash/crc32/crc32_amd64p32.s
+++ b/src/pkg/hash/crc32/crc32_amd64p32.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// func castagnoliSSE42(crc uint32, p []byte) uint32
TEXT ·castagnoliSSE42(SB),NOSPLIT,$0
diff --git a/src/pkg/html/template/error.go b/src/pkg/html/template/error.go
index 46e49ccf83..8f99e1b962 100644
--- a/src/pkg/html/template/error.go
+++ b/src/pkg/html/template/error.go
@@ -6,12 +6,16 @@ package template
import (
"fmt"
+ "text/template/parse"
)
// Error describes a problem encountered during template Escaping.
type Error struct {
// ErrorCode describes the kind of error.
ErrorCode ErrorCode
+ // Node is the node that caused the problem, if known.
+ // If not nil, it overrides Name and Line.
+ Node parse.Node
// Name is the name of the template in which the error was encountered.
Name string
// Line is the line number of the error in the template source or 0.
@@ -182,9 +186,13 @@ const (
)
func (e *Error) Error() string {
- if e.Line != 0 {
+ switch {
+ case e.Node != nil:
+ loc, _ := (*parse.Tree)(nil).ErrorContext(e.Node)
+ return fmt.Sprintf("html/template:%s: %s", loc, e.Description)
+ case e.Line != 0:
return fmt.Sprintf("html/template:%s:%d: %s", e.Name, e.Line, e.Description)
- } else if e.Name != "" {
+ case e.Name != "":
return fmt.Sprintf("html/template:%s: %s", e.Name, e.Description)
}
return "html/template: " + e.Description
@@ -192,6 +200,6 @@ func (e *Error) Error() string {
// errorf creates an error given a format string f and args.
// The template Name still needs to be supplied.
-func errorf(k ErrorCode, line int, f string, args ...interface{}) *Error {
- return &Error{k, "", line, fmt.Sprintf(f, args...)}
+func errorf(k ErrorCode, node parse.Node, line int, f string, args ...interface{}) *Error {
+ return &Error{k, node, "", line, fmt.Sprintf(f, args...)}
}
diff --git a/src/pkg/html/template/escape.go b/src/pkg/html/template/escape.go
index 4e379828d4..ee01fb12ab 100644
--- a/src/pkg/html/template/escape.go
+++ b/src/pkg/html/template/escape.go
@@ -13,40 +13,33 @@ import (
"text/template/parse"
)
-// escapeTemplates rewrites the named templates, which must be
+// escapeTemplate rewrites the named template, which must be
// associated with t, to guarantee that the output of any of the named
-// templates is properly escaped. Names should include the names of
-// all templates that might be Executed but need not include helper
-// templates. If no error is returned, then the named templates have
+// templates is properly escaped. If no error is returned, then the named templates have
// been modified. Otherwise the named templates have been rendered
// unusable.
-func escapeTemplates(tmpl *Template, names ...string) error {
+func escapeTemplate(tmpl *Template, node parse.Node, name string) error {
e := newEscaper(tmpl)
- for _, name := range names {
- c, _ := e.escapeTree(context{}, name, 0)
- var err error
- if c.err != nil {
- err, c.err.Name = c.err, name
- } else if c.state != stateText {
- err = &Error{ErrEndContext, name, 0, fmt.Sprintf("ends in a non-text context: %v", c)}
- }
- if err != nil {
- // Prevent execution of unsafe templates.
- for _, name := range names {
- if t := tmpl.set[name]; t != nil {
- t.text.Tree = nil
- t.Tree = nil
- }
- }
- return err
+ c, _ := e.escapeTree(context{}, node, name, 0)
+ var err error
+ if c.err != nil {
+ err, c.err.Name = c.err, name
+ } else if c.state != stateText {
+ err = &Error{ErrEndContext, nil, name, 0, fmt.Sprintf("ends in a non-text context: %v", c)}
+ }
+ if err != nil {
+ // Prevent execution of unsafe templates.
+ if t := tmpl.set[name]; t != nil {
+ t.escapeErr = err
+ t.text.Tree = nil
+ t.Tree = nil
}
+ return err
}
e.commit()
- for _, name := range names {
- if t := tmpl.set[name]; t != nil {
- t.escaped = true
- t.Tree = t.text.Tree
- }
+ if t := tmpl.set[name]; t != nil {
+ t.escapeErr = escapeOK
+ t.Tree = t.text.Tree
}
return nil
}
@@ -168,7 +161,7 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context {
case urlPartUnknown:
return context{
state: stateError,
- err: errorf(ErrAmbigContext, n.Line, "%s appears in an ambiguous URL context", n),
+ err: errorf(ErrAmbigContext, n, n.Line, "%s appears in an ambiguous URL context", n),
}
default:
panic(c.urlPart.String())
@@ -338,7 +331,7 @@ func escFnsEq(a, b string) bool {
func newIdentCmd(identifier string, pos parse.Pos) *parse.CommandNode {
return &parse.CommandNode{
NodeType: parse.NodeCommand,
- Args: []parse.Node{parse.NewIdentifier(identifier).SetPos(pos)},
+ Args: []parse.Node{parse.NewIdentifier(identifier).SetTree(nil).SetPos(pos)}, // TODO: SetTree.
}
}
@@ -372,7 +365,7 @@ func nudge(c context) context {
// join joins the two contexts of a branch template node. The result is an
// error context if either of the input contexts are error contexts, or if the
// the input contexts differ.
-func join(a, b context, line int, nodeName string) context {
+func join(a, b context, node parse.Node, nodeName string) context {
if a.state == stateError {
return a
}
@@ -405,14 +398,14 @@ func join(a, b context, line int, nodeName string) context {
// ends in an unquoted value state even though the else branch
// ends in stateBeforeValue.
if c, d := nudge(a), nudge(b); !(c.eq(a) && d.eq(b)) {
- if e := join(c, d, line, nodeName); e.state != stateError {
+ if e := join(c, d, node, nodeName); e.state != stateError {
return e
}
}
return context{
state: stateError,
- err: errorf(ErrBranchEnd, line, "{{%s}} branches end in different contexts: %v, %v", nodeName, a, b),
+ err: errorf(ErrBranchEnd, node, 0, "{{%s}} branches end in different contexts: %v, %v", nodeName, a, b),
}
}
@@ -424,7 +417,7 @@ func (e *escaper) escapeBranch(c context, n *parse.BranchNode, nodeName string)
// We check that executing n.List once results in the same context
// as executing n.List twice.
c1, _ := e.escapeListConditionally(c0, n.List, nil)
- c0 = join(c0, c1, n.Line, nodeName)
+ c0 = join(c0, c1, n, nodeName)
if c0.state == stateError {
// Make clear that this is a problem on loop re-entry
// since developers tend to overlook that branch when
@@ -435,7 +428,7 @@ func (e *escaper) escapeBranch(c context, n *parse.BranchNode, nodeName string)
}
}
c1 := e.escapeList(c, n.ElseList)
- return join(c0, c1, n.Line, nodeName)
+ return join(c0, c1, n, nodeName)
}
// escapeList escapes a list template node.
@@ -487,7 +480,7 @@ func (e *escaper) escapeListConditionally(c context, n *parse.ListNode, filter f
// escapeTemplate escapes a {{template}} call node.
func (e *escaper) escapeTemplate(c context, n *parse.TemplateNode) context {
- c, name := e.escapeTree(c, n.Name, n.Line)
+ c, name := e.escapeTree(c, n, n.Name, n.Line)
if name != n.Name {
e.editTemplateNode(n, name)
}
@@ -496,7 +489,7 @@ func (e *escaper) escapeTemplate(c context, n *parse.TemplateNode) context {
// escapeTree escapes the named template starting in the given context as
// necessary and returns its output context.
-func (e *escaper) escapeTree(c context, name string, line int) (context, string) {
+func (e *escaper) escapeTree(c context, node parse.Node, name string, line int) (context, string) {
// Mangle the template name with the input context to produce a reliable
// identifier.
dname := c.mangle(name)
@@ -512,12 +505,12 @@ func (e *escaper) escapeTree(c context, name string, line int) (context, string)
if e.tmpl.set[name] != nil {
return context{
state: stateError,
- err: errorf(ErrNoSuchTemplate, line, "%q is an incomplete or empty template", name),
+ err: errorf(ErrNoSuchTemplate, node, line, "%q is an incomplete or empty template", name),
}, dname
}
return context{
state: stateError,
- err: errorf(ErrNoSuchTemplate, line, "no such template %q", name),
+ err: errorf(ErrNoSuchTemplate, node, line, "no such template %q", name),
}, dname
}
if dname != name {
@@ -549,8 +542,7 @@ func (e *escaper) computeOutCtx(c context, t *template.Template) context {
if !ok && c1.state != stateError {
return context{
state: stateError,
- // TODO: Find the first node with a line in t.text.Tree.Root
- err: errorf(ErrOutputContext, 0, "cannot compute output context for template %s", t.Name()),
+ err: errorf(ErrOutputContext, t.Tree.Root, 0, "cannot compute output context for template %s", t.Name()),
}
}
return c1
@@ -694,7 +686,7 @@ func contextAfterText(c context, s []byte) (context, int) {
if j := bytes.IndexAny(s[:i], "\"'<=`"); j >= 0 {
return context{
state: stateError,
- err: errorf(ErrBadHTML, 0, "%q in unquoted attr: %q", s[j:j+1], s[:i]),
+ err: errorf(ErrBadHTML, nil, 0, "%q in unquoted attr: %q", s[j:j+1], s[:i]),
}, len(s)
}
}
diff --git a/src/pkg/html/template/escape_test.go b/src/pkg/html/template/escape_test.go
index 3ccf93ece0..ef7b877484 100644
--- a/src/pkg/html/template/escape_test.go
+++ b/src/pkg/html/template/escape_test.go
@@ -861,29 +861,29 @@ func TestErrors(t *testing.T) {
// Error cases.
{
"{{if .Cond}}<a{{end}}",
- "z:1: {{if}} branches",
+ "z:1:5: {{if}} branches",
},
{
"{{if .Cond}}\n{{else}}\n<a{{end}}",
- "z:1: {{if}} branches",
+ "z:1:5: {{if}} branches",
},
{
// Missing quote in the else branch.
`{{if .Cond}}<a href="foo">{{else}}<a href="bar>{{end}}`,
- "z:1: {{if}} branches",
+ "z:1:5: {{if}} branches",
},
{
// Different kind of attribute: href implies a URL.
"<a {{if .Cond}}href='{{else}}title='{{end}}{{.X}}'>",
- "z:1: {{if}} branches",
+ "z:1:8: {{if}} branches",
},
{
"\n{{with .X}}<a{{end}}",
- "z:2: {{with}} branches",
+ "z:2:7: {{with}} branches",
},
{
"\n{{with .X}}<a>{{else}}<a{{end}}",
- "z:2: {{with}} branches",
+ "z:2:7: {{with}} branches",
},
{
"{{range .Items}}<a{{end}}",
@@ -891,7 +891,7 @@ func TestErrors(t *testing.T) {
},
{
"\n{{range .Items}} x='<a{{end}}",
- "z:2: on range loop re-entry: {{range}} branches",
+ "z:2:8: on range loop re-entry: {{range}} branches",
},
{
"<a b=1 c={{.H}}",
@@ -903,7 +903,7 @@ func TestErrors(t *testing.T) {
},
{
`<a href="{{if .F}}/foo?a={{else}}/bar/{{end}}{{.H}}">`,
- "z:1: {{.H}} appears in an ambiguous URL context",
+ "z:1:47: {{.H}} appears in an ambiguous URL context",
},
{
`<a onclick="alert('Hello \`,
@@ -932,7 +932,7 @@ func TestErrors(t *testing.T) {
},
{
`{{template "foo"}}`,
- "z:1: no such template \"foo\"",
+ "z:1:11: no such template \"foo\"",
},
{
`<div{{template "y"}}>` +
@@ -994,6 +994,11 @@ func TestErrors(t *testing.T) {
t.Errorf("input=%q: error\n\t%q\ndoes not contain expected string\n\t%q", test.input, got, test.err)
continue
}
+ // Check that we get the same error if we call Execute again.
+ if err := tmpl.Execute(buf, nil); err == nil || err.Error() != got {
+ t.Errorf("input=%q: unexpected error on second call %q", test.input, err)
+
+ }
}
}
diff --git a/src/pkg/html/template/template.go b/src/pkg/html/template/template.go
index d389658979..ce6170105c 100644
--- a/src/pkg/html/template/template.go
+++ b/src/pkg/html/template/template.go
@@ -17,7 +17,8 @@ import (
// Template is a specialized Template from "text/template" that produces a safe
// HTML document fragment.
type Template struct {
- escaped bool
+ // Sticky error if escaping fails.
+ escapeErr error
// We could embed the text/template field, but it's safer not to because
// we need to keep our version of the name space and the underlying
// template's in sync.
@@ -27,6 +28,9 @@ type Template struct {
*nameSpace // common to all associated templates
}
+// escapeOK is a sentinel value used to indicate valid escaping.
+var escapeOK = fmt.Errorf("template escaped correctly")
+
// nameSpace is the data structure shared by all templates in an association.
type nameSpace struct {
mu sync.Mutex
@@ -51,11 +55,12 @@ func (t *Template) Templates() []*Template {
func (t *Template) escape() error {
t.nameSpace.mu.Lock()
defer t.nameSpace.mu.Unlock()
- if !t.escaped {
- if err := escapeTemplates(t, t.Name()); err != nil {
+ if t.escapeErr == nil {
+ if err := escapeTemplate(t, t.text.Root, t.Name()); err != nil {
return err
}
- t.escaped = true
+ } else if t.escapeErr != escapeOK {
+ return t.escapeErr
}
return nil
}
@@ -97,14 +102,17 @@ func (t *Template) lookupAndEscapeTemplate(name string) (tmpl *Template, err err
if tmpl == nil {
return nil, fmt.Errorf("html/template: %q is undefined", name)
}
+ if tmpl.escapeErr != nil && tmpl.escapeErr != escapeOK {
+ return nil, tmpl.escapeErr
+ }
if tmpl.text.Tree == nil || tmpl.text.Root == nil {
return nil, fmt.Errorf("html/template: %q is an incomplete template", name)
}
if t.text.Lookup(name) == nil {
panic("html/template internal error: template escaping out of sync")
}
- if tmpl != nil && !tmpl.escaped {
- err = escapeTemplates(tmpl, name)
+ if tmpl.escapeErr == nil {
+ err = escapeTemplate(tmpl, tmpl.text.Root, name)
}
return tmpl, err
}
@@ -119,7 +127,7 @@ func (t *Template) lookupAndEscapeTemplate(name string) (tmpl *Template, err err
// other than space, comments, and template definitions.)
func (t *Template) Parse(src string) (*Template, error) {
t.nameSpace.mu.Lock()
- t.escaped = false
+ t.escapeErr = nil
t.nameSpace.mu.Unlock()
ret, err := t.text.Parse(src)
if err != nil {
@@ -137,7 +145,7 @@ func (t *Template) Parse(src string) (*Template, error) {
tmpl = t.new(name)
}
// Restore our record of this text/template to its unescaped original state.
- tmpl.escaped = false
+ tmpl.escapeErr = nil
tmpl.text = v
tmpl.Tree = v.Tree
}
@@ -151,7 +159,7 @@ func (t *Template) Parse(src string) (*Template, error) {
func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) {
t.nameSpace.mu.Lock()
defer t.nameSpace.mu.Unlock()
- if t.escaped {
+ if t.escapeErr != nil {
return nil, fmt.Errorf("html/template: cannot AddParseTree to %q after it has executed", t.Name())
}
text, err := t.text.AddParseTree(name, tree)
@@ -159,7 +167,7 @@ func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error
return nil, err
}
ret := &Template{
- false,
+ nil,
text,
text.Tree,
t.nameSpace,
@@ -179,7 +187,7 @@ func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error
func (t *Template) Clone() (*Template, error) {
t.nameSpace.mu.Lock()
defer t.nameSpace.mu.Unlock()
- if t.escaped {
+ if t.escapeErr != nil {
return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name())
}
textClone, err := t.text.Clone()
@@ -187,7 +195,7 @@ func (t *Template) Clone() (*Template, error) {
return nil, err
}
ret := &Template{
- false,
+ nil,
textClone,
textClone.Tree,
&nameSpace{
@@ -197,12 +205,12 @@ func (t *Template) Clone() (*Template, error) {
for _, x := range textClone.Templates() {
name := x.Name()
src := t.set[name]
- if src == nil || src.escaped {
+ if src == nil || src.escapeErr != nil {
return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name())
}
x.Tree = x.Tree.Copy()
ret.set[name] = &Template{
- false,
+ nil,
x,
x.Tree,
ret.nameSpace,
@@ -214,7 +222,7 @@ func (t *Template) Clone() (*Template, error) {
// New allocates a new HTML template with the given name.
func New(name string) *Template {
tmpl := &Template{
- false,
+ nil,
template.New(name),
nil,
&nameSpace{
@@ -237,7 +245,7 @@ func (t *Template) New(name string) *Template {
// new is the implementation of New, without the lock.
func (t *Template) new(name string) *Template {
tmpl := &Template{
- false,
+ nil,
t.text.New(name),
nil,
t.nameSpace,
diff --git a/src/pkg/html/template/transition.go b/src/pkg/html/template/transition.go
index 7f30a7ab8d..b486fcd285 100644
--- a/src/pkg/html/template/transition.go
+++ b/src/pkg/html/template/transition.go
@@ -102,7 +102,7 @@ func tTag(c context, s []byte) (context, int) {
if i == j {
return context{
state: stateError,
- err: errorf(ErrBadHTML, 0, "expected space, attr name, or end of tag, but got %q", s[i:]),
+ err: errorf(ErrBadHTML, nil, 0, "expected space, attr name, or end of tag, but got %q", s[i:]),
}, len(s)
}
switch attrType(string(s[i:j])) {
@@ -245,7 +245,7 @@ func tJS(c context, s []byte) (context, int) {
default:
return context{
state: stateError,
- err: errorf(ErrSlashAmbig, 0, "'/' could start a division or regexp: %.32q", s[i:]),
+ err: errorf(ErrSlashAmbig, nil, 0, "'/' could start a division or regexp: %.32q", s[i:]),
}, len(s)
}
default:
@@ -277,7 +277,7 @@ func tJSDelimited(c context, s []byte) (context, int) {
if i == len(s) {
return context{
state: stateError,
- err: errorf(ErrPartialEscape, 0, "unfinished escape sequence in JS string: %q", s),
+ err: errorf(ErrPartialEscape, nil, 0, "unfinished escape sequence in JS string: %q", s),
}, len(s)
}
case '[':
@@ -299,7 +299,7 @@ func tJSDelimited(c context, s []byte) (context, int) {
// into charsets is desired.
return context{
state: stateError,
- err: errorf(ErrPartialCharset, 0, "unfinished JS regexp charset: %q", s),
+ err: errorf(ErrPartialCharset, nil, 0, "unfinished JS regexp charset: %q", s),
}, len(s)
}
@@ -459,7 +459,7 @@ func tCSSStr(c context, s []byte) (context, int) {
if i == len(s) {
return context{
state: stateError,
- err: errorf(ErrPartialEscape, 0, "unfinished escape sequence in CSS string: %q", s),
+ err: errorf(ErrPartialEscape, nil, 0, "unfinished escape sequence in CSS string: %q", s),
}, len(s)
}
} else {
@@ -489,7 +489,7 @@ func eatAttrName(s []byte, i int) (int, *Error) {
// These result in a parse warning in HTML5 and are
// indicative of serious problems if seen in an attr
// name in a template.
- return -1, errorf(ErrBadHTML, 0, "%q in attribute name: %.32q", s[j:j+1], s)
+ return -1, errorf(ErrBadHTML, nil, 0, "%q in attribute name: %.32q", s[j:j+1], s)
default:
// No-op.
}
diff --git a/src/pkg/image/color/palette/gen.go b/src/pkg/image/color/palette/gen.go
index 4f4d88345a..2b5fdaaf2b 100644
--- a/src/pkg/image/color/palette/gen.go
+++ b/src/pkg/image/color/palette/gen.go
@@ -7,29 +7,49 @@
package main
// This program generates palette.go. Invoke it as
-// go run gen.go | gofmt > palette.go
+// go run gen.go -output palette.go
import (
+ "bytes"
+ "flag"
"fmt"
+ "go/format"
+ "io"
+ "io/ioutil"
+ "log"
)
+var filename = flag.String("output", "palette.go", "output file name")
+
func main() {
- fmt.Println(`// Copyright 2013 The Go Authors. All rights reserved.
+ flag.Parse()
+
+ var buf bytes.Buffer
+
+ fmt.Fprintln(&buf, `// 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.`)
- fmt.Println()
- fmt.Println("// generated by go run gen.go; DO NOT EDIT")
- fmt.Println()
- fmt.Println("// Package palette provides standard color palettes.")
- fmt.Println("package palette")
- fmt.Println()
- fmt.Println(`import "image/color"`)
- fmt.Println()
- printPlan9()
- printWebSafe()
+ fmt.Fprintln(&buf)
+ fmt.Fprintln(&buf, "// generated by go run gen.go -output palette.go; DO NOT EDIT")
+ fmt.Fprintln(&buf)
+ fmt.Fprintln(&buf, "package palette")
+ fmt.Fprintln(&buf)
+ fmt.Fprintln(&buf, `import "image/color"`)
+ fmt.Fprintln(&buf)
+ printPlan9(&buf)
+ printWebSafe(&buf)
+
+ data, err := format.Source(buf.Bytes())
+ if err != nil {
+ log.Fatal(err)
+ }
+ err = ioutil.WriteFile(*filename, data, 0644)
+ if err != nil {
+ log.Fatal(err)
+ }
}
-func printPlan9() {
+func printPlan9(w io.Writer) {
c, lines := [3]int{}, [256]string{}
for r, i := 0, 0; r != 4; r++ {
for v := 0; v != 4; v, i = v+1, i+16 {
@@ -58,27 +78,27 @@ func printPlan9() {
}
}
}
- fmt.Println("// Plan9 is a 256-color palette that partitions the 24-bit RGB space")
- fmt.Println("// into 4×4×4 subdivision, with 4 shades in each subcube. Compared to the")
- fmt.Println("// WebSafe, the idea is to reduce the color resolution by dicing the")
- fmt.Println("// color cube into fewer cells, and to use the extra space to increase the")
- fmt.Println("// intensity resolution. This results in 16 gray shades (4 gray subcubes with")
- fmt.Println("// 4 samples in each), 13 shades of each primary and secondary color (3")
- fmt.Println("// subcubes with 4 samples plus black) and a reasonable selection of colors")
- fmt.Println("// covering the rest of the color cube. The advantage is better representation")
- fmt.Println("// of continuous tones.")
- fmt.Println("//")
- fmt.Println("// This palette was used in the Plan 9 Operating System, described at")
- fmt.Println("// http://plan9.bell-labs.com/magic/man2html/6/color")
- fmt.Println("var Plan9 = []color.Color{")
+ fmt.Fprintln(w, "// Plan9 is a 256-color palette that partitions the 24-bit RGB space")
+ fmt.Fprintln(w, "// into 4×4×4 subdivision, with 4 shades in each subcube. Compared to the")
+ fmt.Fprintln(w, "// WebSafe, the idea is to reduce the color resolution by dicing the")
+ fmt.Fprintln(w, "// color cube into fewer cells, and to use the extra space to increase the")
+ fmt.Fprintln(w, "// intensity resolution. This results in 16 gray shades (4 gray subcubes with")
+ fmt.Fprintln(w, "// 4 samples in each), 13 shades of each primary and secondary color (3")
+ fmt.Fprintln(w, "// subcubes with 4 samples plus black) and a reasonable selection of colors")
+ fmt.Fprintln(w, "// covering the rest of the color cube. The advantage is better representation")
+ fmt.Fprintln(w, "// of continuous tones.")
+ fmt.Fprintln(w, "//")
+ fmt.Fprintln(w, "// This palette was used in the Plan 9 Operating System, described at")
+ fmt.Fprintln(w, "// http://plan9.bell-labs.com/magic/man2html/6/color")
+ fmt.Fprintln(w, "var Plan9 = []color.Color{")
for _, line := range lines {
- fmt.Println(line)
+ fmt.Fprintln(w, line)
}
- fmt.Println("}")
- fmt.Println()
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w)
}
-func printWebSafe() {
+func printWebSafe(w io.Writer) {
lines := [6 * 6 * 6]string{}
for r := 0; r < 6; r++ {
for g := 0; g < 6; g++ {
@@ -88,14 +108,14 @@ func printWebSafe() {
}
}
}
- fmt.Println("// WebSafe is a 216-color palette that was popularized by early versions")
- fmt.Println("// of Netscape Navigator. It is also known as the Netscape Color Cube.")
- fmt.Println("//")
- fmt.Println("// See http://en.wikipedia.org/wiki/Web_colors#Web-safe_colors for details.")
- fmt.Println("var WebSafe = []color.Color{")
+ fmt.Fprintln(w, "// WebSafe is a 216-color palette that was popularized by early versions")
+ fmt.Fprintln(w, "// of Netscape Navigator. It is also known as the Netscape Color Cube.")
+ fmt.Fprintln(w, "//")
+ fmt.Fprintln(w, "// See http://en.wikipedia.org/wiki/Web_colors#Web-safe_colors for details.")
+ fmt.Fprintln(w, "var WebSafe = []color.Color{")
for _, line := range lines {
- fmt.Println(line)
+ fmt.Fprintln(w, line)
}
- fmt.Println("}")
- fmt.Println()
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w)
}
diff --git a/src/pkg/image/color/palette/generate.go b/src/pkg/image/color/palette/generate.go
new file mode 100644
index 0000000000..64c2ec0d9a
--- /dev/null
+++ b/src/pkg/image/color/palette/generate.go
@@ -0,0 +1,8 @@
+// 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.
+
+//go:generate go run gen.go -output palette.go
+
+// Package palette provides standard color palettes.
+package palette
diff --git a/src/pkg/image/color/palette/palette.go b/src/pkg/image/color/palette/palette.go
index f761e5368d..0bf2c8e1aa 100644
--- a/src/pkg/image/color/palette/palette.go
+++ b/src/pkg/image/color/palette/palette.go
@@ -2,9 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// generated by go run gen.go; DO NOT EDIT
+// generated by go run gen.go -output palette.go; DO NOT EDIT
-// Package palette provides standard color palettes.
package palette
import "image/color"
diff --git a/src/pkg/image/png/writer.go b/src/pkg/image/png/writer.go
index 5c232b760a..df23270ee9 100644
--- a/src/pkg/image/png/writer.go
+++ b/src/pkg/image/png/writer.go
@@ -14,7 +14,13 @@ import (
"strconv"
)
+// Encoder configures encoding PNG images.
+type Encoder struct {
+ CompressionLevel CompressionLevel
+}
+
type encoder struct {
+ enc *Encoder
w io.Writer
m image.Image
cb int
@@ -24,6 +30,18 @@ type encoder struct {
tmp [4 * 256]byte
}
+type CompressionLevel int
+
+const (
+ DefaultCompression CompressionLevel = 0
+ NoCompression CompressionLevel = -1
+ BestSpeed CompressionLevel = -2
+ BestCompression CompressionLevel = -3
+
+ // Positive CompressionLevel values are reserved to mean a numeric zlib
+ // compression level, although that is not implemented yet.
+)
+
// Big-endian.
func writeUint32(b []uint8, u uint32) {
b[0] = uint8(u >> 24)
@@ -255,8 +273,11 @@ func filter(cr *[nFilter][]byte, pr []byte, bpp int) int {
return filter
}
-func writeImage(w io.Writer, m image.Image, cb int) error {
- zw := zlib.NewWriter(w)
+func writeImage(w io.Writer, m image.Image, cb int, level int) error {
+ zw, err := zlib.NewWriterLevel(w, level)
+ if err != nil {
+ return err
+ }
defer zw.Close()
bpp := 0 // Bytes per pixel.
@@ -399,7 +420,10 @@ func writeImage(w io.Writer, m image.Image, cb int) error {
}
// Apply the filter.
- f := filter(&cr, pr, bpp)
+ f := ftNone
+ if level != zlib.NoCompression {
+ f = filter(&cr, pr, bpp)
+ }
// Write the compressed bytes.
if _, err := zw.Write(cr[f]); err != nil {
@@ -419,18 +443,41 @@ func (e *encoder) writeIDATs() {
}
var bw *bufio.Writer
bw = bufio.NewWriterSize(e, 1<<15)
- e.err = writeImage(bw, e.m, e.cb)
+ e.err = writeImage(bw, e.m, e.cb, levelToZlib(e.enc.CompressionLevel))
if e.err != nil {
return
}
e.err = bw.Flush()
}
+// This function is required because we want the zero value of
+// Encoder.CompressionLevel to map to zlib.DefaultCompression.
+func levelToZlib(l CompressionLevel) int {
+ switch l {
+ case DefaultCompression:
+ return zlib.DefaultCompression
+ case NoCompression:
+ return zlib.NoCompression
+ case BestSpeed:
+ return zlib.BestSpeed
+ case BestCompression:
+ return zlib.BestCompression
+ default:
+ return zlib.DefaultCompression
+ }
+}
+
func (e *encoder) writeIEND() { e.writeChunk(nil, "IEND") }
-// Encode writes the Image m to w in PNG format. Any Image may be encoded, but
-// images that are not image.NRGBA might be encoded lossily.
+// Encode writes the Image m to w in PNG format. Any Image may be
+// encoded, but images that are not image.NRGBA might be encoded lossily.
func Encode(w io.Writer, m image.Image) error {
+ var e Encoder
+ return e.Encode(w, m)
+}
+
+// Encode writes the Image m to w in PNG format.
+func (enc *Encoder) Encode(w io.Writer, m image.Image) error {
// Obviously, negative widths and heights are invalid. Furthermore, the PNG
// spec section 11.2.2 says that zero is invalid. Excessively large images are
// also rejected.
@@ -440,6 +487,7 @@ func Encode(w io.Writer, m image.Image) error {
}
var e encoder
+ e.enc = enc
e.w = w
e.m = m
diff --git a/src/pkg/image/png/writer_test.go b/src/pkg/image/png/writer_test.go
index 3116fc9ff9..d67a815698 100644
--- a/src/pkg/image/png/writer_test.go
+++ b/src/pkg/image/png/writer_test.go
@@ -40,11 +40,7 @@ func encodeDecode(m image.Image) (image.Image, error) {
if err != nil {
return nil, err
}
- m, err = Decode(&b)
- if err != nil {
- return nil, err
- }
- return m, nil
+ return Decode(&b)
}
func TestWriter(t *testing.T) {
@@ -81,6 +77,29 @@ func TestWriter(t *testing.T) {
}
}
+func TestWriterLevels(t *testing.T) {
+ m := image.NewNRGBA(image.Rect(0, 0, 100, 100))
+
+ var b1, b2 bytes.Buffer
+ if err := (&Encoder{}).Encode(&b1, m); err != nil {
+ t.Fatal(err)
+ }
+ noenc := &Encoder{CompressionLevel: NoCompression}
+ if err := noenc.Encode(&b2, m); err != nil {
+ t.Fatal(err)
+ }
+
+ if b2.Len() <= b1.Len() {
+ t.Error("DefaultCompression encoding was larger than NoCompression encoding")
+ }
+ if _, err := Decode(&b1); err != nil {
+ t.Error("cannot decode DefaultCompression")
+ }
+ if _, err := Decode(&b2); err != nil {
+ t.Error("cannot decode NoCompression")
+ }
+}
+
func TestSubImage(t *testing.T) {
m0 := image.NewRGBA(image.Rect(0, 0, 256, 256))
for y := 0; y < 256; y++ {
diff --git a/src/pkg/internal/syscall/getrandom_linux.go b/src/pkg/internal/syscall/getrandom_linux.go
new file mode 100644
index 0000000000..944bab3f5d
--- /dev/null
+++ b/src/pkg/internal/syscall/getrandom_linux.go
@@ -0,0 +1,56 @@
+// 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 syscall
+
+import (
+ "runtime"
+ "sync/atomic"
+ stdsyscall "syscall"
+ "unsafe"
+)
+
+var randomTrap = map[string]uintptr{
+ "386": 355,
+ "amd64": 318,
+ "arm": 384,
+}[runtime.GOARCH]
+
+var randomUnsupported int32 // atomic
+
+// GetRandomFlag is a flag supported by the getrandom system call.
+type GetRandomFlag uintptr
+
+const (
+ // GRND_NONBLOCK means return EAGAIN rather than blocking.
+ GRND_NONBLOCK GetRandomFlag = 0x0001
+
+ // GRND_RANDOM means use the /dev/random pool instead of /dev/urandom.
+ GRND_RANDOM GetRandomFlag = 0x0002
+)
+
+// GetRandom calls the Linux getrandom system call.
+// See https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=c6e9d6f38894798696f23c8084ca7edbf16ee895
+func GetRandom(p []byte, flags GetRandomFlag) (n int, err error) {
+ if randomTrap == 0 {
+ return 0, stdsyscall.ENOSYS
+ }
+ if len(p) == 0 {
+ return 0, nil
+ }
+ if atomic.LoadInt32(&randomUnsupported) != 0 {
+ return 0, stdsyscall.ENOSYS
+ }
+ r1, _, errno := stdsyscall.Syscall(randomTrap,
+ uintptr(unsafe.Pointer(&p[0])),
+ uintptr(len(p)),
+ uintptr(flags))
+ if errno != 0 {
+ if errno == stdsyscall.ENOSYS {
+ atomic.StoreInt32(&randomUnsupported, 1)
+ }
+ return 0, errno
+ }
+ return int(r1), nil
+}
diff --git a/src/pkg/io/io.go b/src/pkg/io/io.go
index 022fdb6764..e8bbad537c 100644
--- a/src/pkg/io/io.go
+++ b/src/pkg/io/io.go
@@ -63,7 +63,7 @@ var ErrNoProgress = errors.New("multiple Read calls return no data or error")
//
// Implementations of Read are discouraged from returning a
// zero byte count with a nil error, and callers should treat
-// that situation as a no-op.
+// that situation as a no-op. Implementations must not retain p.
type Reader interface {
Read(p []byte) (n int, err error)
}
@@ -75,6 +75,8 @@ type Reader interface {
// and any error encountered that caused the write to stop early.
// Write must return a non-nil error if it returns n < len(p).
// Write must not modify the slice data, even temporarily.
+//
+// Implementations must not retain p.
type Writer interface {
Write(p []byte) (n int, err error)
}
@@ -192,6 +194,8 @@ type WriterTo interface {
//
// Clients of ReadAt can execute parallel ReadAt calls on the
// same input source.
+//
+// Implementations must not retain p.
type ReaderAt interface {
ReadAt(p []byte, off int64) (n int, err error)
}
@@ -209,6 +213,8 @@ type ReaderAt interface {
//
// Clients of WriteAt can execute parallel WriteAt calls on the same
// destination if the ranges do not overlap.
+//
+// Implementations must not retain p.
type WriterAt interface {
WriteAt(p []byte, off int64) (n int, err error)
}
diff --git a/src/pkg/math/abs_386.s b/src/pkg/math/abs_386.s
index 3490cf66c7..f30a439c26 100644
--- a/src/pkg/math/abs_386.s
+++ b/src/pkg/math/abs_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// func Abs(x float64) float64
TEXT ·Abs(SB),NOSPLIT,$0
diff --git a/src/pkg/math/abs_amd64.s b/src/pkg/math/abs_amd64.s
index 779c8f5484..0424eb5fad 100644
--- a/src/pkg/math/abs_amd64.s
+++ b/src/pkg/math/abs_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// func Abs(x float64) float64
TEXT ·Abs(SB),NOSPLIT,$0
diff --git a/src/pkg/math/abs_arm.s b/src/pkg/math/abs_arm.s
index b5117ab39c..bfa77eb491 100644
--- a/src/pkg/math/abs_arm.s
+++ b/src/pkg/math/abs_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Abs(SB),NOSPLIT,$0
MOVW x_lo+0(FP), R0
diff --git a/src/pkg/math/abs_power64x.s b/src/pkg/math/abs_power64x.s
index 8fd8020f98..3ba8201b3b 100644
--- a/src/pkg/math/abs_power64x.s
+++ b/src/pkg/math/abs_power64x.s
@@ -4,7 +4,7 @@
// +build power64 power64le
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Abs(SB),NOSPLIT,$0-16
MOVD x+0(FP), R3
diff --git a/src/pkg/math/asin_386.s b/src/pkg/math/asin_386.s
index 2c1d270948..4f34e123ef 100644
--- a/src/pkg/math/asin_386.s
+++ b/src/pkg/math/asin_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// func Asin(x float64) float64
TEXT ·Asin(SB),NOSPLIT,$0
diff --git a/src/pkg/math/asin_amd64.s b/src/pkg/math/asin_amd64.s
index ea48104ac6..1a43d489b5 100644
--- a/src/pkg/math/asin_amd64.s
+++ b/src/pkg/math/asin_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Asin(SB),NOSPLIT,$0
JMP ·asin(SB)
diff --git a/src/pkg/math/asin_arm.s b/src/pkg/math/asin_arm.s
index b90526003e..8fe03b61d8 100644
--- a/src/pkg/math/asin_arm.s
+++ b/src/pkg/math/asin_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Asin(SB),NOSPLIT,$0
B ·asin(SB)
diff --git a/src/pkg/math/atan2_386.s b/src/pkg/math/atan2_386.s
index fb649316a0..31a74e7262 100644
--- a/src/pkg/math/atan2_386.s
+++ b/src/pkg/math/atan2_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// func Atan2(y, x float64) float64 // =atan(y/x)
TEXT ·Atan2(SB),NOSPLIT,$0
diff --git a/src/pkg/math/atan2_amd64.s b/src/pkg/math/atan2_amd64.s
index f7a5a11d46..fc471f76c5 100644
--- a/src/pkg/math/atan2_amd64.s
+++ b/src/pkg/math/atan2_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Atan2(SB),NOSPLIT,$0
JMP ·atan2(SB)
diff --git a/src/pkg/math/atan2_arm.s b/src/pkg/math/atan2_arm.s
index 24bff2c03d..06c12ecbc4 100644
--- a/src/pkg/math/atan2_arm.s
+++ b/src/pkg/math/atan2_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Atan2(SB),NOSPLIT,$0
B ·atan2(SB)
diff --git a/src/pkg/math/atan_386.s b/src/pkg/math/atan_386.s
index aad8ffcec7..f3976b1d35 100644
--- a/src/pkg/math/atan_386.s
+++ b/src/pkg/math/atan_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// func Atan(x float64) float64
TEXT ·Atan(SB),NOSPLIT,$0
diff --git a/src/pkg/math/atan_amd64.s b/src/pkg/math/atan_amd64.s
index fc4a91b0de..b801ae99d2 100644
--- a/src/pkg/math/atan_amd64.s
+++ b/src/pkg/math/atan_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Atan(SB),NOSPLIT,$0
JMP ·atan(SB)
diff --git a/src/pkg/math/atan_arm.s b/src/pkg/math/atan_arm.s
index defa93a1ed..d190a8bb0c 100644
--- a/src/pkg/math/atan_arm.s
+++ b/src/pkg/math/atan_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Atan(SB),NOSPLIT,$0
B ·atan(SB)
diff --git a/src/pkg/math/big/arith_386.s b/src/pkg/math/big/arith_386.s
index 15b036c657..1b47c898f9 100644
--- a/src/pkg/math/big/arith_386.s
+++ b/src/pkg/math/big/arith_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// This file provides fast assembly versions for the elementary
// arithmetic operations on vectors implemented in arith.go.
diff --git a/src/pkg/math/big/arith_amd64.s b/src/pkg/math/big/arith_amd64.s
index e2113a7e3a..56c4cb050e 100644
--- a/src/pkg/math/big/arith_amd64.s
+++ b/src/pkg/math/big/arith_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// This file provides fast assembly versions for the elementary
// arithmetic operations on vectors implemented in arith.go.
diff --git a/src/pkg/math/big/arith_amd64p32.s b/src/pkg/math/big/arith_amd64p32.s
index 227870a005..908dbbdc58 100644
--- a/src/pkg/math/big/arith_amd64p32.s
+++ b/src/pkg/math/big/arith_amd64p32.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·mulWW(SB),NOSPLIT,$0
JMP ·mulWW_g(SB)
diff --git a/src/pkg/math/big/arith_arm.s b/src/pkg/math/big/arith_arm.s
index 8d36761c4c..a4c51c2127 100644
--- a/src/pkg/math/big/arith_arm.s
+++ b/src/pkg/math/big/arith_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// This file provides fast assembly versions for the elementary
// arithmetic operations on vectors implemented in arith.go.
diff --git a/src/pkg/math/big/arith_power64x.s b/src/pkg/math/big/arith_power64x.s
index 502d40cb64..c33a9209f1 100644
--- a/src/pkg/math/big/arith_power64x.s
+++ b/src/pkg/math/big/arith_power64x.s
@@ -4,7 +4,7 @@
// +build power64 power64le
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// This file provides fast assembly versions for the elementary
// arithmetic operations on vectors implemented in arith.go.
diff --git a/src/pkg/math/dim_386.s b/src/pkg/math/dim_386.s
index f715114c43..c8194fed83 100644
--- a/src/pkg/math/dim_386.s
+++ b/src/pkg/math/dim_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Dim(SB),NOSPLIT,$0
JMP ·dim(SB)
diff --git a/src/pkg/math/dim_amd64.s b/src/pkg/math/dim_amd64.s
index 38423ef028..622cc3fbad 100644
--- a/src/pkg/math/dim_amd64.s
+++ b/src/pkg/math/dim_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
#define PosInf 0x7FF0000000000000
#define NaN 0x7FF8000000000001
diff --git a/src/pkg/math/dim_arm.s b/src/pkg/math/dim_arm.s
index 162f08cda5..be66950687 100644
--- a/src/pkg/math/dim_arm.s
+++ b/src/pkg/math/dim_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Dim(SB),NOSPLIT,$0
B ·dim(SB)
diff --git a/src/pkg/math/exp2_386.s b/src/pkg/math/exp2_386.s
index 71959d94dd..7d11920c2d 100644
--- a/src/pkg/math/exp2_386.s
+++ b/src/pkg/math/exp2_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// func Exp2(x float64) float64
TEXT ·Exp2(SB),NOSPLIT,$0
diff --git a/src/pkg/math/exp2_amd64.s b/src/pkg/math/exp2_amd64.s
index 77a53dad4e..903c835896 100644
--- a/src/pkg/math/exp2_amd64.s
+++ b/src/pkg/math/exp2_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Exp2(SB),NOSPLIT,$0
JMP ·exp2(SB)
diff --git a/src/pkg/math/exp2_arm.s b/src/pkg/math/exp2_arm.s
index fe51f25226..58283cd082 100644
--- a/src/pkg/math/exp2_arm.s
+++ b/src/pkg/math/exp2_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Exp2(SB),NOSPLIT,$0
B ·exp2(SB)
diff --git a/src/pkg/math/exp_386.s b/src/pkg/math/exp_386.s
index af2d680d55..6a478a5e60 100644
--- a/src/pkg/math/exp_386.s
+++ b/src/pkg/math/exp_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// func Exp(x float64) float64
TEXT ·Exp(SB),NOSPLIT,$0
diff --git a/src/pkg/math/exp_amd64.s b/src/pkg/math/exp_amd64.s
index 070b45264a..d9cf8fd86c 100644
--- a/src/pkg/math/exp_amd64.s
+++ b/src/pkg/math/exp_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// The method is based on a paper by Naoki Shibata: "Efficient evaluation
// methods of elementary functions suitable for SIMD computation", Proc.
diff --git a/src/pkg/math/exp_arm.s b/src/pkg/math/exp_arm.s
index 130b502ac1..ce36d03caa 100644
--- a/src/pkg/math/exp_arm.s
+++ b/src/pkg/math/exp_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Exp(SB),NOSPLIT,$0
B ·exp(SB)
diff --git a/src/pkg/math/expm1_386.s b/src/pkg/math/expm1_386.s
index b268c58c63..a48ca8a58a 100644
--- a/src/pkg/math/expm1_386.s
+++ b/src/pkg/math/expm1_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// func Expm1(x float64) float64
TEXT ·Expm1(SB),NOSPLIT,$0
diff --git a/src/pkg/math/expm1_amd64.s b/src/pkg/math/expm1_amd64.s
index 66a75b3d55..b7d5a3be01 100644
--- a/src/pkg/math/expm1_amd64.s
+++ b/src/pkg/math/expm1_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Expm1(SB),NOSPLIT,$0
JMP ·expm1(SB)
diff --git a/src/pkg/math/expm1_arm.s b/src/pkg/math/expm1_arm.s
index 838744447f..5f80d872f3 100644
--- a/src/pkg/math/expm1_arm.s
+++ b/src/pkg/math/expm1_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Expm1(SB),NOSPLIT,$0
B ·expm1(SB)
diff --git a/src/pkg/math/floor_386.s b/src/pkg/math/floor_386.s
index 37d5a4559f..31c9b174d6 100644
--- a/src/pkg/math/floor_386.s
+++ b/src/pkg/math/floor_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// func Ceil(x float64) float64
TEXT ·Ceil(SB),NOSPLIT,$0
diff --git a/src/pkg/math/floor_amd64.s b/src/pkg/math/floor_amd64.s
index 2fd31c4fae..67b7cdec04 100644
--- a/src/pkg/math/floor_amd64.s
+++ b/src/pkg/math/floor_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
#define Big 0x4330000000000000 // 2**52
diff --git a/src/pkg/math/floor_arm.s b/src/pkg/math/floor_arm.s
index cb3b98e95c..59091765b3 100644
--- a/src/pkg/math/floor_arm.s
+++ b/src/pkg/math/floor_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Floor(SB),NOSPLIT,$0
B ·floor(SB)
diff --git a/src/pkg/math/frexp_386.s b/src/pkg/math/frexp_386.s
index c6d0a80eda..5bff7e2156 100644
--- a/src/pkg/math/frexp_386.s
+++ b/src/pkg/math/frexp_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// func Frexp(f float64) (frac float64, exp int)
TEXT ·Frexp(SB),NOSPLIT,$0
diff --git a/src/pkg/math/frexp_amd64.s b/src/pkg/math/frexp_amd64.s
index 03d1012433..93a321039b 100644
--- a/src/pkg/math/frexp_amd64.s
+++ b/src/pkg/math/frexp_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Frexp(SB),NOSPLIT,$0
JMP ·frexp(SB)
diff --git a/src/pkg/math/frexp_arm.s b/src/pkg/math/frexp_arm.s
index 9d40ae46a6..7842eca59b 100644
--- a/src/pkg/math/frexp_arm.s
+++ b/src/pkg/math/frexp_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Frexp(SB),NOSPLIT,$0
B ·frexp(SB)
diff --git a/src/pkg/math/hypot_386.s b/src/pkg/math/hypot_386.s
index eec1bf5548..d321f465b4 100644
--- a/src/pkg/math/hypot_386.s
+++ b/src/pkg/math/hypot_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// func Hypot(p, q float64) float64
TEXT ·Hypot(SB),NOSPLIT,$0
diff --git a/src/pkg/math/hypot_amd64.s b/src/pkg/math/hypot_amd64.s
index 5c0ff4dd83..a68eebc8ca 100644
--- a/src/pkg/math/hypot_amd64.s
+++ b/src/pkg/math/hypot_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
#define PosInf 0x7FF0000000000000
#define NaN 0x7FF8000000000001
diff --git a/src/pkg/math/hypot_arm.s b/src/pkg/math/hypot_arm.s
index 2562aa8306..9c8abca130 100644
--- a/src/pkg/math/hypot_arm.s
+++ b/src/pkg/math/hypot_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Hypot(SB),NOSPLIT,$0
B ·hypot(SB)
diff --git a/src/pkg/math/ldexp_386.s b/src/pkg/math/ldexp_386.s
index baf377ead6..ac8e8ba540 100644
--- a/src/pkg/math/ldexp_386.s
+++ b/src/pkg/math/ldexp_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// func Ldexp(frac float64, exp int) float64
TEXT ·Ldexp(SB),NOSPLIT,$0
diff --git a/src/pkg/math/ldexp_amd64.s b/src/pkg/math/ldexp_amd64.s
index c7fc226efa..6063a64807 100644
--- a/src/pkg/math/ldexp_amd64.s
+++ b/src/pkg/math/ldexp_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Ldexp(SB),NOSPLIT,$0
JMP ·ldexp(SB)
diff --git a/src/pkg/math/ldexp_arm.s b/src/pkg/math/ldexp_arm.s
index 16744ea57d..fcffa2e0fa 100644
--- a/src/pkg/math/ldexp_arm.s
+++ b/src/pkg/math/ldexp_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Ldexp(SB),NOSPLIT,$0
B ·ldexp(SB)
diff --git a/src/pkg/math/log10_386.s b/src/pkg/math/log10_386.s
index 4ae069da62..2897f3c155 100644
--- a/src/pkg/math/log10_386.s
+++ b/src/pkg/math/log10_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// func Log10(x float64) float64
TEXT ·Log10(SB),NOSPLIT,$0
diff --git a/src/pkg/math/log10_amd64.s b/src/pkg/math/log10_amd64.s
index b9ae832689..8382ba7ae5 100644
--- a/src/pkg/math/log10_amd64.s
+++ b/src/pkg/math/log10_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Log10(SB),NOSPLIT,$0
JMP ·log10(SB)
diff --git a/src/pkg/math/log10_arm.s b/src/pkg/math/log10_arm.s
index fa7580f6fb..dbcb8351cf 100644
--- a/src/pkg/math/log10_arm.s
+++ b/src/pkg/math/log10_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Log10(SB),NOSPLIT,$0
B ·log10(SB)
diff --git a/src/pkg/math/log1p_386.s b/src/pkg/math/log1p_386.s
index 3b30fd5b7f..1c2d683a8f 100644
--- a/src/pkg/math/log1p_386.s
+++ b/src/pkg/math/log1p_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// func Log1p(x float64) float64
TEXT ·Log1p(SB),NOSPLIT,$0
diff --git a/src/pkg/math/log1p_amd64.s b/src/pkg/math/log1p_amd64.s
index 48c24f41f2..1e58fb110d 100644
--- a/src/pkg/math/log1p_amd64.s
+++ b/src/pkg/math/log1p_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Log1p(SB),NOSPLIT,$0
JMP ·log1p(SB)
diff --git a/src/pkg/math/log1p_arm.s b/src/pkg/math/log1p_arm.s
index fd2740d0dd..95d5496788 100644
--- a/src/pkg/math/log1p_arm.s
+++ b/src/pkg/math/log1p_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Log1p(SB),NOSPLIT,$0
B ·log1p(SB)
diff --git a/src/pkg/math/log_386.s b/src/pkg/math/log_386.s
index 21a0633b19..ff998afb47 100644
--- a/src/pkg/math/log_386.s
+++ b/src/pkg/math/log_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// func Log(x float64) float64
TEXT ·Log(SB),NOSPLIT,$0
diff --git a/src/pkg/math/log_amd64.s b/src/pkg/math/log_amd64.s
index dffc5aec88..84c60ab4de 100644
--- a/src/pkg/math/log_amd64.s
+++ b/src/pkg/math/log_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
#define HSqrt2 7.07106781186547524401e-01 // sqrt(2)/2
#define Ln2Hi 6.93147180369123816490e-01 // 0x3fe62e42fee00000
diff --git a/src/pkg/math/log_arm.s b/src/pkg/math/log_arm.s
index 28448aeefc..e21d0366e9 100644
--- a/src/pkg/math/log_arm.s
+++ b/src/pkg/math/log_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Log(SB),NOSPLIT,$0
B ·log(SB)
diff --git a/src/pkg/math/mod_386.s b/src/pkg/math/mod_386.s
index 9b3b6bd06f..10ad98be3e 100644
--- a/src/pkg/math/mod_386.s
+++ b/src/pkg/math/mod_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// func Mod(x, y float64) float64
TEXT ·Mod(SB),NOSPLIT,$0
diff --git a/src/pkg/math/mod_amd64.s b/src/pkg/math/mod_amd64.s
index bef83fcf7f..f99dbe2931 100644
--- a/src/pkg/math/mod_amd64.s
+++ b/src/pkg/math/mod_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Mod(SB),NOSPLIT,$0
JMP ·mod(SB)
diff --git a/src/pkg/math/mod_arm.s b/src/pkg/math/mod_arm.s
index 1f51588f8d..5afb3594dd 100644
--- a/src/pkg/math/mod_arm.s
+++ b/src/pkg/math/mod_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Mod(SB),NOSPLIT,$0
B ·mod(SB)
diff --git a/src/pkg/math/modf_386.s b/src/pkg/math/modf_386.s
index 07a0dc5cfe..3debd3b95d 100644
--- a/src/pkg/math/modf_386.s
+++ b/src/pkg/math/modf_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// func Modf(f float64) (int float64, frac float64)
TEXT ·Modf(SB),NOSPLIT,$0
diff --git a/src/pkg/math/modf_amd64.s b/src/pkg/math/modf_amd64.s
index 05feb4bf90..701cf72a3b 100644
--- a/src/pkg/math/modf_amd64.s
+++ b/src/pkg/math/modf_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Modf(SB),NOSPLIT,$0
JMP ·modf(SB)
diff --git a/src/pkg/math/modf_arm.s b/src/pkg/math/modf_arm.s
index e6bd26d53b..ea3c8dc74f 100644
--- a/src/pkg/math/modf_arm.s
+++ b/src/pkg/math/modf_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Modf(SB),NOSPLIT,$0
B ·modf(SB)
diff --git a/src/pkg/math/remainder_386.s b/src/pkg/math/remainder_386.s
index bbe13a0d92..318fa2c465 100644
--- a/src/pkg/math/remainder_386.s
+++ b/src/pkg/math/remainder_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// func Remainder(x, y float64) float64
TEXT ·Remainder(SB),NOSPLIT,$0
diff --git a/src/pkg/math/remainder_amd64.s b/src/pkg/math/remainder_amd64.s
index e5e23c7ce3..f7fda99d85 100644
--- a/src/pkg/math/remainder_amd64.s
+++ b/src/pkg/math/remainder_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Remainder(SB),NOSPLIT,$0
JMP ·remainder(SB)
diff --git a/src/pkg/math/remainder_arm.s b/src/pkg/math/remainder_arm.s
index 8728afe931..1ae597a60e 100644
--- a/src/pkg/math/remainder_arm.s
+++ b/src/pkg/math/remainder_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Remainder(SB),NOSPLIT,$0
B ·remainder(SB)
diff --git a/src/pkg/math/sin_386.s b/src/pkg/math/sin_386.s
index 09271c0355..ccc8e64bee 100644
--- a/src/pkg/math/sin_386.s
+++ b/src/pkg/math/sin_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// func Cos(x float64) float64
TEXT ·Cos(SB),NOSPLIT,$0
diff --git a/src/pkg/math/sin_amd64.s b/src/pkg/math/sin_amd64.s
index 008bf4be58..0c33cecef3 100644
--- a/src/pkg/math/sin_amd64.s
+++ b/src/pkg/math/sin_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Sin(SB),NOSPLIT,$0
JMP ·sin(SB)
diff --git a/src/pkg/math/sin_arm.s b/src/pkg/math/sin_arm.s
index a057b4fc9d..467af3dead 100644
--- a/src/pkg/math/sin_arm.s
+++ b/src/pkg/math/sin_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Sin(SB),NOSPLIT,$0
B ·sin(SB)
diff --git a/src/pkg/math/sincos_386.s b/src/pkg/math/sincos_386.s
index bf964b1681..83af5016ef 100644
--- a/src/pkg/math/sincos_386.s
+++ b/src/pkg/math/sincos_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// func Sincos(x float64) (sin, cos float64)
TEXT ·Sincos(SB),NOSPLIT,$0
diff --git a/src/pkg/math/sincos_amd64.s b/src/pkg/math/sincos_amd64.s
index bccc1ade12..dae636b248 100644
--- a/src/pkg/math/sincos_amd64.s
+++ b/src/pkg/math/sincos_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// The method is based on a paper by Naoki Shibata: "Efficient evaluation
// methods of elementary functions suitable for SIMD computation", Proc.
diff --git a/src/pkg/math/sincos_arm.s b/src/pkg/math/sincos_arm.s
index b6866af54f..9fe048248b 100644
--- a/src/pkg/math/sincos_arm.s
+++ b/src/pkg/math/sincos_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Sincos(SB),NOSPLIT,$0
B ·sincos(SB)
diff --git a/src/pkg/math/sqrt_386.s b/src/pkg/math/sqrt_386.s
index 2d0c786d0c..5234a1e881 100644
--- a/src/pkg/math/sqrt_386.s
+++ b/src/pkg/math/sqrt_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// func Sqrt(x float64) float64
TEXT ·Sqrt(SB),NOSPLIT,$0
diff --git a/src/pkg/math/sqrt_amd64.s b/src/pkg/math/sqrt_amd64.s
index 1508944c90..443d83fe35 100644
--- a/src/pkg/math/sqrt_amd64.s
+++ b/src/pkg/math/sqrt_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// func Sqrt(x float64) float64
TEXT ·Sqrt(SB),NOSPLIT,$0
diff --git a/src/pkg/math/sqrt_arm.s b/src/pkg/math/sqrt_arm.s
index f731ee976d..4f9dc2e038 100644
--- a/src/pkg/math/sqrt_arm.s
+++ b/src/pkg/math/sqrt_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// func Sqrt(x float64) float64
TEXT ·Sqrt(SB),NOSPLIT,$0
diff --git a/src/pkg/math/tan_386.s b/src/pkg/math/tan_386.s
index 2320326e37..f1bdae1536 100644
--- a/src/pkg/math/tan_386.s
+++ b/src/pkg/math/tan_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// func Tan(x float64) float64
TEXT ·Tan(SB),NOSPLIT,$0
diff --git a/src/pkg/math/tan_amd64.s b/src/pkg/math/tan_amd64.s
index 9fa5f148ef..39aa08061c 100644
--- a/src/pkg/math/tan_amd64.s
+++ b/src/pkg/math/tan_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Tan(SB),NOSPLIT,$0
JMP ·tan(SB)
diff --git a/src/pkg/math/tan_arm.s b/src/pkg/math/tan_arm.s
index 68fea318da..36c7c128f6 100644
--- a/src/pkg/math/tan_arm.s
+++ b/src/pkg/math/tan_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·Tan(SB),NOSPLIT,$0
B ·tan(SB)
diff --git a/src/pkg/mime/type.go b/src/pkg/mime/type.go
index 00cff263ba..ffda1f0ce5 100644
--- a/src/pkg/mime/type.go
+++ b/src/pkg/mime/type.go
@@ -11,26 +11,41 @@ import (
"sync"
)
-var mimeTypes = map[string]string{
- ".css": "text/css; charset=utf-8",
- ".gif": "image/gif",
- ".htm": "text/html; charset=utf-8",
- ".html": "text/html; charset=utf-8",
- ".jpg": "image/jpeg",
- ".js": "application/x-javascript",
- ".pdf": "application/pdf",
- ".png": "image/png",
- ".xml": "text/xml; charset=utf-8",
-}
+var (
+ mimeLock sync.RWMutex
+ mimeTypesLower = map[string]string{
+ ".css": "text/css; charset=utf-8",
+ ".gif": "image/gif",
+ ".htm": "text/html; charset=utf-8",
+ ".html": "text/html; charset=utf-8",
+ ".jpg": "image/jpeg",
+ ".js": "application/x-javascript",
+ ".pdf": "application/pdf",
+ ".png": "image/png",
+ ".xml": "text/xml; charset=utf-8",
+ }
+ mimeTypes = clone(mimeTypesLower)
+)
-var mimeLock sync.RWMutex
+func clone(m map[string]string) map[string]string {
+ m2 := make(map[string]string, len(m))
+ for k, v := range m {
+ m2[k] = v
+ if strings.ToLower(k) != k {
+ panic("keys in mimeTypesLower must be lowercase")
+ }
+ }
+ return m2
+}
-var once sync.Once
+var once sync.Once // guards initMime
// TypeByExtension returns the MIME type associated with the file extension ext.
// The extension ext should begin with a leading dot, as in ".html".
// When ext has no associated type, TypeByExtension returns "".
//
+// Extensions are looked up first case-sensitively, then case-insensitively.
+//
// The built-in table is small but on unix it is augmented by the local
// system's mime.types file(s) if available under one or more of these
// names:
@@ -39,23 +54,49 @@ var once sync.Once
// /etc/apache2/mime.types
// /etc/apache/mime.types
//
-// Windows system mime types are extracted from registry.
+// On Windows, MIME types are extracted from the registry.
//
// Text types have the charset parameter set to "utf-8" by default.
func TypeByExtension(ext string) string {
once.Do(initMime)
mimeLock.RLock()
- typename := mimeTypes[ext]
- mimeLock.RUnlock()
- return typename
+ defer mimeLock.RUnlock()
+
+ // Case-sensitive lookup.
+ v := mimeTypes[ext]
+ if v != "" {
+ return v
+ }
+
+ // Case-insensitive lookup.
+ // Optimistically assume a short ASCII extension and be
+ // allocation-free in that case.
+ var buf [10]byte
+ lower := buf[:0]
+ const utf8RuneSelf = 0x80 // from utf8 package, but not importing it.
+ for i := 0; i < len(ext); i++ {
+ c := ext[i]
+ if c >= utf8RuneSelf {
+ // Slow path.
+ return mimeTypesLower[strings.ToLower(ext)]
+ }
+ if 'A' <= c && c <= 'Z' {
+ lower = append(lower, c+('a'-'A'))
+ } else {
+ lower = append(lower, c)
+ }
+ }
+ // The conversion from []byte to string doesn't allocate in
+ // a map lookup.
+ return mimeTypesLower[string(lower)]
}
// AddExtensionType sets the MIME type associated with
-// the extension ext to typ. The extension should begin with
+// the extension ext to typ. The extension should begin with
// a leading dot, as in ".html".
func AddExtensionType(ext, typ string) error {
- if ext == "" || ext[0] != '.' {
- return fmt.Errorf(`mime: extension "%s" misses dot`, ext)
+ if !strings.HasPrefix(ext, ".") {
+ return fmt.Errorf(`mime: extension %q misses dot`, ext)
}
once.Do(initMime)
return setExtensionType(ext, typ)
@@ -70,8 +111,11 @@ func setExtensionType(extension, mimeType string) error {
param["charset"] = "utf-8"
mimeType = FormatMediaType(mimeType, param)
}
+ extLower := strings.ToLower(extension)
+
mimeLock.Lock()
mimeTypes[extension] = mimeType
+ mimeTypesLower[extLower] = mimeType
mimeLock.Unlock()
return nil
}
diff --git a/src/pkg/mime/type_plan9.go b/src/pkg/mime/type_plan9.go
index b8f0511ee7..8cbf6777f1 100644
--- a/src/pkg/mime/type_plan9.go
+++ b/src/pkg/mime/type_plan9.go
@@ -48,6 +48,6 @@ func initMimeForTests() map[string]string {
return map[string]string{
".t1": "application/test",
".t2": "text/test; charset=utf-8",
- ".png": "image/png",
+ ".pNg": "image/png",
}
}
diff --git a/src/pkg/mime/type_test.go b/src/pkg/mime/type_test.go
index 07e1cd5dae..d2d254ae9a 100644
--- a/src/pkg/mime/type_test.go
+++ b/src/pkg/mime/type_test.go
@@ -4,7 +4,9 @@
package mime
-import "testing"
+import (
+ "testing"
+)
var typeTests = initMimeForTests()
@@ -14,16 +16,39 @@ func TestTypeByExtension(t *testing.T) {
if val != want {
t.Errorf("TypeByExtension(%q) = %q, want %q", ext, val, want)
}
-
}
}
-func TestCustomExtension(t *testing.T) {
- custom := "text/xml; charset=iso-8859-1"
- if error := AddExtensionType(".xml", custom); error != nil {
- t.Fatalf("error %s for AddExtension(%s)", error, custom)
+func TestTypeByExtensionCase(t *testing.T) {
+ const custom = "test/test; charset=iso-8859-1"
+ const caps = "test/test; WAS=ALLCAPS"
+ if err := AddExtensionType(".TEST", caps); err != nil {
+ t.Fatalf("error %s for AddExtension(%s)", err, custom)
+ }
+ if err := AddExtensionType(".tesT", custom); err != nil {
+ t.Fatalf("error %s for AddExtension(%s)", err, custom)
+ }
+
+ // case-sensitive lookup
+ if got := TypeByExtension(".tesT"); got != custom {
+ t.Fatalf("for .tesT, got %q; want %q", got, custom)
+ }
+ if got := TypeByExtension(".TEST"); got != caps {
+ t.Fatalf("for .TEST, got %q; want %s", got, caps)
}
- if registered := TypeByExtension(".xml"); registered != custom {
- t.Fatalf("registered %s instead of %s", registered, custom)
+
+ // case-insensitive
+ if got := TypeByExtension(".TesT"); got != custom {
+ t.Fatalf("for .TesT, got %q; want %q", got, custom)
+ }
+}
+
+func TestLookupMallocs(t *testing.T) {
+ n := testing.AllocsPerRun(10000, func() {
+ TypeByExtension(".html")
+ TypeByExtension(".HtML")
+ })
+ if n > 0 {
+ t.Errorf("allocs = %v; want 0", n)
}
}
diff --git a/src/pkg/mime/type_unix.go b/src/pkg/mime/type_unix.go
index 1d394315a4..3e404cf742 100644
--- a/src/pkg/mime/type_unix.go
+++ b/src/pkg/mime/type_unix.go
@@ -53,7 +53,7 @@ func initMime() {
func initMimeForTests() map[string]string {
typeFiles = []string{"testdata/test.types"}
return map[string]string{
- ".t1": "application/test",
+ ".T1": "application/test",
".t2": "text/test; charset=utf-8",
".png": "image/png",
}
diff --git a/src/pkg/mime/type_windows.go b/src/pkg/mime/type_windows.go
index 180f948d16..ae758d78b3 100644
--- a/src/pkg/mime/type_windows.go
+++ b/src/pkg/mime/type_windows.go
@@ -58,6 +58,6 @@ func initMime() {
func initMimeForTests() map[string]string {
return map[string]string{
- ".png": "image/png",
+ ".PnG": "image/png",
}
}
diff --git a/src/pkg/net/dnsclient_unix.go b/src/pkg/net/dnsclient_unix.go
index eb4b5900de..abe7da05cb 100644
--- a/src/pkg/net/dnsclient_unix.go
+++ b/src/pkg/net/dnsclient_unix.go
@@ -169,33 +169,20 @@ func tryOneName(cfg *dnsConfig, name string, qtype uint16) (string, []dnsRR, err
}
timeout := time.Duration(cfg.timeout) * time.Second
var lastErr error
- for _, server := range cfg.servers {
- server += ":53"
- lastErr = &DNSError{
- Err: "no answer from DNS server",
- Name: name,
- Server: server,
- IsTimeout: true,
- }
- for i := 0; i < cfg.attempts; i++ {
+ for i := 0; i < cfg.attempts; i++ {
+ for _, server := range cfg.servers {
+ server = JoinHostPort(server, "53")
msg, err := exchange(server, name, qtype, timeout)
if err != nil {
- if nerr, ok := err.(Error); ok && nerr.Timeout() {
- lastErr = &DNSError{
- Err: nerr.Error(),
- Name: name,
- Server: server,
- IsTimeout: true,
- }
- continue
-
- }
lastErr = &DNSError{
Err: err.Error(),
Name: name,
Server: server,
}
- break
+ if nerr, ok := err.(Error); ok && nerr.Timeout() {
+ lastErr.(*DNSError).IsTimeout = true
+ }
+ continue
}
cname, addrs, err := answer(name, server, msg, qtype)
if err == nil || err.(*DNSError).Err == noSuchHost {
@@ -310,13 +297,10 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error)
}
// Can try as ordinary name.
cname, addrs, err = tryOneName(cfg.dnsConfig, rname, qtype)
- if err == nil {
+ if rooted || err == nil {
return
}
}
- if rooted {
- return
- }
// Otherwise, try suffixes.
for i := 0; i < len(cfg.dnsConfig.search); i++ {
@@ -330,15 +314,15 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error)
}
}
- // Last ditch effort: try unsuffixed.
- rname := name
- if !rooted {
- rname += "."
- }
- cname, addrs, err = tryOneName(cfg.dnsConfig, rname, qtype)
- if err == nil {
- return
+ // Last ditch effort: try unsuffixed only if we haven't already,
+ // that is, name is not rooted and has less than ndots dots.
+ if count(name, '.') < cfg.dnsConfig.ndots {
+ cname, addrs, err = tryOneName(cfg.dnsConfig, name+".", qtype)
+ if err == nil {
+ return
+ }
}
+
if e, ok := err.(*DNSError); ok {
// Show original name passed to lookup, not suffixed one.
// In general we might have tried many suffixes; showing
@@ -390,31 +374,36 @@ func goLookupIP(name string) (addrs []IP, err error) {
return
}
}
- var records []dnsRR
- var cname string
- var err4, err6 error
- cname, records, err4 = lookup(name, dnsTypeA)
- addrs = convertRR_A(records)
- if cname != "" {
- name = cname
- }
- _, records, err6 = lookup(name, dnsTypeAAAA)
- if err4 != nil && err6 == nil {
- // Ignore A error because AAAA lookup succeeded.
- err4 = nil
+ type racer struct {
+ qtype uint16
+ rrs []dnsRR
+ error
}
- if err6 != nil && len(addrs) > 0 {
- // Ignore AAAA error because A lookup succeeded.
- err6 = nil
+ lane := make(chan racer, 1)
+ qtypes := [...]uint16{dnsTypeA, dnsTypeAAAA}
+ for _, qtype := range qtypes {
+ go func(qtype uint16) {
+ _, rrs, err := lookup(name, qtype)
+ lane <- racer{qtype, rrs, err}
+ }(qtype)
}
- if err4 != nil {
- return nil, err4
+ var lastErr error
+ for range qtypes {
+ racer := <-lane
+ if racer.error != nil {
+ lastErr = racer.error
+ continue
+ }
+ switch racer.qtype {
+ case dnsTypeA:
+ addrs = append(addrs, convertRR_A(racer.rrs)...)
+ case dnsTypeAAAA:
+ addrs = append(addrs, convertRR_AAAA(racer.rrs)...)
+ }
}
- if err6 != nil {
- return nil, err6
+ if len(addrs) == 0 && lastErr != nil {
+ return nil, lastErr
}
-
- addrs = append(addrs, convertRR_AAAA(records)...)
return addrs, nil
}
diff --git a/src/pkg/net/dnsclient_unix_test.go b/src/pkg/net/dnsclient_unix_test.go
index 39d82d9961..1167c26b39 100644
--- a/src/pkg/net/dnsclient_unix_test.go
+++ b/src/pkg/net/dnsclient_unix_test.go
@@ -204,7 +204,7 @@ func TestReloadResolvConfChange(t *testing.T) {
if _, err := goLookupIP("golang.org"); err != nil {
t.Fatalf("goLookupIP(good) failed: %v", err)
}
- r.WantServers([]string{"[8.8.8.8]"})
+ r.WantServers([]string{"8.8.8.8"})
// Using a bad resolv.conf when we had a good one
// before should not update the config
@@ -215,5 +215,32 @@ func TestReloadResolvConfChange(t *testing.T) {
// A new good config should get picked up
r.SetConf("nameserver 8.8.4.4")
- r.WantServers([]string{"[8.8.4.4]"})
+ r.WantServers([]string{"8.8.4.4"})
+}
+
+func BenchmarkGoLookupIP(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ goLookupIP("www.example.com")
+ }
+}
+
+func BenchmarkGoLookupIPNoSuchHost(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ goLookupIP("some.nonexistent")
+ }
+}
+
+func BenchmarkGoLookupIPWithBrokenNameServer(b *testing.B) {
+ onceLoadConfig.Do(loadDefaultConfig)
+ if cfg.dnserr != nil || cfg.dnsConfig == nil {
+ b.Fatalf("loadConfig failed: %v", cfg.dnserr)
+ }
+ // This looks ugly but it's safe as long as benchmarks are run
+ // sequentially in package testing.
+ orig := cfg.dnsConfig
+ cfg.dnsConfig.servers = append([]string{"203.0.113.254"}, cfg.dnsConfig.servers...) // use TEST-NET-3 block, see RFC 5737
+ for i := 0; i < b.N; i++ {
+ goLookupIP("www.example.com")
+ }
+ cfg.dnsConfig = orig
}
diff --git a/src/pkg/net/dnsconfig_unix.go b/src/pkg/net/dnsconfig_unix.go
index db45716f12..ebb6e673f1 100644
--- a/src/pkg/net/dnsconfig_unix.go
+++ b/src/pkg/net/dnsconfig_unix.go
@@ -25,13 +25,12 @@ func dnsReadConfig(filename string) (*dnsConfig, error) {
if err != nil {
return nil, &DNSConfigError{err}
}
- conf := new(dnsConfig)
- conf.servers = make([]string, 0, 3) // small, but the standard limit
- conf.search = make([]string, 0)
- conf.ndots = 1
- conf.timeout = 5
- conf.attempts = 2
- conf.rotate = false
+ defer file.close()
+ conf := &dnsConfig{
+ ndots: 1,
+ timeout: 5,
+ attempts: 2,
+ }
for line, ok := file.readLine(); ok; line, ok = file.readLine() {
f := getFields(line)
if len(f) < 1 {
@@ -39,30 +38,20 @@ func dnsReadConfig(filename string) (*dnsConfig, error) {
}
switch f[0] {
case "nameserver": // add one name server
- a := conf.servers
- n := len(a)
- if len(f) > 1 && n < cap(a) {
+ if len(f) > 1 && len(conf.servers) < 3 { // small, but the standard limit
// One more check: make sure server name is
// just an IP address. Otherwise we need DNS
// to look it up.
- name := f[1]
- switch len(ParseIP(name)) {
- case 16:
- name = "[" + name + "]"
- fallthrough
- case 4:
- a = a[0 : n+1]
- a[n] = name
- conf.servers = a
+ if parseIPv4(f[1]) != nil {
+ conf.servers = append(conf.servers, f[1])
+ } else if ip, _ := parseIPv6(f[1], true); ip != nil {
+ conf.servers = append(conf.servers, f[1])
}
}
case "domain": // set search path to just this domain
if len(f) > 1 {
- conf.search = make([]string, 1)
- conf.search[0] = f[1]
- } else {
- conf.search = make([]string, 0)
+ conf.search = []string{f[1]}
}
case "search": // set search path to given servers
@@ -99,8 +88,6 @@ func dnsReadConfig(filename string) (*dnsConfig, error) {
}
}
}
- file.close()
-
return conf, nil
}
diff --git a/src/pkg/net/dnsconfig_unix_test.go b/src/pkg/net/dnsconfig_unix_test.go
index 37ed4931db..94fb0c32e2 100644
--- a/src/pkg/net/dnsconfig_unix_test.go
+++ b/src/pkg/net/dnsconfig_unix_test.go
@@ -6,41 +6,64 @@
package net
-import "testing"
+import (
+ "reflect"
+ "testing"
+)
-func TestDNSReadConfig(t *testing.T) {
- dnsConfig, err := dnsReadConfig("testdata/resolv.conf")
- if err != nil {
- t.Fatal(err)
- }
-
- if len(dnsConfig.servers) != 1 {
- t.Errorf("len(dnsConfig.servers) = %d; want %d", len(dnsConfig.servers), 1)
- }
- if dnsConfig.servers[0] != "[192.168.1.1]" {
- t.Errorf("dnsConfig.servers[0] = %s; want %s", dnsConfig.servers[0], "[192.168.1.1]")
- }
-
- if len(dnsConfig.search) != 1 {
- t.Errorf("len(dnsConfig.search) = %d; want %d", len(dnsConfig.search), 1)
- }
- if dnsConfig.search[0] != "Home" {
- t.Errorf("dnsConfig.search[0] = %s; want %s", dnsConfig.search[0], "Home")
- }
-
- if dnsConfig.ndots != 5 {
- t.Errorf("dnsConfig.ndots = %d; want %d", dnsConfig.ndots, 5)
- }
-
- if dnsConfig.timeout != 10 {
- t.Errorf("dnsConfig.timeout = %d; want %d", dnsConfig.timeout, 10)
- }
-
- if dnsConfig.attempts != 3 {
- t.Errorf("dnsConfig.attempts = %d; want %d", dnsConfig.attempts, 3)
- }
+var dnsReadConfigTests = []struct {
+ name string
+ conf dnsConfig
+}{
+ {
+ name: "testdata/resolv.conf",
+ conf: dnsConfig{
+ servers: []string{"8.8.8.8", "2001:4860:4860::8888", "fe80::1%lo0"},
+ search: []string{"localdomain"},
+ ndots: 5,
+ timeout: 10,
+ attempts: 3,
+ rotate: true,
+ },
+ },
+ {
+ name: "testdata/domain-resolv.conf",
+ conf: dnsConfig{
+ servers: []string{"8.8.8.8"},
+ search: []string{"localdomain"},
+ ndots: 1,
+ timeout: 5,
+ attempts: 2,
+ },
+ },
+ {
+ name: "testdata/search-resolv.conf",
+ conf: dnsConfig{
+ servers: []string{"8.8.8.8"},
+ search: []string{"test", "invalid"},
+ ndots: 1,
+ timeout: 5,
+ attempts: 2,
+ },
+ },
+ {
+ name: "testdata/empty-resolv.conf",
+ conf: dnsConfig{
+ ndots: 1,
+ timeout: 5,
+ attempts: 2,
+ },
+ },
+}
- if dnsConfig.rotate != true {
- t.Errorf("dnsConfig.rotate = %t; want %t", dnsConfig.rotate, true)
+func TestDNSReadConfig(t *testing.T) {
+ for _, tt := range dnsReadConfigTests {
+ conf, err := dnsReadConfig(tt.name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(conf, &tt.conf) {
+ t.Errorf("got %v; want %v", conf, &tt.conf)
+ }
}
}
diff --git a/src/pkg/net/http/export_test.go b/src/pkg/net/http/export_test.go
index 960563b240..2c87353554 100644
--- a/src/pkg/net/http/export_test.go
+++ b/src/pkg/net/http/export_test.go
@@ -70,3 +70,9 @@ func ResetCachedEnvironment() {
}
var DefaultUserAgent = defaultUserAgent
+
+// SetPendingDialHooks sets the hooks that run before and after handling
+// pending dials.
+func SetPendingDialHooks(before, after func()) {
+ prePendingDial, postPendingDial = before, after
+}
diff --git a/src/pkg/net/http/fs.go b/src/pkg/net/http/fs.go
index 146b0026b9..bae902cd29 100644
--- a/src/pkg/net/http/fs.go
+++ b/src/pkg/net/http/fs.go
@@ -403,7 +403,7 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec
return
}
- // serverContent will check modification time
+ // serveContent will check modification time
sizeFunc := func() (int64, error) { return d.Size(), nil }
serveContent(w, r, d.Name(), d.ModTime(), sizeFunc, f)
}
diff --git a/src/pkg/net/http/httptest/server_test.go b/src/pkg/net/http/httptest/server_test.go
index 501cc8a999..500a9f0b80 100644
--- a/src/pkg/net/http/httptest/server_test.go
+++ b/src/pkg/net/http/httptest/server_test.go
@@ -8,7 +8,6 @@ import (
"io/ioutil"
"net/http"
"testing"
- "time"
)
func TestServer(t *testing.T) {
@@ -28,25 +27,3 @@ func TestServer(t *testing.T) {
t.Errorf("got %q, want hello", string(got))
}
}
-
-func TestIssue7264(t *testing.T) {
- for i := 0; i < 1000; i++ {
- func() {
- inHandler := make(chan bool, 1)
- ts := NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- inHandler <- true
- }))
- defer ts.Close()
- tr := &http.Transport{
- ResponseHeaderTimeout: time.Nanosecond,
- }
- defer tr.CloseIdleConnections()
- c := &http.Client{Transport: tr}
- res, err := c.Get(ts.URL)
- <-inHandler
- if err == nil {
- res.Body.Close()
- }
- }()
- }
-}
diff --git a/src/pkg/net/http/httputil/reverseproxy.go b/src/pkg/net/http/httputil/reverseproxy.go
index 48ada5f5fd..ab46370180 100644
--- a/src/pkg/net/http/httputil/reverseproxy.go
+++ b/src/pkg/net/http/httputil/reverseproxy.go
@@ -40,6 +40,12 @@ type ReverseProxy struct {
// response body.
// If zero, no periodic flushing is done.
FlushInterval time.Duration
+
+ // ErrorLog specifies an optional logger for errors
+ // that occur when attempting to proxy the request.
+ // If nil, logging goes to os.Stderr via the log package's
+ // standard logger.
+ ErrorLog *log.Logger
}
func singleJoiningSlash(a, b string) string {
@@ -138,7 +144,7 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
res, err := transport.RoundTrip(outreq)
if err != nil {
- log.Printf("http: proxy error: %v", err)
+ p.logf("http: proxy error: %v", err)
rw.WriteHeader(http.StatusInternalServerError)
return
}
@@ -171,6 +177,14 @@ func (p *ReverseProxy) copyResponse(dst io.Writer, src io.Reader) {
io.Copy(dst, src)
}
+func (p *ReverseProxy) logf(format string, args ...interface{}) {
+ if p.ErrorLog != nil {
+ p.ErrorLog.Printf(format, args...)
+ } else {
+ log.Printf(format, args...)
+ }
+}
+
type writeFlusher interface {
io.Writer
http.Flusher
diff --git a/src/pkg/net/http/readrequest_test.go b/src/pkg/net/http/readrequest_test.go
index ffdd6a892d..e930d99af6 100644
--- a/src/pkg/net/http/readrequest_test.go
+++ b/src/pkg/net/http/readrequest_test.go
@@ -11,6 +11,7 @@ import (
"io"
"net/url"
"reflect"
+ "strings"
"testing"
)
@@ -295,14 +296,39 @@ var reqTests = []reqTest{
noTrailer,
noError,
},
+
+ // Connection: close. golang.org/issue/8261
+ {
+ "GET / HTTP/1.1\r\nHost: issue8261.com\r\nConnection: close\r\n\r\n",
+ &Request{
+ Method: "GET",
+ URL: &url.URL{
+ Path: "/",
+ },
+ Header: Header{
+ // This wasn't removed from Go 1.0 to
+ // Go 1.3, so locking it in that we
+ // keep this:
+ "Connection": []string{"close"},
+ },
+ Host: "issue8261.com",
+ Proto: "HTTP/1.1",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Close: true,
+ RequestURI: "/",
+ },
+
+ noBody,
+ noTrailer,
+ noError,
+ },
}
func TestReadRequest(t *testing.T) {
for i := range reqTests {
tt := &reqTests[i]
- var braw bytes.Buffer
- braw.WriteString(tt.Raw)
- req, err := ReadRequest(bufio.NewReader(&braw))
+ req, err := ReadRequest(bufio.NewReader(strings.NewReader(tt.Raw)))
if err != nil {
if err.Error() != tt.Error {
t.Errorf("#%d: error %q, want error %q", i, err.Error(), tt.Error)
@@ -311,21 +337,22 @@ func TestReadRequest(t *testing.T) {
}
rbody := req.Body
req.Body = nil
- diff(t, fmt.Sprintf("#%d Request", i), req, tt.Req)
+ testName := fmt.Sprintf("Test %d (%q)", i, tt.Raw)
+ diff(t, testName, req, tt.Req)
var bout bytes.Buffer
if rbody != nil {
_, err := io.Copy(&bout, rbody)
if err != nil {
- t.Fatalf("#%d. copying body: %v", i, err)
+ t.Fatalf("%s: copying body: %v", testName, err)
}
rbody.Close()
}
body := bout.String()
if body != tt.Body {
- t.Errorf("#%d: Body = %q want %q", i, body, tt.Body)
+ t.Errorf("%s: Body = %q want %q", testName, body, tt.Body)
}
if !reflect.DeepEqual(tt.Trailer, req.Trailer) {
- t.Errorf("#%d. Trailers differ.\n got: %v\nwant: %v", i, req.Trailer, tt.Trailer)
+ t.Errorf("%s: Trailers differ.\n got: %v\nwant: %v", testName, req.Trailer, tt.Trailer)
}
}
}
diff --git a/src/pkg/net/http/request.go b/src/pkg/net/http/request.go
index 131cb6d67e..263c26c9bd 100644
--- a/src/pkg/net/http/request.go
+++ b/src/pkg/net/http/request.go
@@ -10,6 +10,7 @@ import (
"bufio"
"bytes"
"crypto/tls"
+ "encoding/base64"
"errors"
"fmt"
"io"
@@ -521,6 +522,35 @@ func NewRequest(method, urlStr string, body io.Reader) (*Request, error) {
return req, nil
}
+// BasicAuth returns the username and password provided in the request's
+// Authorization header, if the request uses HTTP Basic Authentication.
+// See RFC 2617, Section 2.
+func (r *Request) BasicAuth() (username, password string, ok bool) {
+ auth := r.Header.Get("Authorization")
+ if auth == "" {
+ return
+ }
+ return parseBasicAuth(auth)
+}
+
+// parseBasicAuth parses an HTTP Basic Authentication string.
+// "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" returns ("Aladdin", "open sesame", true).
+func parseBasicAuth(auth string) (username, password string, ok bool) {
+ if !strings.HasPrefix(auth, "Basic ") {
+ return
+ }
+ c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(auth, "Basic "))
+ if err != nil {
+ return
+ }
+ cs := string(c)
+ s := strings.IndexByte(cs, ':')
+ if s < 0 {
+ return
+ }
+ return cs[:s], cs[s+1:], true
+}
+
// SetBasicAuth sets the request's Authorization header to use HTTP
// Basic Authentication with the provided username and password.
//
@@ -635,6 +665,7 @@ func ReadRequest(b *bufio.Reader) (req *Request, err error) {
return nil, err
}
+ req.Close = shouldClose(req.ProtoMajor, req.ProtoMinor, req.Header, false)
return req, nil
}
diff --git a/src/pkg/net/http/request_test.go b/src/pkg/net/http/request_test.go
index b9fa3c2bfc..759ea4e8b5 100644
--- a/src/pkg/net/http/request_test.go
+++ b/src/pkg/net/http/request_test.go
@@ -7,6 +7,7 @@ package http_test
import (
"bufio"
"bytes"
+ "encoding/base64"
"fmt"
"io"
"io/ioutil"
@@ -396,6 +397,75 @@ func TestParseHTTPVersion(t *testing.T) {
}
}
+type getBasicAuthTest struct {
+ username, password string
+ ok bool
+}
+
+type parseBasicAuthTest getBasicAuthTest
+
+type basicAuthCredentialsTest struct {
+ username, password string
+}
+
+var getBasicAuthTests = []struct {
+ username, password string
+ ok bool
+}{
+ {"Aladdin", "open sesame", true},
+ {"Aladdin", "open:sesame", true},
+ {"", "", true},
+}
+
+func TestGetBasicAuth(t *testing.T) {
+ for _, tt := range getBasicAuthTests {
+ r, _ := NewRequest("GET", "http://example.com/", nil)
+ r.SetBasicAuth(tt.username, tt.password)
+ username, password, ok := r.BasicAuth()
+ if ok != tt.ok || username != tt.username || password != tt.password {
+ t.Errorf("BasicAuth() = %#v, want %#v", getBasicAuthTest{username, password, ok},
+ getBasicAuthTest{tt.username, tt.password, tt.ok})
+ }
+ }
+ // Unauthenticated request.
+ r, _ := NewRequest("GET", "http://example.com/", nil)
+ username, password, ok := r.BasicAuth()
+ if ok {
+ t.Errorf("expected false from BasicAuth when the request is unauthenticated")
+ }
+ want := basicAuthCredentialsTest{"", ""}
+ if username != want.username || password != want.password {
+ t.Errorf("expected credentials: %#v when the request is unauthenticated, got %#v",
+ want, basicAuthCredentialsTest{username, password})
+ }
+}
+
+var parseBasicAuthTests = []struct {
+ header, username, password string
+ ok bool
+}{
+ {"Basic " + base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "Aladdin", "open sesame", true},
+ {"Basic " + base64.StdEncoding.EncodeToString([]byte("Aladdin:open:sesame")), "Aladdin", "open:sesame", true},
+ {"Basic " + base64.StdEncoding.EncodeToString([]byte(":")), "", "", true},
+ {"Basic" + base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "", "", false},
+ {base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "", "", false},
+ {"Basic ", "", "", false},
+ {"Basic Aladdin:open sesame", "", "", false},
+ {`Digest username="Aladdin"`, "", "", false},
+}
+
+func TestParseBasicAuth(t *testing.T) {
+ for _, tt := range parseBasicAuthTests {
+ r, _ := NewRequest("GET", "http://example.com/", nil)
+ r.Header.Set("Authorization", tt.header)
+ username, password, ok := r.BasicAuth()
+ if ok != tt.ok || username != tt.username || password != tt.password {
+ t.Errorf("BasicAuth() = %#v, want %#v", getBasicAuthTest{username, password, ok},
+ getBasicAuthTest{tt.username, tt.password, tt.ok})
+ }
+ }
+}
+
type logWrites struct {
t *testing.T
dst *[]string
diff --git a/src/pkg/net/http/requestwrite_test.go b/src/pkg/net/http/requestwrite_test.go
index 997010c2b2..7a6bd58786 100644
--- a/src/pkg/net/http/requestwrite_test.go
+++ b/src/pkg/net/http/requestwrite_test.go
@@ -280,7 +280,7 @@ var reqWriteTests = []reqWriteTest{
ContentLength: 10, // but we're going to send only 5 bytes
},
Body: []byte("12345"),
- WantError: errors.New("http: Request.ContentLength=10 with Body length 5"),
+ WantError: errors.New("http: ContentLength=10 with Body length 5"),
},
// Request with a ContentLength of 4 but an 8 byte body.
@@ -294,7 +294,7 @@ var reqWriteTests = []reqWriteTest{
ContentLength: 4, // but we're going to try to send 8 bytes
},
Body: []byte("12345678"),
- WantError: errors.New("http: Request.ContentLength=4 with Body length 8"),
+ WantError: errors.New("http: ContentLength=4 with Body length 8"),
},
// Request with a 5 ContentLength and nil body.
diff --git a/src/pkg/net/http/serve_test.go b/src/pkg/net/http/serve_test.go
index 2a3fc307be..ee4f204995 100644
--- a/src/pkg/net/http/serve_test.go
+++ b/src/pkg/net/http/serve_test.go
@@ -15,6 +15,7 @@ import (
"io"
"io/ioutil"
"log"
+ "math/rand"
"net"
. "net/http"
"net/http/httptest"
@@ -1188,6 +1189,82 @@ func TestTimeoutHandler(t *testing.T) {
}
}
+// See issues 8209 and 8414.
+func TestTimeoutHandlerRace(t *testing.T) {
+ defer afterTest(t)
+
+ delayHi := HandlerFunc(func(w ResponseWriter, r *Request) {
+ ms, _ := strconv.Atoi(r.URL.Path[1:])
+ if ms == 0 {
+ ms = 1
+ }
+ for i := 0; i < ms; i++ {
+ w.Write([]byte("hi"))
+ time.Sleep(time.Millisecond)
+ }
+ })
+
+ ts := httptest.NewServer(TimeoutHandler(delayHi, 20*time.Millisecond, ""))
+ defer ts.Close()
+
+ var wg sync.WaitGroup
+ gate := make(chan bool, 10)
+ n := 50
+ if testing.Short() {
+ n = 10
+ gate = make(chan bool, 3)
+ }
+ for i := 0; i < n; i++ {
+ gate <- true
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ defer func() { <-gate }()
+ res, err := Get(fmt.Sprintf("%s/%d", ts.URL, rand.Intn(50)))
+ if err == nil {
+ io.Copy(ioutil.Discard, res.Body)
+ res.Body.Close()
+ }
+ }()
+ }
+ wg.Wait()
+}
+
+// See issues 8209 and 8414.
+func TestTimeoutHandlerRaceHeader(t *testing.T) {
+ defer afterTest(t)
+
+ delay204 := HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.WriteHeader(204)
+ })
+
+ ts := httptest.NewServer(TimeoutHandler(delay204, time.Nanosecond, ""))
+ defer ts.Close()
+
+ var wg sync.WaitGroup
+ gate := make(chan bool, 50)
+ n := 500
+ if testing.Short() {
+ n = 10
+ }
+ for i := 0; i < n; i++ {
+ gate <- true
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ defer func() { <-gate }()
+ res, err := Get(ts.URL)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ defer res.Body.Close()
+ io.Copy(ioutil.Discard, res.Body)
+ }()
+ }
+ wg.Wait()
+}
+
// Verifies we don't path.Clean() on the wrong parts in redirects.
func TestRedirectMunging(t *testing.T) {
req, _ := NewRequest("GET", "http://example.com/", nil)
diff --git a/src/pkg/net/http/server.go b/src/pkg/net/http/server.go
index eae097eb8e..203037e9f5 100644
--- a/src/pkg/net/http/server.go
+++ b/src/pkg/net/http/server.go
@@ -1916,9 +1916,9 @@ func (tw *timeoutWriter) Header() Header {
func (tw *timeoutWriter) Write(p []byte) (int, error) {
tw.mu.Lock()
- timedOut := tw.timedOut
- tw.mu.Unlock()
- if timedOut {
+ defer tw.mu.Unlock()
+ tw.wroteHeader = true // implicitly at least
+ if tw.timedOut {
return 0, ErrHandlerTimeout
}
return tw.w.Write(p)
@@ -1926,12 +1926,11 @@ func (tw *timeoutWriter) Write(p []byte) (int, error) {
func (tw *timeoutWriter) WriteHeader(code int) {
tw.mu.Lock()
+ defer tw.mu.Unlock()
if tw.timedOut || tw.wroteHeader {
- tw.mu.Unlock()
return
}
tw.wroteHeader = true
- tw.mu.Unlock()
tw.w.WriteHeader(code)
}
diff --git a/src/pkg/net/http/transfer.go b/src/pkg/net/http/transfer.go
index c9be871595..520500330b 100644
--- a/src/pkg/net/http/transfer.go
+++ b/src/pkg/net/http/transfer.go
@@ -228,7 +228,7 @@ func (t *transferWriter) WriteBody(w io.Writer) error {
}
if !t.ResponseToHEAD && t.ContentLength != -1 && t.ContentLength != ncopy {
- return fmt.Errorf("http: Request.ContentLength=%d with Body length %d",
+ return fmt.Errorf("http: ContentLength=%d with Body length %d",
t.ContentLength, ncopy)
}
@@ -303,7 +303,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
t.StatusCode = rr.StatusCode
t.ProtoMajor = rr.ProtoMajor
t.ProtoMinor = rr.ProtoMinor
- t.Close = shouldClose(t.ProtoMajor, t.ProtoMinor, t.Header)
+ t.Close = shouldClose(t.ProtoMajor, t.ProtoMinor, t.Header, true)
isResponse = true
if rr.Request != nil {
t.RequestMethod = rr.Request.Method
@@ -502,7 +502,7 @@ func fixLength(isResponse bool, status int, requestMethod string, header Header,
// Determine whether to hang up after sending a request and body, or
// receiving a response and body
// 'header' is the request headers
-func shouldClose(major, minor int, header Header) bool {
+func shouldClose(major, minor int, header Header, removeCloseHeader bool) bool {
if major < 1 {
return true
} else if major == 1 && minor == 0 {
@@ -514,7 +514,9 @@ func shouldClose(major, minor int, header Header) bool {
// TODO: Should split on commas, toss surrounding white space,
// and check each field.
if strings.ToLower(header.get("Connection")) == "close" {
- header.Del("Connection")
+ if removeCloseHeader {
+ header.Del("Connection")
+ }
return true
}
}
diff --git a/src/pkg/net/http/transport.go b/src/pkg/net/http/transport.go
index b1cc632a78..527ed8bdd1 100644
--- a/src/pkg/net/http/transport.go
+++ b/src/pkg/net/http/transport.go
@@ -43,8 +43,8 @@ var DefaultTransport RoundTripper = &Transport{
// MaxIdleConnsPerHost.
const DefaultMaxIdleConnsPerHost = 2
-// Transport is an implementation of RoundTripper that supports http,
-// https, and http proxies (for either http or https with CONNECT).
+// Transport is an implementation of RoundTripper that supports HTTP,
+// HTTPS, and HTTP proxies (for either HTTP or HTTPS with CONNECT).
// Transport can also cache connections for future re-use.
type Transport struct {
idleMu sync.Mutex
@@ -61,11 +61,22 @@ type Transport struct {
// If Proxy is nil or returns a nil *URL, no proxy is used.
Proxy func(*Request) (*url.URL, error)
- // Dial specifies the dial function for creating TCP
- // connections.
+ // Dial specifies the dial function for creating unencrypted
+ // TCP connections.
// If Dial is nil, net.Dial is used.
Dial func(network, addr string) (net.Conn, error)
+ // DialTLS specifies an optional dial function for creating
+ // TLS connections for non-proxied HTTPS requests.
+ //
+ // If DialTLS is nil, Dial and TLSClientConfig are used.
+ //
+ // If DialTLS is set, the Dial hook is not used for HTTPS
+ // requests and the TLSClientConfig and TLSHandshakeTimeout
+ // are ignored. The returned net.Conn is assumed to already be
+ // past the TLS handshake.
+ DialTLS func(network, addr string) (net.Conn, error)
+
// TLSClientConfig specifies the TLS configuration to use with
// tls.Client. If nil, the default configuration is used.
TLSClientConfig *tls.Config
@@ -444,6 +455,9 @@ func (t *Transport) dial(network, addr string) (c net.Conn, err error) {
return net.Dial(network, addr)
}
+// Testing hooks:
+var prePendingDial, postPendingDial func()
+
// getConn dials and creates a new persistConn to the target as
// specified in the connectMethod. This includes doing a proxy CONNECT
// and/or setting up TLS. If this doesn't return an error, the persistConn
@@ -460,9 +474,17 @@ func (t *Transport) getConn(req *Request, cm connectMethod) (*persistConn, error
dialc := make(chan dialRes)
handlePendingDial := func() {
- if v := <-dialc; v.err == nil {
- t.putIdleConn(v.pc)
+ if prePendingDial != nil {
+ prePendingDial()
}
+ go func() {
+ if v := <-dialc; v.err == nil {
+ t.putIdleConn(v.pc)
+ }
+ if postPendingDial != nil {
+ postPendingDial()
+ }
+ }()
}
cancelc := make(chan struct{})
@@ -484,53 +506,65 @@ func (t *Transport) getConn(req *Request, cm connectMethod) (*persistConn, error
// else's dial that they didn't use.
// But our dial is still going, so give it away
// when it finishes:
- go handlePendingDial()
+ handlePendingDial()
return pc, nil
case <-cancelc:
- go handlePendingDial()
+ handlePendingDial()
return nil, errors.New("net/http: request canceled while waiting for connection")
}
}
func (t *Transport) dialConn(cm connectMethod) (*persistConn, error) {
- conn, err := t.dial("tcp", cm.addr())
- if err != nil {
- if cm.proxyURL != nil {
- err = fmt.Errorf("http: error connecting to proxy %s: %v", cm.proxyURL, err)
- }
- return nil, err
- }
-
- pa := cm.proxyAuth()
-
pconn := &persistConn{
t: t,
cacheKey: cm.key(),
- conn: conn,
reqch: make(chan requestAndChan, 1),
writech: make(chan writeRequest, 1),
closech: make(chan struct{}),
writeErrCh: make(chan error, 1),
}
+ tlsDial := t.DialTLS != nil && cm.targetScheme == "https" && cm.proxyURL == nil
+ if tlsDial {
+ var err error
+ pconn.conn, err = t.DialTLS("tcp", cm.addr())
+ if err != nil {
+ return nil, err
+ }
+ if tc, ok := pconn.conn.(*tls.Conn); ok {
+ cs := tc.ConnectionState()
+ pconn.tlsState = &cs
+ }
+ } else {
+ conn, err := t.dial("tcp", cm.addr())
+ if err != nil {
+ if cm.proxyURL != nil {
+ err = fmt.Errorf("http: error connecting to proxy %s: %v", cm.proxyURL, err)
+ }
+ return nil, err
+ }
+ pconn.conn = conn
+ }
+ // Proxy setup.
switch {
case cm.proxyURL == nil:
- // Do nothing.
+ // Do nothing. Not using a proxy.
case cm.targetScheme == "http":
pconn.isProxy = true
- if pa != "" {
+ if pa := cm.proxyAuth(); pa != "" {
pconn.mutateHeaderFunc = func(h Header) {
h.Set("Proxy-Authorization", pa)
}
}
case cm.targetScheme == "https":
+ conn := pconn.conn
connectReq := &Request{
Method: "CONNECT",
URL: &url.URL{Opaque: cm.targetAddr},
Host: cm.targetAddr,
Header: make(Header),
}
- if pa != "" {
+ if pa := cm.proxyAuth(); pa != "" {
connectReq.Header.Set("Proxy-Authorization", pa)
}
connectReq.Write(conn)
@@ -551,7 +585,7 @@ func (t *Transport) dialConn(cm connectMethod) (*persistConn, error) {
}
}
- if cm.targetScheme == "https" {
+ if cm.targetScheme == "https" && !tlsDial {
// Initiate TLS and check remote host name against certificate.
cfg := t.TLSClientConfig
if cfg == nil || cfg.ServerName == "" {
@@ -564,7 +598,7 @@ func (t *Transport) dialConn(cm connectMethod) (*persistConn, error) {
cfg = &clone
}
}
- plainConn := conn
+ plainConn := pconn.conn
tlsConn := tls.Client(plainConn, cfg)
errc := make(chan error, 2)
var timer *time.Timer // for canceling TLS handshake
diff --git a/src/pkg/net/http/transport_test.go b/src/pkg/net/http/transport_test.go
index 964ca0fca5..3460d690e3 100644
--- a/src/pkg/net/http/transport_test.go
+++ b/src/pkg/net/http/transport_test.go
@@ -1063,20 +1063,18 @@ func TestTransportConcurrency(t *testing.T) {
var wg sync.WaitGroup
wg.Add(numReqs)
- tr := &Transport{
- Dial: func(netw, addr string) (c net.Conn, err error) {
- // Due to the Transport's "socket late
- // binding" (see idleConnCh in transport.go),
- // the numReqs HTTP requests below can finish
- // with a dial still outstanding. So count
- // our dials as work too so the leak checker
- // doesn't complain at us.
- wg.Add(1)
- defer wg.Done()
- return net.Dial(netw, addr)
- },
- }
+ // Due to the Transport's "socket late binding" (see
+ // idleConnCh in transport.go), the numReqs HTTP requests
+ // below can finish with a dial still outstanding. To keep
+ // the leak checker happy, keep track of pending dials and
+ // wait for them to finish (and be closed or returned to the
+ // idle pool) before we close idle connections.
+ SetPendingDialHooks(func() { wg.Add(1) }, wg.Done)
+ defer SetPendingDialHooks(nil, nil)
+
+ tr := &Transport{}
defer tr.CloseIdleConnections()
+
c := &Client{Transport: tr}
reqs := make(chan string)
defer close(reqs)
@@ -2098,6 +2096,46 @@ func TestTransportClosesBodyOnError(t *testing.T) {
}
}
+func TestTransportDialTLS(t *testing.T) {
+ var mu sync.Mutex // guards following
+ var gotReq, didDial bool
+
+ ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ mu.Lock()
+ gotReq = true
+ mu.Unlock()
+ }))
+ defer ts.Close()
+ tr := &Transport{
+ DialTLS: func(netw, addr string) (net.Conn, error) {
+ mu.Lock()
+ didDial = true
+ mu.Unlock()
+ c, err := tls.Dial(netw, addr, &tls.Config{
+ InsecureSkipVerify: true,
+ })
+ if err != nil {
+ return nil, err
+ }
+ return c, c.Handshake()
+ },
+ }
+ defer tr.CloseIdleConnections()
+ client := &Client{Transport: tr}
+ res, err := client.Get(ts.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ res.Body.Close()
+ mu.Lock()
+ if !gotReq {
+ t.Error("didn't get request")
+ }
+ if !didDial {
+ t.Error("didn't use dial hook")
+ }
+}
+
func wantBody(res *http.Response, err error, want string) error {
if err != nil {
return err
diff --git a/src/pkg/net/lookup_test.go b/src/pkg/net/lookup_test.go
index 3355e46948..057e1322b9 100644
--- a/src/pkg/net/lookup_test.go
+++ b/src/pkg/net/lookup_test.go
@@ -15,87 +15,181 @@ import (
var testExternal = flag.Bool("external", true, "allow use of external networks during long test")
-func TestGoogleSRV(t *testing.T) {
+var lookupGoogleSRVTests = []struct {
+ service, proto, name string
+ cname, target string
+}{
+ {
+ "xmpp-server", "tcp", "google.com",
+ ".google.com", ".google.com",
+ },
+ {
+ "", "", "_xmpp-server._tcp.google.com", // non-standard back door
+ ".google.com", ".google.com",
+ },
+}
+
+func TestLookupGoogleSRV(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("skipping test to avoid external network")
}
- _, addrs, err := LookupSRV("xmpp-server", "tcp", "google.com")
- if err != nil {
- t.Errorf("failed: %s", err)
+
+ for _, tt := range lookupGoogleSRVTests {
+ cname, srvs, err := LookupSRV(tt.service, tt.proto, tt.name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(srvs) == 0 {
+ t.Error("got no record")
+ }
+ if !strings.Contains(cname, tt.cname) {
+ t.Errorf("got %q; want %q", cname, tt.cname)
+ }
+ for _, srv := range srvs {
+ if !strings.Contains(srv.Target, tt.target) {
+ t.Errorf("got %v; want a record containing %q", srv, tt.target)
+ }
+ }
}
- if len(addrs) == 0 {
- t.Errorf("no results")
+}
+
+func TestLookupGmailMX(t *testing.T) {
+ if testing.Short() || !*testExternal {
+ t.Skip("skipping test to avoid external network")
}
- // Non-standard back door.
- _, addrs, err = LookupSRV("", "", "_xmpp-server._tcp.google.com")
+ mxs, err := LookupMX("gmail.com")
if err != nil {
- t.Errorf("back door failed: %s", err)
+ t.Fatal(err)
}
- if len(addrs) == 0 {
- t.Errorf("back door no results")
+ if len(mxs) == 0 {
+ t.Error("got no record")
+ }
+ for _, mx := range mxs {
+ if !strings.Contains(mx.Host, ".google.com") {
+ t.Errorf("got %v; want a record containing .google.com.", mx)
+ }
}
}
-func TestGmailMX(t *testing.T) {
+func TestLookupGmailNS(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("skipping test to avoid external network")
}
- mx, err := LookupMX("gmail.com")
+
+ nss, err := LookupNS("gmail.com")
if err != nil {
- t.Errorf("failed: %s", err)
+ t.Fatal(err)
+ }
+ if len(nss) == 0 {
+ t.Error("got no record")
}
- if len(mx) == 0 {
- t.Errorf("no results")
+ for _, ns := range nss {
+ if !strings.Contains(ns.Host, ".google.com") {
+ t.Errorf("got %v; want a record containing .google.com.", ns)
+ }
}
}
-func TestGmailNS(t *testing.T) {
+func TestLookupGmailTXT(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("skipping test to avoid external network")
}
- ns, err := LookupNS("gmail.com")
+
+ txts, err := LookupTXT("gmail.com")
if err != nil {
- t.Errorf("failed: %s", err)
+ t.Fatal(err)
+ }
+ if len(txts) == 0 {
+ t.Error("got no record")
+ }
+ for _, txt := range txts {
+ if !strings.Contains(txt, "spf") {
+ t.Errorf("got %q; want a spf record", txt)
+ }
+ }
+}
+
+var lookupGooglePublicDNSAddrs = []struct {
+ addr string
+ name string
+}{
+ {"8.8.8.8", ".google.com."},
+ {"8.8.4.4", ".google.com."},
+ {"2001:4860:4860::8888", ".google.com."},
+ {"2001:4860:4860::8844", ".google.com."},
+}
+
+func TestLookupGooglePublicDNSAddr(t *testing.T) {
+ if testing.Short() || !*testExternal {
+ t.Skip("skipping test to avoid external network")
}
- if len(ns) == 0 {
- t.Errorf("no results")
+
+ for _, tt := range lookupGooglePublicDNSAddrs {
+ names, err := LookupAddr(tt.addr)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(names) == 0 {
+ t.Error("got no record")
+ }
+ for _, name := range names {
+ if !strings.HasSuffix(name, tt.name) {
+ t.Errorf("got %q; want a record containing %q", name, tt.name)
+ }
+ }
}
}
-func TestGmailTXT(t *testing.T) {
+func TestLookupIANACNAME(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("skipping test to avoid external network")
}
- txt, err := LookupTXT("gmail.com")
+
+ cname, err := LookupCNAME("www.iana.org")
if err != nil {
- t.Errorf("failed: %s", err)
+ t.Fatal(err)
}
- if len(txt) == 0 || len(txt[0]) == 0 {
- t.Errorf("no results")
+ if !strings.HasSuffix(cname, ".icann.org.") {
+ t.Errorf("got %q; want a record containing .icann.org.", cname)
}
}
-func TestGoogleDNSAddr(t *testing.T) {
+func TestLookupGoogleHost(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("skipping test to avoid external network")
}
- names, err := LookupAddr("8.8.8.8")
+
+ addrs, err := LookupHost("google.com")
if err != nil {
- t.Errorf("failed: %s", err)
+ t.Fatal(err)
+ }
+ if len(addrs) == 0 {
+ t.Error("got no record")
}
- if len(names) == 0 {
- t.Errorf("no results")
+ for _, addr := range addrs {
+ if ParseIP(addr) == nil {
+ t.Errorf("got %q; want a literal ip address", addr)
+ }
}
}
-func TestLookupIANACNAME(t *testing.T) {
+func TestLookupGoogleIP(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("skipping test to avoid external network")
}
- cname, err := LookupCNAME("www.iana.org")
- if !strings.HasSuffix(cname, ".icann.org.") || err != nil {
- t.Errorf(`LookupCNAME("www.iana.org.") = %q, %v, want "*.icann.org.", nil`, cname, err)
+
+ ips, err := LookupIP("google.com")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(ips) == 0 {
+ t.Error("got no record")
+ }
+ for _, ip := range ips {
+ if ip.To4() == nil && ip.To16() == nil {
+ t.Errorf("got %v; want an ip address", ip)
+ }
}
}
diff --git a/src/pkg/net/lookup_windows.go b/src/pkg/net/lookup_windows.go
index 130364231d..6a925b0a7a 100644
--- a/src/pkg/net/lookup_windows.go
+++ b/src/pkg/net/lookup_windows.go
@@ -210,14 +210,21 @@ func lookupCNAME(name string) (cname string, err error) {
defer releaseThread()
var r *syscall.DNSRecord
e := syscall.DnsQuery(name, syscall.DNS_TYPE_CNAME, 0, nil, &r, nil)
+ // windows returns DNS_INFO_NO_RECORDS if there are no CNAME-s
+ if errno, ok := e.(syscall.Errno); ok && errno == syscall.DNS_INFO_NO_RECORDS {
+ // if there are no aliases, the canonical name is the input name
+ if name == "" || name[len(name)-1] != '.' {
+ return name + ".", nil
+ }
+ return name, nil
+ }
if e != nil {
return "", os.NewSyscallError("LookupCNAME", e)
}
defer syscall.DnsRecordListFree(r, 1)
- if r != nil && r.Type == syscall.DNS_TYPE_CNAME {
- v := (*syscall.DNSPTRData)(unsafe.Pointer(&r.Data[0]))
- cname = syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:]) + "."
- }
+
+ resolved := resolveCNAME(syscall.StringToUTF16Ptr(name), r)
+ cname = syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(resolved))[:]) + "."
return
}
@@ -236,8 +243,9 @@ func lookupSRV(service, proto, name string) (cname string, addrs []*SRV, err err
return "", nil, os.NewSyscallError("LookupSRV", e)
}
defer syscall.DnsRecordListFree(r, 1)
+
addrs = make([]*SRV, 0, 10)
- for p := r; p != nil && p.Type == syscall.DNS_TYPE_SRV; p = p.Next {
+ for _, p := range validRecs(r, syscall.DNS_TYPE_SRV, target) {
v := (*syscall.DNSSRVData)(unsafe.Pointer(&p.Data[0]))
addrs = append(addrs, &SRV{syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Target))[:]), v.Port, v.Priority, v.Weight})
}
@@ -254,8 +262,9 @@ func lookupMX(name string) (mx []*MX, err error) {
return nil, os.NewSyscallError("LookupMX", e)
}
defer syscall.DnsRecordListFree(r, 1)
+
mx = make([]*MX, 0, 10)
- for p := r; p != nil && p.Type == syscall.DNS_TYPE_MX; p = p.Next {
+ for _, p := range validRecs(r, syscall.DNS_TYPE_MX, name) {
v := (*syscall.DNSMXData)(unsafe.Pointer(&p.Data[0]))
mx = append(mx, &MX{syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.NameExchange))[:]) + ".", v.Preference})
}
@@ -272,8 +281,9 @@ func lookupNS(name string) (ns []*NS, err error) {
return nil, os.NewSyscallError("LookupNS", e)
}
defer syscall.DnsRecordListFree(r, 1)
+
ns = make([]*NS, 0, 10)
- for p := r; p != nil && p.Type == syscall.DNS_TYPE_NS; p = p.Next {
+ for _, p := range validRecs(r, syscall.DNS_TYPE_NS, name) {
v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0]))
ns = append(ns, &NS{syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:]) + "."})
}
@@ -289,9 +299,10 @@ func lookupTXT(name string) (txt []string, err error) {
return nil, os.NewSyscallError("LookupTXT", e)
}
defer syscall.DnsRecordListFree(r, 1)
+
txt = make([]string, 0, 10)
- if r != nil && r.Type == syscall.DNS_TYPE_TEXT {
- d := (*syscall.DNSTXTData)(unsafe.Pointer(&r.Data[0]))
+ for _, p := range validRecs(r, syscall.DNS_TYPE_TEXT, name) {
+ d := (*syscall.DNSTXTData)(unsafe.Pointer(&p.Data[0]))
for _, v := range (*[1 << 10]*uint16)(unsafe.Pointer(&(d.StringArray[0])))[:d.StringCount] {
s := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(v))[:])
txt = append(txt, s)
@@ -313,10 +324,58 @@ func lookupAddr(addr string) (name []string, err error) {
return nil, os.NewSyscallError("LookupAddr", e)
}
defer syscall.DnsRecordListFree(r, 1)
+
name = make([]string, 0, 10)
- for p := r; p != nil && p.Type == syscall.DNS_TYPE_PTR; p = p.Next {
+ for _, p := range validRecs(r, syscall.DNS_TYPE_PTR, arpa) {
v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0]))
name = append(name, syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:]))
}
return name, nil
}
+
+const dnsSectionMask = 0x0003
+
+// returns only results applicable to name and resolves CNAME entries
+func validRecs(r *syscall.DNSRecord, dnstype uint16, name string) []*syscall.DNSRecord {
+ cname := syscall.StringToUTF16Ptr(name)
+ if dnstype != syscall.DNS_TYPE_CNAME {
+ cname = resolveCNAME(cname, r)
+ }
+ rec := make([]*syscall.DNSRecord, 0, 10)
+ for p := r; p != nil; p = p.Next {
+ if p.Dw&dnsSectionMask != syscall.DnsSectionAnswer {
+ continue
+ }
+ if p.Type != dnstype {
+ continue
+ }
+ if !syscall.DnsNameCompare(cname, p.Name) {
+ continue
+ }
+ rec = append(rec, p)
+ }
+ return rec
+}
+
+// returns the last CNAME in chain
+func resolveCNAME(name *uint16, r *syscall.DNSRecord) *uint16 {
+ // limit cname resolving to 10 in case of a infinite CNAME loop
+Cname:
+ for cnameloop := 0; cnameloop < 10; cnameloop++ {
+ for p := r; p != nil; p = p.Next {
+ if p.Dw&dnsSectionMask != syscall.DnsSectionAnswer {
+ continue
+ }
+ if p.Type != syscall.DNS_TYPE_CNAME {
+ continue
+ }
+ if !syscall.DnsNameCompare(name, p.Name) {
+ continue
+ }
+ name = (*syscall.DNSPTRData)(unsafe.Pointer(&r.Data[0])).Host
+ continue Cname
+ }
+ break
+ }
+ return name
+}
diff --git a/src/pkg/net/lookup_windows_test.go b/src/pkg/net/lookup_windows_test.go
new file mode 100644
index 0000000000..7495b5b578
--- /dev/null
+++ b/src/pkg/net/lookup_windows_test.go
@@ -0,0 +1,243 @@
+// 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 net
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "os/exec"
+ "reflect"
+ "regexp"
+ "sort"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+var nslookupTestServers = []string{"mail.golang.com", "gmail.com"}
+
+func toJson(v interface{}) string {
+ data, _ := json.Marshal(v)
+ return string(data)
+}
+
+func TestLookupMX(t *testing.T) {
+ if testing.Short() || !*testExternal {
+ t.Skip("skipping test to avoid external network")
+ }
+ for _, server := range nslookupTestServers {
+ mx, err := LookupMX(server)
+ if err != nil {
+ t.Errorf("failed %s: %s", server, err)
+ continue
+ }
+ if len(mx) == 0 {
+ t.Errorf("no results")
+ continue
+ }
+ expected, err := nslookupMX(server)
+ if err != nil {
+ t.Logf("skipping failed nslookup %s test: %s", server, err)
+ }
+ sort.Sort(byPrefAndHost(expected))
+ sort.Sort(byPrefAndHost(mx))
+ if !reflect.DeepEqual(expected, mx) {
+ t.Errorf("different results %s:\texp:%v\tgot:%v", server, toJson(expected), toJson(mx))
+ }
+ }
+}
+
+func TestLookupCNAME(t *testing.T) {
+ if testing.Short() || !*testExternal {
+ t.Skip("skipping test to avoid external network")
+ }
+ for _, server := range nslookupTestServers {
+ cname, err := LookupCNAME(server)
+ if err != nil {
+ t.Errorf("failed %s: %s", server, err)
+ continue
+ }
+ if cname == "" {
+ t.Errorf("no result %s", server)
+ }
+ expected, err := nslookupCNAME(server)
+ if err != nil {
+ t.Logf("skipping failed nslookup %s test: %s", server, err)
+ continue
+ }
+ if expected != cname {
+ t.Errorf("different results %s:\texp:%v\tgot:%v", server, expected, cname)
+ }
+ }
+}
+
+func TestLookupNS(t *testing.T) {
+ if testing.Short() || !*testExternal {
+ t.Skip("skipping test to avoid external network")
+ }
+ for _, server := range nslookupTestServers {
+ ns, err := LookupNS(server)
+ if err != nil {
+ t.Errorf("failed %s: %s", server, err)
+ continue
+ }
+ if len(ns) == 0 {
+ t.Errorf("no results")
+ continue
+ }
+ expected, err := nslookupNS(server)
+ if err != nil {
+ t.Logf("skipping failed nslookup %s test: %s", server, err)
+ continue
+ }
+ sort.Sort(byHost(expected))
+ sort.Sort(byHost(ns))
+ if !reflect.DeepEqual(expected, ns) {
+ t.Errorf("different results %s:\texp:%v\tgot:%v", toJson(server), toJson(expected), ns)
+ }
+ }
+}
+
+func TestLookupTXT(t *testing.T) {
+ if testing.Short() || !*testExternal {
+ t.Skip("skipping test to avoid external network")
+ }
+ for _, server := range nslookupTestServers {
+ txt, err := LookupTXT(server)
+ if err != nil {
+ t.Errorf("failed %s: %s", server, err)
+ continue
+ }
+ if len(txt) == 0 {
+ t.Errorf("no results")
+ continue
+ }
+ expected, err := nslookupTXT(server)
+ if err != nil {
+ t.Logf("skipping failed nslookup %s test: %s", server, err)
+ continue
+ }
+ sort.Strings(expected)
+ sort.Strings(txt)
+ if !reflect.DeepEqual(expected, txt) {
+ t.Errorf("different results %s:\texp:%v\tgot:%v", server, toJson(expected), toJson(txt))
+ }
+ }
+}
+
+type byPrefAndHost []*MX
+
+func (s byPrefAndHost) Len() int { return len(s) }
+func (s byPrefAndHost) Less(i, j int) bool {
+ if s[i].Pref != s[j].Pref {
+ return s[i].Pref < s[j].Pref
+ }
+ return s[i].Host < s[j].Host
+}
+func (s byPrefAndHost) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+type byHost []*NS
+
+func (s byHost) Len() int { return len(s) }
+func (s byHost) Less(i, j int) bool { return s[i].Host < s[j].Host }
+func (s byHost) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+func fqdn(s string) string {
+ if len(s) == 0 || s[len(s)-1] != '.' {
+ return s + "."
+ }
+ return s
+}
+
+func nslookup(qtype, name string) (string, error) {
+ var out bytes.Buffer
+ var err bytes.Buffer
+ cmd := exec.Command("nslookup", "-querytype="+qtype, name)
+ cmd.Stdout = &out
+ cmd.Stderr = &err
+ if err := cmd.Run(); err != nil {
+ return "", err
+ }
+ r := strings.Replace(out.String(), "\r\n", "\n", -1)
+ // nslookup stderr output contains also debug information such as
+ // "Non-authoritative answer" and it doesn't return the correct errcode
+ if strings.Contains(err.String(), "can't find") {
+ return r, errors.New(err.String())
+ }
+ return r, nil
+}
+
+func nslookupMX(name string) (mx []*MX, err error) {
+ var r string
+ if r, err = nslookup("mx", name); err != nil {
+ return
+ }
+ mx = make([]*MX, 0, 10)
+ // linux nslookup syntax
+ // golang.org mail exchanger = 2 alt1.aspmx.l.google.com.
+ rx := regexp.MustCompile(`(?m)^([a-z0-9.\-]+)\s+mail exchanger\s*=\s*([0-9]+)\s*([a-z0-9.\-]+)$`)
+ for _, ans := range rx.FindAllStringSubmatch(r, -1) {
+ pref, _ := strconv.Atoi(ans[2])
+ mx = append(mx, &MX{fqdn(ans[3]), uint16(pref)})
+ }
+ // windows nslookup syntax
+ // gmail.com MX preference = 30, mail exchanger = alt3.gmail-smtp-in.l.google.com
+ rx = regexp.MustCompile(`(?m)^([a-z0-9.\-]+)\s+MX preference\s*=\s*([0-9]+)\s*,\s*mail exchanger\s*=\s*([a-z0-9.\-]+)$`)
+ for _, ans := range rx.FindAllStringSubmatch(r, -1) {
+ pref, _ := strconv.Atoi(ans[2])
+ mx = append(mx, &MX{fqdn(ans[3]), uint16(pref)})
+ }
+ return
+}
+
+func nslookupNS(name string) (ns []*NS, err error) {
+ var r string
+ if r, err = nslookup("ns", name); err != nil {
+ return
+ }
+ ns = make([]*NS, 0, 10)
+ // golang.org nameserver = ns1.google.com.
+ rx := regexp.MustCompile(`(?m)^([a-z0-9.\-]+)\s+nameserver\s*=\s*([a-z0-9.\-]+)$`)
+ for _, ans := range rx.FindAllStringSubmatch(r, -1) {
+ ns = append(ns, &NS{fqdn(ans[2])})
+ }
+ return
+}
+
+func nslookupCNAME(name string) (cname string, err error) {
+ var r string
+ if r, err = nslookup("cname", name); err != nil {
+ return
+ }
+ // mail.golang.com canonical name = golang.org.
+ rx := regexp.MustCompile(`(?m)^([a-z0-9.\-]+)\s+canonical name\s*=\s*([a-z0-9.\-]+)$`)
+ // assumes the last CNAME is the correct one
+ last := name
+ for _, ans := range rx.FindAllStringSubmatch(r, -1) {
+ last = ans[2]
+ }
+ return fqdn(last), nil
+}
+
+func nslookupTXT(name string) (txt []string, err error) {
+ var r string
+ if r, err = nslookup("txt", name); err != nil {
+ return
+ }
+ txt = make([]string, 0, 10)
+ // linux
+ // golang.org text = "v=spf1 redirect=_spf.google.com"
+
+ // windows
+ // golang.org text =
+ //
+ // "v=spf1 redirect=_spf.google.com"
+ rx := regexp.MustCompile(`(?m)^([a-z0-9.\-]+)\s+text\s*=\s*"(.*)"$`)
+ for _, ans := range rx.FindAllStringSubmatch(r, -1) {
+ txt = append(txt, ans[2])
+ }
+ return
+}
diff --git a/src/pkg/net/net_windows_test.go b/src/pkg/net/net_windows_test.go
index 2f57745e3c..750a4304b2 100644
--- a/src/pkg/net/net_windows_test.go
+++ b/src/pkg/net/net_windows_test.go
@@ -16,6 +16,8 @@ import (
)
func TestAcceptIgnoreSomeErrors(t *testing.T) {
+ t.Skip("skipping temporarily, see issue 8662")
+
recv := func(ln Listener) (string, error) {
c, err := ln.Accept()
if err != nil {
diff --git a/src/pkg/net/sockopt_bsd.go b/src/pkg/net/sockopt_bsd.go
index 77d51d7376..2d36a55953 100644
--- a/src/pkg/net/sockopt_bsd.go
+++ b/src/pkg/net/sockopt_bsd.go
@@ -17,7 +17,7 @@ func setDefaultSockopts(s, family, sotype int, ipv6only bool) error {
// On DragonFly BSD, we adjust the ephemeral port
// range because unlike other BSD systems its default
// port range doesn't conform to IANA recommendation
- // as described in RFC 6355 and is pretty narrow.
+ // as described in RFC 6056 and is pretty narrow.
switch family {
case syscall.AF_INET:
syscall.SetsockoptInt(s, syscall.IPPROTO_IP, syscall.IP_PORTRANGE, syscall.IP_PORTRANGE_HIGH)
diff --git a/src/pkg/net/testdata/domain-resolv.conf b/src/pkg/net/testdata/domain-resolv.conf
new file mode 100644
index 0000000000..ff269180f4
--- /dev/null
+++ b/src/pkg/net/testdata/domain-resolv.conf
@@ -0,0 +1,5 @@
+# /etc/resolv.conf
+
+search test invalid
+domain localdomain
+nameserver 8.8.8.8
diff --git a/src/pkg/net/testdata/empty-resolv.conf b/src/pkg/net/testdata/empty-resolv.conf
new file mode 100644
index 0000000000..c4b2b57654
--- /dev/null
+++ b/src/pkg/net/testdata/empty-resolv.conf
@@ -0,0 +1 @@
+# /etc/resolv.conf
diff --git a/src/pkg/net/testdata/resolv.conf b/src/pkg/net/testdata/resolv.conf
index 3841bbf904..04e87eed03 100644
--- a/src/pkg/net/testdata/resolv.conf
+++ b/src/pkg/net/testdata/resolv.conf
@@ -1,6 +1,8 @@
# /etc/resolv.conf
-domain Home
-nameserver 192.168.1.1
+domain localdomain
+nameserver 8.8.8.8
+nameserver 2001:4860:4860::8888
+nameserver fe80::1%lo0
options ndots:5 timeout:10 attempts:3 rotate
options attempts 3
diff --git a/src/pkg/net/testdata/search-resolv.conf b/src/pkg/net/testdata/search-resolv.conf
new file mode 100644
index 0000000000..1c846bfaff
--- /dev/null
+++ b/src/pkg/net/testdata/search-resolv.conf
@@ -0,0 +1,5 @@
+# /etc/resolv.conf
+
+domain localdomain
+search test invalid
+nameserver 8.8.8.8
diff --git a/src/pkg/net/url/url.go b/src/pkg/net/url/url.go
index 75f650a275..0b32cd7c8a 100644
--- a/src/pkg/net/url/url.go
+++ b/src/pkg/net/url/url.go
@@ -64,7 +64,6 @@ func (e EscapeError) Error() string {
// Return true if the specified character should be escaped when
// appearing in a URL string, according to RFC 3986.
-// When 'all' is true the full range of reserved characters are matched.
func shouldEscape(c byte, mode encoding) bool {
// §2.3 Unreserved characters (alphanum)
if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' {
@@ -86,10 +85,12 @@ func shouldEscape(c byte, mode encoding) bool {
// last two as well. That leaves only ? to escape.
return c == '?'
- case encodeUserPassword: // §3.2.2
- // The RFC allows ; : & = + $ , in userinfo, so we must escape only @ and /.
- // The parsing of userinfo treats : as special so we must escape that too.
- return c == '@' || c == '/' || c == ':'
+ case encodeUserPassword: // §3.2.1
+ // The RFC allows ';', ':', '&', '=', '+', '$', and ',' in
+ // userinfo, so we must escape only '@', '/', and '?'.
+ // The parsing of userinfo treats ':' as special so we must escape
+ // that too.
+ return c == '@' || c == '/' || c == '?' || c == ':'
case encodeQueryComponent: // §3.4
// The RFC reserves (so we must escape) everything.
diff --git a/src/pkg/net/url/url_test.go b/src/pkg/net/url/url_test.go
index cad758f238..d8b19d805d 100644
--- a/src/pkg/net/url/url_test.go
+++ b/src/pkg/net/url/url_test.go
@@ -279,6 +279,16 @@ var urltests = []URLTest{
},
"a/b/c",
},
+ // escaped '?' in username and password
+ {
+ "http://%3Fam:pa%3Fsword@google.com",
+ &URL{
+ Scheme: "http",
+ User: UserPassword("?am", "pa?sword"),
+ Host: "google.com",
+ },
+ "",
+ },
}
// more useful string for debugging than fmt's struct printer
@@ -903,3 +913,49 @@ func TestParseFailure(t *testing.T) {
t.Errorf(`ParseQuery(%q) returned error %q, want something containing %q"`, url, errStr, "%gh")
}
}
+
+type shouldEscapeTest struct {
+ in byte
+ mode encoding
+ escape bool
+}
+
+var shouldEscapeTests = []shouldEscapeTest{
+ // Unreserved characters (§2.3)
+ {'a', encodePath, false},
+ {'a', encodeUserPassword, false},
+ {'a', encodeQueryComponent, false},
+ {'a', encodeFragment, false},
+ {'z', encodePath, false},
+ {'A', encodePath, false},
+ {'Z', encodePath, false},
+ {'0', encodePath, false},
+ {'9', encodePath, false},
+ {'-', encodePath, false},
+ {'-', encodeUserPassword, false},
+ {'-', encodeQueryComponent, false},
+ {'-', encodeFragment, false},
+ {'.', encodePath, false},
+ {'_', encodePath, false},
+ {'~', encodePath, false},
+
+ // User information (§3.2.1)
+ {':', encodeUserPassword, true},
+ {'/', encodeUserPassword, true},
+ {'?', encodeUserPassword, true},
+ {'@', encodeUserPassword, true},
+ {'$', encodeUserPassword, false},
+ {'&', encodeUserPassword, false},
+ {'+', encodeUserPassword, false},
+ {',', encodeUserPassword, false},
+ {';', encodeUserPassword, false},
+ {'=', encodeUserPassword, false},
+}
+
+func TestShouldEscape(t *testing.T) {
+ for _, tt := range shouldEscapeTests {
+ if shouldEscape(tt.in, tt.mode) != tt.escape {
+ t.Errorf("shouldEscape(%q, %v) returned %v; expected %v", tt.in, tt.mode, !tt.escape, tt.escape)
+ }
+ }
+}
diff --git a/src/pkg/os/file_windows.go b/src/pkg/os/file_windows.go
index d3aa03b2fb..e78d4abf64 100644
--- a/src/pkg/os/file_windows.go
+++ b/src/pkg/os/file_windows.go
@@ -117,8 +117,10 @@ func openDir(name string) (file *File, err error) {
}
d.path = name
if !isAbs(d.path) {
- cwd, _ := Getwd()
- d.path = cwd + `\` + d.path
+ d.path, e = syscall.FullPath(d.path)
+ if e != nil {
+ return nil, e
+ }
}
f := newFile(r, name)
f.dirinfo = d
diff --git a/src/pkg/os/getwd.go b/src/pkg/os/getwd.go
index a72edeaee6..d5da53b34b 100644
--- a/src/pkg/os/getwd.go
+++ b/src/pkg/os/getwd.go
@@ -5,6 +5,7 @@
package os
import (
+ "runtime"
"sync"
"syscall"
)
@@ -23,22 +24,16 @@ var useSyscallwd = func(error) bool { return true }
// reached via multiple paths (due to symbolic links),
// Getwd may return any one of them.
func Getwd() (dir string, err error) {
- // If the operating system provides a Getwd call, use it.
- if syscall.ImplementsGetwd {
- s, e := syscall.Getwd()
- if useSyscallwd(e) {
- return s, NewSyscallError("getwd", e)
- }
+ if runtime.GOOS == "windows" {
+ return syscall.Getwd()
}
- // Otherwise, we're trying to find our way back to ".".
+ // Clumsy but widespread kludge:
+ // if $PWD is set and matches ".", use it.
dot, err := Stat(".")
if err != nil {
return "", err
}
-
- // Clumsy but widespread kludge:
- // if $PWD is set and matches ".", use it.
dir = Getenv("PWD")
if len(dir) > 0 && dir[0] == '/' {
d, err := Stat(dir)
@@ -47,6 +42,15 @@ func Getwd() (dir string, err error) {
}
}
+ // If the operating system provides a Getwd call, use it.
+ // Otherwise, we're trying to find our way back to ".".
+ if syscall.ImplementsGetwd {
+ s, e := syscall.Getwd()
+ if useSyscallwd(e) {
+ return s, NewSyscallError("getwd", e)
+ }
+ }
+
// Apply same kludge but to cached dir instead of $PWD.
getwdCache.Lock()
dir = getwdCache.dir
diff --git a/src/pkg/os/os_test.go b/src/pkg/os/os_test.go
index 2811f29f34..0224c9b01d 100644
--- a/src/pkg/os/os_test.go
+++ b/src/pkg/os/os_test.go
@@ -811,8 +811,8 @@ func TestChdirAndGetwd(t *testing.T) {
t.Fatalf("Open .: %s", err)
}
// These are chosen carefully not to be symlinks on a Mac
- // (unlike, say, /var, /etc, and /tmp).
- dirs := []string{"/", "/usr/bin"}
+ // (unlike, say, /var, /etc), except /tmp, which we handle below.
+ dirs := []string{"/", "/usr/bin", "/tmp"}
// /usr/bin does not usually exist on Plan 9 or Android.
switch runtime.GOOS {
case "android":
@@ -820,6 +820,7 @@ func TestChdirAndGetwd(t *testing.T) {
case "plan9":
dirs = []string{"/", "/usr"}
}
+ oldwd := Getenv("PWD")
for mode := 0; mode < 2; mode++ {
for _, d := range dirs {
if mode == 0 {
@@ -833,7 +834,11 @@ func TestChdirAndGetwd(t *testing.T) {
err = fd1.Chdir()
fd1.Close()
}
+ if d == "/tmp" {
+ Setenv("PWD", "/tmp")
+ }
pwd, err1 := Getwd()
+ Setenv("PWD", oldwd)
err2 := fd.Chdir()
if err2 != nil {
// We changed the current directory and cannot go back.
diff --git a/src/pkg/os/os_windows_test.go b/src/pkg/os/os_windows_test.go
index af7332f0f2..fd96713eac 100644
--- a/src/pkg/os/os_windows_test.go
+++ b/src/pkg/os/os_windows_test.go
@@ -5,6 +5,7 @@ import (
"os"
"path/filepath"
"syscall"
+ "testing"
)
func init() {
@@ -25,3 +26,56 @@ func init() {
supportsSymlinks = false
}
}
+
+func TestSameWindowsFile(t *testing.T) {
+ temp, err := ioutil.TempDir("", "TestSameWindowsFile")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(temp)
+
+ wd, err := os.Getwd()
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = os.Chdir(temp)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.Chdir(wd)
+
+ f, err := os.Create("a")
+ if err != nil {
+ t.Fatal(err)
+ }
+ f.Close()
+
+ ia1, err := os.Stat("a")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ path, err := filepath.Abs("a")
+ if err != nil {
+ t.Fatal(err)
+ }
+ ia2, err := os.Stat(path)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !os.SameFile(ia1, ia2) {
+ t.Errorf("files should be same")
+ }
+
+ p := filepath.VolumeName(path) + filepath.Base(path)
+ if err != nil {
+ t.Fatal(err)
+ }
+ ia3, err := os.Stat(p)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !os.SameFile(ia1, ia3) {
+ t.Errorf("files should be same")
+ }
+}
diff --git a/src/pkg/os/signal/sig.s b/src/pkg/os/signal/sig.s
index c324fcda4e..1826c37fbd 100644
--- a/src/pkg/os/signal/sig.s
+++ b/src/pkg/os/signal/sig.s
@@ -6,7 +6,7 @@
// +build amd64 amd64p32 arm 386 power64 power64le
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
#ifdef GOARCH_arm
#define JMP B
diff --git a/src/pkg/os/stat_windows.go b/src/pkg/os/stat_windows.go
index 3222060448..f396c1db31 100644
--- a/src/pkg/os/stat_windows.go
+++ b/src/pkg/os/stat_windows.go
@@ -87,8 +87,10 @@ func Lstat(name string) (fi FileInfo, err error) {
}
fs.path = name
if !isAbs(fs.path) {
- cwd, _ := Getwd()
- fs.path = cwd + `\` + fs.path
+ fs.path, e = syscall.FullPath(fs.path)
+ if e != nil {
+ return nil, e
+ }
}
return fs, nil
}
diff --git a/src/pkg/path/filepath/path.go b/src/pkg/path/filepath/path.go
index 71603cc594..d37fc9dfc8 100644
--- a/src/pkg/path/filepath/path.go
+++ b/src/pkg/path/filepath/path.go
@@ -231,6 +231,10 @@ func EvalSymlinks(path string) (string, error) {
// working directory to turn it into an absolute path. The absolute
// path name for a given file is not guaranteed to be unique.
func Abs(path string) (string, error) {
+ return abs(path)
+}
+
+func unixAbs(path string) (string, error) {
if IsAbs(path) {
return Clean(path), nil
}
@@ -448,13 +452,6 @@ func Dir(path string) string {
i--
}
dir := Clean(path[len(vol) : i+1])
- last := len(dir) - 1
- if last > 0 && os.IsPathSeparator(dir[last]) {
- dir = dir[:last]
- }
- if dir == "" {
- dir = "."
- }
return vol + dir
}
diff --git a/src/pkg/path/filepath/path_plan9.go b/src/pkg/path/filepath/path_plan9.go
index 12e85aae00..ee8912d58e 100644
--- a/src/pkg/path/filepath/path_plan9.go
+++ b/src/pkg/path/filepath/path_plan9.go
@@ -28,3 +28,7 @@ func splitList(path string) []string {
}
return strings.Split(path, string(ListSeparator))
}
+
+func abs(path string) (string, error) {
+ return unixAbs(path)
+}
diff --git a/src/pkg/path/filepath/path_test.go b/src/pkg/path/filepath/path_test.go
index 17b53bdf92..399284b97d 100644
--- a/src/pkg/path/filepath/path_test.go
+++ b/src/pkg/path/filepath/path_test.go
@@ -628,6 +628,8 @@ var winisabstests = []IsAbsTest{
{`\`, false},
{`\Windows`, false},
{`c:a\b`, false},
+ {`c:\a\b`, true},
+ {`c:/a/b`, true},
{`\\host\share\foo`, true},
{`//host/share/foo/bar`, true},
}
@@ -784,12 +786,6 @@ var absTests = []string{
}
func TestAbs(t *testing.T) {
- oldwd, err := os.Getwd()
- if err != nil {
- t.Fatal("Getwd failed: ", err)
- }
- defer os.Chdir(oldwd)
-
root, err := ioutil.TempDir("", "TestAbs")
if err != nil {
t.Fatal("TempDir failed: ", err)
@@ -813,6 +809,19 @@ func TestAbs(t *testing.T) {
}
}
+ if runtime.GOOS == "windows" {
+ vol := filepath.VolumeName(root)
+ var extra []string
+ for _, path := range absTests {
+ if strings.Index(path, "$") != -1 {
+ continue
+ }
+ path = vol + path
+ extra = append(extra, path)
+ }
+ absTests = append(absTests, extra...)
+ }
+
err = os.Chdir(absTestDirs[0])
if err != nil {
t.Fatal("chdir failed: ", err)
diff --git a/src/pkg/path/filepath/path_unix.go b/src/pkg/path/filepath/path_unix.go
index 7aba0ab5b9..4e7d0d1b42 100644
--- a/src/pkg/path/filepath/path_unix.go
+++ b/src/pkg/path/filepath/path_unix.go
@@ -30,3 +30,7 @@ func splitList(path string) []string {
}
return strings.Split(path, string(ListSeparator))
}
+
+func abs(path string) (string, error) {
+ return unixAbs(path)
+}
diff --git a/src/pkg/path/filepath/path_windows.go b/src/pkg/path/filepath/path_windows.go
index e99997257d..ec50f6b264 100644
--- a/src/pkg/path/filepath/path_windows.go
+++ b/src/pkg/path/filepath/path_windows.go
@@ -6,6 +6,7 @@ package filepath
import (
"strings"
+ "syscall"
)
func isSlash(c uint8) bool {
@@ -103,3 +104,7 @@ func splitList(path string) []string {
return list
}
+
+func abs(path string) (string, error) {
+ return syscall.FullPath(path)
+}
diff --git a/src/pkg/path/path.go b/src/pkg/path/path.go
index bdb85c6b92..98a6d52922 100644
--- a/src/pkg/path/path.go
+++ b/src/pkg/path/path.go
@@ -206,13 +206,5 @@ func IsAbs(path string) bool {
// slash.
func Dir(path string) string {
dir, _ := Split(path)
- dir = Clean(dir)
- last := len(dir) - 1
- if last > 0 && dir[last] == '/' {
- dir = dir[:last]
- }
- if dir == "" {
- dir = "."
- }
- return dir
+ return Clean(dir)
}
diff --git a/src/pkg/reflect/all_test.go b/src/pkg/reflect/all_test.go
index 50d223f923..a6bacf32cc 100644
--- a/src/pkg/reflect/all_test.go
+++ b/src/pkg/reflect/all_test.go
@@ -2499,6 +2499,15 @@ func TestSlice(t *testing.T) {
if vs != s[3:5] {
t.Errorf("s.Slice(3, 5) = %q; expected %q", vs, s[3:5])
}
+
+ rv := ValueOf(&xs).Elem()
+ rv = rv.Slice(3, 4)
+ ptr2 := rv.Pointer()
+ rv = rv.Slice(5, 5)
+ ptr3 := rv.Pointer()
+ if ptr3 != ptr2 {
+ t.Errorf("xs.Slice(3,4).Slice3(5,5).Pointer() = %#x, want %#x", ptr3, ptr2)
+ }
}
func TestSlice3(t *testing.T) {
@@ -2537,6 +2546,15 @@ func TestSlice3(t *testing.T) {
s := "hello world"
rv = ValueOf(&s).Elem()
shouldPanic(func() { rv.Slice3(1, 2, 3) })
+
+ rv = ValueOf(&xs).Elem()
+ rv = rv.Slice3(3, 5, 7)
+ ptr2 := rv.Pointer()
+ rv = rv.Slice3(4, 4, 4)
+ ptr3 := rv.Pointer()
+ if ptr3 != ptr2 {
+ t.Errorf("xs.Slice3(3,5,7).Slice3(4,4,4).Pointer() = %#x, want %#x", ptr3, ptr2)
+ }
}
func TestSetLenCap(t *testing.T) {
@@ -3218,6 +3236,9 @@ func checkSameType(t *testing.T, x, y interface{}) {
}
func TestArrayOf(t *testing.T) {
+ // TODO(rsc): Finish ArrayOf and enable-test.
+ t.Skip("ArrayOf is not finished (and not exported)")
+
// check construction and use of type not in binary
type T int
at := ArrayOf(10, TypeOf(T(1)))
diff --git a/src/pkg/reflect/asm_386.s b/src/pkg/reflect/asm_386.s
index 18b348adc1..c028113a0c 100644
--- a/src/pkg/reflect/asm_386.s
+++ b/src/pkg/reflect/asm_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// makeFuncStub is the code half of the function returned by MakeFunc.
// See the comment on the declaration of makeFuncStub in makefunc.go
@@ -25,24 +25,3 @@ TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$8
MOVL CX, 4(SP)
CALL ·callMethod(SB)
RET
-
-// Stubs to give reflect package access to runtime services
-// TODO: should probably be done another way.
-TEXT ·makemap(SB),NOSPLIT,$0-0
- JMP runtime·reflect_makemap(SB)
-TEXT ·mapaccess(SB),NOSPLIT,$0-0
- JMP runtime·reflect_mapaccess(SB)
-TEXT ·mapassign(SB),NOSPLIT,$0-0
- JMP runtime·reflect_mapassign(SB)
-TEXT ·mapdelete(SB),NOSPLIT,$0-0
- JMP runtime·reflect_mapdelete(SB)
-TEXT ·mapiterinit(SB),NOSPLIT,$0-0
- JMP runtime·reflect_mapiterinit(SB)
-TEXT ·mapiterkey(SB),NOSPLIT,$0-0
- JMP runtime·reflect_mapiterkey(SB)
-TEXT ·mapiternext(SB),NOSPLIT,$0-0
- JMP runtime·reflect_mapiternext(SB)
-TEXT ·maplen(SB),NOSPLIT,$0-0
- JMP runtime·reflect_maplen(SB)
-TEXT ·ismapkey(SB),NOSPLIT,$0-0
- JMP runtime·reflect_ismapkey(SB)
diff --git a/src/pkg/reflect/asm_amd64.s b/src/pkg/reflect/asm_amd64.s
index 9a9eed02aa..b3c54f0482 100644
--- a/src/pkg/reflect/asm_amd64.s
+++ b/src/pkg/reflect/asm_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// makeFuncStub is the code half of the function returned by MakeFunc.
// See the comment on the declaration of makeFuncStub in makefunc.go
@@ -25,24 +25,3 @@ TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$16
MOVQ CX, 8(SP)
CALL ·callMethod(SB)
RET
-
-// Stubs to give reflect package access to runtime services
-// TODO: should probably be done another way.
-TEXT ·makemap(SB),NOSPLIT,$0-0
- JMP runtime·reflect_makemap(SB)
-TEXT ·mapaccess(SB),NOSPLIT,$0-0
- JMP runtime·reflect_mapaccess(SB)
-TEXT ·mapassign(SB),NOSPLIT,$0-0
- JMP runtime·reflect_mapassign(SB)
-TEXT ·mapdelete(SB),NOSPLIT,$0-0
- JMP runtime·reflect_mapdelete(SB)
-TEXT ·mapiterinit(SB),NOSPLIT,$0-0
- JMP runtime·reflect_mapiterinit(SB)
-TEXT ·mapiterkey(SB),NOSPLIT,$0-0
- JMP runtime·reflect_mapiterkey(SB)
-TEXT ·mapiternext(SB),NOSPLIT,$0-0
- JMP runtime·reflect_mapiternext(SB)
-TEXT ·maplen(SB),NOSPLIT,$0-0
- JMP runtime·reflect_maplen(SB)
-TEXT ·ismapkey(SB),NOSPLIT,$0-0
- JMP runtime·reflect_ismapkey(SB)
diff --git a/src/pkg/reflect/asm_amd64p32.s b/src/pkg/reflect/asm_amd64p32.s
index 18b348adc1..c028113a0c 100644
--- a/src/pkg/reflect/asm_amd64p32.s
+++ b/src/pkg/reflect/asm_amd64p32.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// makeFuncStub is the code half of the function returned by MakeFunc.
// See the comment on the declaration of makeFuncStub in makefunc.go
@@ -25,24 +25,3 @@ TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$8
MOVL CX, 4(SP)
CALL ·callMethod(SB)
RET
-
-// Stubs to give reflect package access to runtime services
-// TODO: should probably be done another way.
-TEXT ·makemap(SB),NOSPLIT,$0-0
- JMP runtime·reflect_makemap(SB)
-TEXT ·mapaccess(SB),NOSPLIT,$0-0
- JMP runtime·reflect_mapaccess(SB)
-TEXT ·mapassign(SB),NOSPLIT,$0-0
- JMP runtime·reflect_mapassign(SB)
-TEXT ·mapdelete(SB),NOSPLIT,$0-0
- JMP runtime·reflect_mapdelete(SB)
-TEXT ·mapiterinit(SB),NOSPLIT,$0-0
- JMP runtime·reflect_mapiterinit(SB)
-TEXT ·mapiterkey(SB),NOSPLIT,$0-0
- JMP runtime·reflect_mapiterkey(SB)
-TEXT ·mapiternext(SB),NOSPLIT,$0-0
- JMP runtime·reflect_mapiternext(SB)
-TEXT ·maplen(SB),NOSPLIT,$0-0
- JMP runtime·reflect_maplen(SB)
-TEXT ·ismapkey(SB),NOSPLIT,$0-0
- JMP runtime·reflect_ismapkey(SB)
diff --git a/src/pkg/reflect/asm_arm.s b/src/pkg/reflect/asm_arm.s
index 1db6b9b9d4..6bd5d48ec9 100644
--- a/src/pkg/reflect/asm_arm.s
+++ b/src/pkg/reflect/asm_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// makeFuncStub is jumped to by the code generated by MakeFunc.
// See the comment on the declaration of makeFuncStub in makefunc.go
@@ -25,24 +25,3 @@ TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$8
MOVW R1, 8(R13)
BL ·callMethod(SB)
RET
-
-// Stubs to give reflect package access to runtime services
-// TODO: should probably be done another way.
-TEXT ·makemap(SB),NOSPLIT,$-4-0
- B runtime·reflect_makemap(SB)
-TEXT ·mapaccess(SB),NOSPLIT,$-4-0
- B runtime·reflect_mapaccess(SB)
-TEXT ·mapassign(SB),NOSPLIT,$-4-0
- B runtime·reflect_mapassign(SB)
-TEXT ·mapdelete(SB),NOSPLIT,$-4-0
- B runtime·reflect_mapdelete(SB)
-TEXT ·mapiterinit(SB),NOSPLIT,$-4-0
- B runtime·reflect_mapiterinit(SB)
-TEXT ·mapiterkey(SB),NOSPLIT,$-4-0
- B runtime·reflect_mapiterkey(SB)
-TEXT ·mapiternext(SB),NOSPLIT,$-4-0
- B runtime·reflect_mapiternext(SB)
-TEXT ·maplen(SB),NOSPLIT,$-4-0
- B runtime·reflect_maplen(SB)
-TEXT ·ismapkey(SB),NOSPLIT,$-4-0
- B runtime·reflect_ismapkey(SB)
diff --git a/src/pkg/reflect/asm_power64x.s b/src/pkg/reflect/asm_power64x.s
index 78839075c4..e430cdf04c 100644
--- a/src/pkg/reflect/asm_power64x.s
+++ b/src/pkg/reflect/asm_power64x.s
@@ -4,7 +4,7 @@
// +build power64 power64le
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// makeFuncStub is the code half of the function returned by MakeFunc.
// See the comment on the declaration of makeFuncStub in makefunc.go
@@ -27,24 +27,3 @@ TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$16
MOVD R3, 16(R1)
BL ·callMethod(SB)
RETURN
-
-// Stubs to give reflect package access to runtime services
-// TODO: should probably be done another way.
-TEXT ·makemap(SB),NOSPLIT,$0-0
- BR runtime·reflect_makemap(SB)
-TEXT ·mapaccess(SB),NOSPLIT,$0-0
- BR runtime·reflect_mapaccess(SB)
-TEXT ·mapassign(SB),NOSPLIT,$0-0
- BR runtime·reflect_mapassign(SB)
-TEXT ·mapdelete(SB),NOSPLIT,$0-0
- BR runtime·reflect_mapdelete(SB)
-TEXT ·mapiterinit(SB),NOSPLIT,$0-0
- BR runtime·reflect_mapiterinit(SB)
-TEXT ·mapiterkey(SB),NOSPLIT,$0-0
- BR runtime·reflect_mapiterkey(SB)
-TEXT ·mapiternext(SB),NOSPLIT,$0-0
- BR runtime·reflect_mapiternext(SB)
-TEXT ·maplen(SB),NOSPLIT,$0-0
- BR runtime·reflect_maplen(SB)
-TEXT ·ismapkey(SB),NOSPLIT,$0-0
- BR runtime·reflect_ismapkey(SB)
diff --git a/src/pkg/reflect/type.go b/src/pkg/reflect/type.go
index d7d4974597..47aecd0023 100644
--- a/src/pkg/reflect/type.go
+++ b/src/pkg/reflect/type.go
@@ -383,12 +383,11 @@ type Method struct {
Index int // index for Type.Method
}
-// High bit says whether type has
-// embedded pointers,to help garbage collector.
const (
- kindMask = 0x3f
- kindGCProg = 0x40
- kindNoPointers = 0x80
+ kindDirectIface = 1 << 5
+ kindGCProg = 1 << 6 // Type.gc points to GC program
+ kindNoPointers = 1 << 7
+ kindMask = (1 << 5) - 1
)
func (k Kind) String() string {
@@ -1503,6 +1502,7 @@ func (gc *gcProg) appendProg(t *rtype) {
var prog []byte
if t.kind&kindGCProg != 0 {
// Ensure that the runtime has unrolled GC program.
+ // TODO(rsc): Do not allocate.
unsafe_New(t)
// The program is stored in t.gc[0], skip unroll flag.
prog = (*[1 << 30]byte)(unsafe.Pointer(t.gc[0]))[1:]
@@ -1652,6 +1652,8 @@ func SliceOf(t Type) Type {
//
// TODO(rsc): Unexported for now. Export once the alg field is set correctly
// for the type. This may require significant work.
+//
+// TODO(rsc): TestArrayOf is also disabled. Re-enable.
func arrayOf(count int, elem Type) Type {
typ := elem.(*rtype)
slice := SliceOf(elem)
@@ -1676,6 +1678,7 @@ func arrayOf(count int, elem Type) Type {
prototype := *(**arrayType)(unsafe.Pointer(&iarray))
array := new(arrayType)
*array = *prototype
+ // TODO: Set extra kind bits correctly.
array.string = &s
array.hash = fnv1(typ.hash, '[')
for n := uint32(count); n > 0; n >>= 8 {
@@ -1692,6 +1695,7 @@ func arrayOf(count int, elem Type) Type {
array.fieldAlign = typ.fieldAlign
// TODO: array.alg
// TODO: array.gc
+ // TODO:
array.uncommonType = nil
array.ptrToThis = nil
array.zero = unsafe.Pointer(&make([]byte, array.size)[0])
@@ -1763,7 +1767,7 @@ func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uin
// Reflect uses the "interface" calling convention for
// methods, where receivers take one word of argument
// space no matter how big they actually are.
- if rcvr.size > ptrSize {
+ if !isDirectIface(rcvr) {
// we pass a pointer to the receiver.
gc.append(bitsPointer)
} else if rcvr.pointers() {
@@ -1813,3 +1817,8 @@ func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uin
layoutCache.Unlock()
return x, argSize, retOffset
}
+
+// isDirectIface reports whether t is stored directly in an interface value.
+func isDirectIface(t *rtype) bool {
+ return t.kind&kindDirectIface != 0
+}
diff --git a/src/pkg/reflect/value.go b/src/pkg/reflect/value.go
index 50f5315961..6ffa63e98f 100644
--- a/src/pkg/reflect/value.go
+++ b/src/pkg/reflect/value.go
@@ -82,7 +82,7 @@ type Value struct {
// This repeats typ.Kind() except for method values.
// The remaining 23+ bits give a method number for method values.
// If flag.kind() != Func, code can assume that flagMethod is unset.
- // If typ.size > ptrSize, code can assume that flagIndir is set.
+ // If !isDirectIface(typ), code can assume that flagIndir is set.
flag
// A method value represents a curried method invocation
@@ -128,7 +128,10 @@ func packEface(v Value) interface{} {
e := (*emptyInterface)(unsafe.Pointer(&i))
// First, fill in the data portion of the interface.
switch {
- case t.size > ptrSize:
+ case !isDirectIface(t):
+ if v.flag&flagIndir == 0 {
+ panic("bad indir")
+ }
// Value is indirect, and so is the interface we're making.
ptr := v.ptr
if v.flag&flagAddr != 0 {
@@ -172,7 +175,7 @@ func unpackEface(i interface{}) Value {
return Value{}
}
f := flag(t.Kind()) << flagKindShift
- if t.size > ptrSize {
+ if !isDirectIface(t) {
return Value{t, unsafe.Pointer(e.word), 0, f | flagIndir}
}
if t.pointers() {
@@ -607,8 +610,8 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer) {
off += -off & uintptr(typ.align-1)
addr := unsafe.Pointer(uintptr(ptr) + off)
v := Value{typ, nil, 0, flag(typ.Kind()) << flagKindShift}
- if typ.size > ptrSize {
- // value does not fit in word.
+ if !isDirectIface(typ) {
+ // value cannot be inlined in interface data.
// Must make a copy, because f might keep a reference to it,
// and we cannot let f keep a reference to the stack frame
// after this function returns, not even a read-only reference.
@@ -714,7 +717,7 @@ func storeRcvr(v Value, p unsafe.Pointer) {
iface := (*nonEmptyInterface)(v.ptr)
*(*unsafe.Pointer)(p) = unsafe.Pointer(iface.word)
} else if v.flag&flagIndir != 0 {
- if t.size > ptrSize {
+ if !isDirectIface(t) {
*(*unsafe.Pointer)(p) = v.ptr
} else if t.pointers() {
*(*unsafe.Pointer)(p) = *(*unsafe.Pointer)(v.ptr)
@@ -987,7 +990,13 @@ func (v Value) Index(i int) Value {
val = unsafe.Pointer(uintptr(v.ptr) + offset)
case typ.pointers():
if offset != 0 {
- panic("can't Index(i) with i!=0 on ptrLike value")
+ // This is an array stored inline in an interface value.
+ // And the array element type has pointers.
+ // Since the inline storage space is only a single word,
+ // this implies we must be holding an array of length 1
+ // with an element type that is a single pointer.
+ // If the offset is not 0, something has gone wrong.
+ panic("reflect: internal error: unexpected array index")
}
val = v.ptr
case bigEndian:
@@ -1014,14 +1023,13 @@ func (v Value) Index(i int) Value {
return Value{typ, val, 0, fl}
case String:
- fl := v.flag&flagRO | flag(Uint8<<flagKindShift)
+ fl := v.flag&flagRO | flag(Uint8<<flagKindShift) | flagIndir
s := (*stringHeader)(v.ptr)
if i < 0 || i >= s.Len {
panic("reflect: string index out of range")
}
- b := uintptr(0)
- *(*byte)(unsafe.Pointer(&b)) = *(*byte)(unsafe.Pointer(uintptr(s.Data) + uintptr(i)))
- return Value{uint8Type, nil, b, fl}
+ p := unsafe.Pointer(uintptr(s.Data) + uintptr(i))
+ return Value{uint8Type, p, 0, fl}
}
panic(&ValueError{"reflect.Value.Index", k})
}
@@ -1209,7 +1217,7 @@ func (v Value) MapIndex(key Value) Value {
typ := tt.elem
fl := (v.flag | key.flag) & flagRO
fl |= flag(typ.Kind()) << flagKindShift
- if typ.size > ptrSize {
+ if !isDirectIface(typ) {
// Copy result so future changes to the map
// won't change the underlying value.
c := unsafe_New(typ)
@@ -1249,7 +1257,7 @@ func (v Value) MapKeys() []Value {
// we can do about it.
break
}
- if keyType.size > ptrSize {
+ if !isDirectIface(keyType) {
// Copy result so future changes to the map
// won't change the underlying value.
c := unsafe_New(keyType)
@@ -1448,7 +1456,7 @@ func (v Value) recv(nb bool) (val Value, ok bool) {
t := tt.elem
val = Value{t, nil, 0, flag(t.Kind()) << flagKindShift}
var p unsafe.Pointer
- if t.size > ptrSize {
+ if !isDirectIface(t) {
p = unsafe_New(t)
val.ptr = p
val.flag |= flagIndir
@@ -1738,9 +1746,14 @@ func (v Value) Slice(i, j int) Value {
// Reinterpret as *sliceHeader to edit.
s := (*sliceHeader)(unsafe.Pointer(&x))
- s.Data = unsafe.Pointer(uintptr(base) + uintptr(i)*typ.elem.Size())
s.Len = j - i
s.Cap = cap - i
+ if cap-i > 0 {
+ s.Data = unsafe.Pointer(uintptr(base) + uintptr(i)*typ.elem.Size())
+ } else {
+ // do not advance pointer, to avoid pointing beyond end of slice
+ s.Data = base
+ }
fl := v.flag&flagRO | flagIndir | flag(Slice)<<flagKindShift
return Value{typ.common(), unsafe.Pointer(&x), 0, fl}
@@ -1785,9 +1798,14 @@ func (v Value) Slice3(i, j, k int) Value {
// Reinterpret as *sliceHeader to edit.
s := (*sliceHeader)(unsafe.Pointer(&x))
- s.Data = unsafe.Pointer(uintptr(base) + uintptr(i)*typ.elem.Size())
s.Len = j - i
s.Cap = k - i
+ if k-i > 0 {
+ s.Data = unsafe.Pointer(uintptr(base) + uintptr(i)*typ.elem.Size())
+ } else {
+ // do not advance pointer, to avoid pointing beyond end of slice
+ s.Data = base
+ }
fl := v.flag&flagRO | flagIndir | flag(Slice)<<flagKindShift
return Value{typ.common(), unsafe.Pointer(&x), 0, fl}
@@ -2055,7 +2073,7 @@ func Copy(dst, src Value) int {
}
// A runtimeSelect is a single case passed to rselect.
-// This must match ../runtime/chan.c:/runtimeSelect
+// This must match ../runtime/select.go:/runtimeSelect
type runtimeSelect struct {
dir uintptr // 0, SendDir, or RecvDir
typ *rtype // channel type
@@ -2073,7 +2091,7 @@ func rselect([]runtimeSelect) (chosen int, recvOK bool)
// A SelectDir describes the communication direction of a select case.
type SelectDir int
-// NOTE: These values must match ../runtime/chan.c:/SelectDir.
+// NOTE: These values must match ../runtime/select.go:/selectDir.
const (
_ SelectDir = iota
@@ -2190,7 +2208,7 @@ func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool) {
t := tt.elem
p := runcases[chosen].val
fl := flag(t.Kind()) << flagKindShift
- if t.size > ptrSize {
+ if !isDirectIface(t) {
recv = Value{t, p, 0, fl | flagIndir}
} else if t.pointers() {
recv = Value{t, *(*unsafe.Pointer)(p), 0, fl}
@@ -2291,7 +2309,7 @@ func Zero(typ Type) Value {
}
t := typ.common()
fl := flag(t.Kind()) << flagKindShift
- if t.size <= ptrSize {
+ if isDirectIface(t) {
return Value{t, nil, 0, fl}
}
return Value{t, unsafe_New(typ.(*rtype)), 0, fl | flagIndir}
@@ -2450,10 +2468,18 @@ func convertOp(dst, src *rtype) func(Value, Type) Value {
// where t is a signed or unsigned int type.
func makeInt(f flag, bits uint64, t Type) Value {
typ := t.common()
- if typ.size > ptrSize {
- // Assume ptrSize >= 4, so this must be uint64.
+ if !isDirectIface(typ) {
ptr := unsafe_New(typ)
- *(*uint64)(unsafe.Pointer(ptr)) = bits
+ switch typ.size {
+ case 1:
+ *(*uint8)(unsafe.Pointer(ptr)) = uint8(bits)
+ case 2:
+ *(*uint16)(unsafe.Pointer(ptr)) = uint16(bits)
+ case 4:
+ *(*uint32)(unsafe.Pointer(ptr)) = uint32(bits)
+ case 8:
+ *(*uint64)(unsafe.Pointer(ptr)) = bits
+ }
return Value{typ, ptr, 0, f | flagIndir | flag(typ.Kind())<<flagKindShift}
}
var s uintptr
@@ -2474,10 +2500,14 @@ func makeInt(f flag, bits uint64, t Type) Value {
// where t is a float32 or float64 type.
func makeFloat(f flag, v float64, t Type) Value {
typ := t.common()
- if typ.size > ptrSize {
- // Assume ptrSize >= 4, so this must be float64.
+ if !isDirectIface(typ) {
ptr := unsafe_New(typ)
- *(*float64)(unsafe.Pointer(ptr)) = v
+ switch typ.size {
+ case 4:
+ *(*float32)(unsafe.Pointer(ptr)) = float32(v)
+ case 8:
+ *(*float64)(unsafe.Pointer(ptr)) = v
+ }
return Value{typ, ptr, 0, f | flagIndir | flag(typ.Kind())<<flagKindShift}
}
@@ -2495,7 +2525,7 @@ func makeFloat(f flag, v float64, t Type) Value {
// where t is a complex64 or complex128 type.
func makeComplex(f flag, v complex128, t Type) Value {
typ := t.common()
- if typ.size > ptrSize {
+ if !isDirectIface(typ) {
ptr := unsafe_New(typ)
switch typ.size {
case 8:
@@ -2506,9 +2536,13 @@ func makeComplex(f flag, v complex128, t Type) Value {
return Value{typ, ptr, 0, f | flagIndir | flag(typ.Kind())<<flagKindShift}
}
- // Assume ptrSize <= 8 so this must be complex64.
var s uintptr
- *(*complex64)(unsafe.Pointer(&s)) = complex64(v)
+ switch typ.size {
+ case 8:
+ *(*complex64)(unsafe.Pointer(&s)) = complex64(v)
+ case 16:
+ *(*complex128)(unsafe.Pointer(&s)) = v
+ }
return Value{typ, nil, s, f | flag(typ.Kind())<<flagKindShift}
}
@@ -2665,8 +2699,8 @@ func mapiterinit(t *rtype, m unsafe.Pointer) unsafe.Pointer
func mapiterkey(it unsafe.Pointer) (key unsafe.Pointer)
func mapiternext(it unsafe.Pointer)
func maplen(m unsafe.Pointer) int
-
func call(fn, arg unsafe.Pointer, n uint32, retoffset uint32)
+
func ifaceE2I(t *rtype, src interface{}, dst unsafe.Pointer)
// Dummy annotation marking that the value x escapes,
diff --git a/src/pkg/regexp/onepass.go b/src/pkg/regexp/onepass.go
index 7666a80ee5..e6f4285638 100644
--- a/src/pkg/regexp/onepass.go
+++ b/src/pkg/regexp/onepass.go
@@ -1,4 +1,6 @@
// 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 regexp
@@ -9,9 +11,6 @@ import (
"unicode"
)
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
// "One-pass" regexp execution.
// Some regexps can be analyzed to determine that they never need
// backtracking: they are guaranteed to run in one pass over the string
diff --git a/src/pkg/regexp/syntax/parse.go b/src/pkg/regexp/syntax/parse.go
index cb25dca395..08f8d307ae 100644
--- a/src/pkg/regexp/syntax/parse.go
+++ b/src/pkg/regexp/syntax/parse.go
@@ -1639,7 +1639,7 @@ const (
// minimum and maximum runes involved in folding.
// checked during test.
minFold = 0x0041
- maxFold = 0x1044f
+ maxFold = 0x118df
)
// appendFoldedRange returns the result of appending the range lo-hi
diff --git a/src/pkg/runtime/alg.go b/src/pkg/runtime/alg.go
index 000d4a18b7..e9ed59503f 100644
--- a/src/pkg/runtime/alg.go
+++ b/src/pkg/runtime/alg.go
@@ -7,10 +7,11 @@ package runtime
import "unsafe"
const (
- c0 = uintptr((8-uint64(ptrSize))/4*2860486313 + (uint64(ptrSize)-4)/4*33054211828000289)
- c1 = uintptr((8-uint64(ptrSize))/4*3267000013 + (uint64(ptrSize)-4)/4*23344194077549503)
+ c0 = uintptr((8-ptrSize)/4*2860486313 + (ptrSize-4)/4*33054211828000289)
+ c1 = uintptr((8-ptrSize)/4*3267000013 + (ptrSize-4)/4*23344194077549503)
)
+// type algorithms - known to compiler
const (
alg_MEM = iota
alg_MEM0
@@ -37,15 +38,52 @@ const (
alg_max
)
+type typeAlg struct {
+ // function for hashing objects of this type
+ // (ptr to object, size, seed) -> hash
+ hash func(unsafe.Pointer, uintptr, uintptr) uintptr
+ // function for comparing objects of this type
+ // (ptr to object A, ptr to object B, size) -> ==?
+ equal func(unsafe.Pointer, unsafe.Pointer, uintptr) bool
+}
+
+var algarray = [alg_max]typeAlg{
+ alg_MEM: {memhash, memequal},
+ alg_MEM0: {memhash, memequal0},
+ alg_MEM8: {memhash, memequal8},
+ alg_MEM16: {memhash, memequal16},
+ alg_MEM32: {memhash, memequal32},
+ alg_MEM64: {memhash, memequal64},
+ alg_MEM128: {memhash, memequal128},
+ alg_NOEQ: {nil, nil},
+ alg_NOEQ0: {nil, nil},
+ alg_NOEQ8: {nil, nil},
+ alg_NOEQ16: {nil, nil},
+ alg_NOEQ32: {nil, nil},
+ alg_NOEQ64: {nil, nil},
+ alg_NOEQ128: {nil, nil},
+ alg_STRING: {strhash, strequal},
+ alg_INTER: {interhash, interequal},
+ alg_NILINTER: {nilinterhash, nilinterequal},
+ alg_SLICE: {nil, nil},
+ alg_FLOAT32: {f32hash, f32equal},
+ alg_FLOAT64: {f64hash, f64equal},
+ alg_CPLX64: {c64hash, c64equal},
+ alg_CPLX128: {c128hash, c128equal},
+}
+
const nacl = GOOS == "nacl"
-var use_aeshash bool
+var useAeshash bool
// in asm_*.s
-func aeshash(p unsafe.Pointer, s uintptr, h uintptr) uintptr
+func aeshash(p unsafe.Pointer, s, h uintptr) uintptr
+func aeshash32(p unsafe.Pointer, s, h uintptr) uintptr
+func aeshash64(p unsafe.Pointer, s, h uintptr) uintptr
+func aeshashstr(p unsafe.Pointer, s, h uintptr) uintptr
-func memhash(p unsafe.Pointer, s uintptr, h uintptr) uintptr {
- if !nacl && use_aeshash {
+func memhash(p unsafe.Pointer, s, h uintptr) uintptr {
+ if !nacl && useAeshash {
return aeshash(p, s, h)
}
@@ -58,8 +96,8 @@ func memhash(p unsafe.Pointer, s uintptr, h uintptr) uintptr {
return h
}
-func strhash(a *string, s uintptr, h uintptr) uintptr {
- return memhash((*stringStruct)(unsafe.Pointer(a)).str, uintptr(len(*a)), h)
+func strhash(a unsafe.Pointer, s, h uintptr) uintptr {
+ return memhash((*stringStruct)(a).str, uintptr(len(*(*string)(a))), h)
}
// NOTE: Because NaN != NaN, a map can contain any
@@ -67,89 +105,248 @@ func strhash(a *string, s uintptr, h uintptr) uintptr {
// To avoid long hash chains, we assign a random number
// as the hash value for a NaN.
-func f32hash(a *float32, s uintptr, h uintptr) uintptr {
- f := *a
+func f32hash(p unsafe.Pointer, s, h uintptr) uintptr {
+ f := *(*float32)(p)
switch {
case f == 0:
return c1 * (c0 ^ h) // +0, -0
case f != f:
- return c1 * (c0 ^ h ^ uintptr(fastrand2())) // any kind of NaN
+ return c1 * (c0 ^ h ^ uintptr(fastrand1())) // any kind of NaN
default:
- return c1 * (c0 ^ h ^ uintptr(*(*uint32)(unsafe.Pointer(a))))
+ return memhash(p, 4, h)
}
}
-func f64hash(a *float64, s uintptr, h uintptr) uintptr {
- f := *a
+func f64hash(p unsafe.Pointer, s, h uintptr) uintptr {
+ f := *(*float64)(p)
switch {
case f == 0:
return c1 * (c0 ^ h) // +0, -0
case f != f:
- return c1 * (c0 ^ h ^ uintptr(fastrand2())) // any kind of NaN
- case ptrSize == 4:
- x := (*[2]uintptr)(unsafe.Pointer(a))
- return c1 * (c0 ^ h ^ (x[1] * c1) ^ x[0])
+ return c1 * (c0 ^ h ^ uintptr(fastrand1())) // any kind of NaN
default:
- return c1 * (c0 ^ h ^ *(*uintptr)(unsafe.Pointer(a)))
+ return memhash(p, 8, h)
}
}
-func c64hash(a *complex64, s uintptr, h uintptr) uintptr {
- x := (*[2]float32)(unsafe.Pointer(a))
- return f32hash(&x[1], 4, f32hash(&x[0], 4, h))
+func c64hash(p unsafe.Pointer, s, h uintptr) uintptr {
+ x := (*[2]float32)(p)
+ return f32hash(unsafe.Pointer(&x[1]), 4, f32hash(unsafe.Pointer(&x[0]), 4, h))
}
-func c128hash(a *complex128, s uintptr, h uintptr) uintptr {
- x := (*[2]float64)(unsafe.Pointer(a))
- return f64hash(&x[1], 4, f64hash(&x[0], 4, h))
+func c128hash(p unsafe.Pointer, s, h uintptr) uintptr {
+ x := (*[2]float64)(p)
+ return f64hash(unsafe.Pointer(&x[1]), 8, f64hash(unsafe.Pointer(&x[0]), 8, h))
}
-func nohash(a unsafe.Pointer, s uintptr, h uintptr) uintptr {
- panic(errorString("hash of unhashable type"))
-}
-
-func interhash(a *interface {
- f()
-}, s uintptr, h uintptr) uintptr {
- return 0
+func interhash(p unsafe.Pointer, s, h uintptr) uintptr {
+ a := (*iface)(p)
+ tab := a.tab
+ if tab == nil {
+ return h
+ }
+ t := tab._type
+ fn := goalg(t.alg).hash
+ if fn == nil {
+ panic(errorString("hash of unhashable type " + *t._string))
+ }
+ if isDirectIface(t) {
+ return c1 * fn(unsafe.Pointer(&a.data), uintptr(t.size), h^c0)
+ } else {
+ return c1 * fn(a.data, uintptr(t.size), h^c0)
+ }
}
-func nilinterhash(a *interface{}, s uintptr, h uintptr) uintptr {
- t := (*eface)(unsafe.Pointer(a))._type
+func nilinterhash(p unsafe.Pointer, s, h uintptr) uintptr {
+ a := (*eface)(p)
+ t := a._type
if t == nil {
return h
}
fn := goalg(t.alg).hash
- if **(**uintptr)(unsafe.Pointer(&fn)) == nohashcode {
- // calling nohash will panic too,
- // but we can print a better error.
+ if fn == nil {
panic(errorString("hash of unhashable type " + *t._string))
}
- if uintptr(t.size) <= ptrSize {
- return c1 * fn(unsafe.Pointer(&(*eface)(unsafe.Pointer(a)).data), uintptr(t.size), h^c0)
+ if isDirectIface(t) {
+ return c1 * fn(unsafe.Pointer(&a.data), uintptr(t.size), h^c0)
} else {
- return c1 * fn((*eface)(unsafe.Pointer(a)).data, uintptr(t.size), h^c0)
+ return c1 * fn(a.data, uintptr(t.size), h^c0)
+ }
+}
+
+func memequal(p, q unsafe.Pointer, size uintptr) bool {
+ if p == q {
+ return true
}
+ return memeq(p, q, size)
+}
+
+func memequal0(p, q unsafe.Pointer, size uintptr) bool {
+ return true
+}
+func memequal8(p, q unsafe.Pointer, size uintptr) bool {
+ return *(*int8)(p) == *(*int8)(q)
+}
+func memequal16(p, q unsafe.Pointer, size uintptr) bool {
+ return *(*int16)(p) == *(*int16)(q)
+}
+func memequal32(p, q unsafe.Pointer, size uintptr) bool {
+ return *(*int32)(p) == *(*int32)(q)
+}
+func memequal64(p, q unsafe.Pointer, size uintptr) bool {
+ return *(*int64)(p) == *(*int64)(q)
+}
+func memequal128(p, q unsafe.Pointer, size uintptr) bool {
+ return *(*[2]int64)(p) == *(*[2]int64)(q)
+}
+func f32equal(p, q unsafe.Pointer, size uintptr) bool {
+ return *(*float32)(p) == *(*float32)(q)
+}
+func f64equal(p, q unsafe.Pointer, size uintptr) bool {
+ return *(*float64)(p) == *(*float64)(q)
+}
+func c64equal(p, q unsafe.Pointer, size uintptr) bool {
+ return *(*complex64)(p) == *(*complex64)(q)
+}
+func c128equal(p, q unsafe.Pointer, size uintptr) bool {
+ return *(*complex128)(p) == *(*complex128)(q)
+}
+func strequal(p, q unsafe.Pointer, size uintptr) bool {
+ return *(*string)(p) == *(*string)(q)
+}
+func interequal(p, q unsafe.Pointer, size uintptr) bool {
+ return ifaceeq(*(*interface {
+ f()
+ })(p), *(*interface {
+ f()
+ })(q))
+}
+func nilinterequal(p, q unsafe.Pointer, size uintptr) bool {
+ return efaceeq(*(*interface{})(p), *(*interface{})(q))
+}
+func efaceeq(p, q interface{}) bool {
+ x := (*eface)(unsafe.Pointer(&p))
+ y := (*eface)(unsafe.Pointer(&q))
+ t := x._type
+ if t != y._type {
+ return false
+ }
+ if t == nil {
+ return true
+ }
+ eq := goalg(t.alg).equal
+ if eq == nil {
+ panic(errorString("comparing uncomparable type " + *t._string))
+ }
+ if isDirectIface(t) {
+ return eq(noescape(unsafe.Pointer(&x.data)), noescape(unsafe.Pointer(&y.data)), uintptr(t.size))
+ }
+ return eq(x.data, y.data, uintptr(t.size))
+}
+func ifaceeq(p, q interface {
+ f()
+}) bool {
+ x := (*iface)(unsafe.Pointer(&p))
+ y := (*iface)(unsafe.Pointer(&q))
+ xtab := x.tab
+ if xtab != y.tab {
+ return false
+ }
+ if xtab == nil {
+ return true
+ }
+ t := xtab._type
+ eq := goalg(t.alg).equal
+ if eq == nil {
+ panic(errorString("comparing uncomparable type " + *t._string))
+ }
+ if isDirectIface(t) {
+ return eq(noescape(unsafe.Pointer(&x.data)), noescape(unsafe.Pointer(&y.data)), uintptr(t.size))
+ }
+ return eq(x.data, y.data, uintptr(t.size))
}
// Testing adapters for hash quality tests (see hash_test.go)
func haveGoodHash() bool {
- return use_aeshash
+ return useAeshash
}
func stringHash(s string, seed uintptr) uintptr {
- return goalg(&algarray[alg_STRING]).hash(noescape(unsafe.Pointer(&s)), unsafe.Sizeof(s), seed)
+ return algarray[alg_STRING].hash(noescape(unsafe.Pointer(&s)), unsafe.Sizeof(s), seed)
}
func bytesHash(b []byte, seed uintptr) uintptr {
- // TODO: use sliceStruct
- return goalg(&algarray[alg_MEM]).hash(*(*unsafe.Pointer)(unsafe.Pointer(&b)), uintptr(len(b)), seed)
+ s := (*sliceStruct)(unsafe.Pointer(&b))
+ return algarray[alg_MEM].hash(s.array, uintptr(s.len), seed)
}
func int32Hash(i uint32, seed uintptr) uintptr {
- return goalg(&algarray[alg_MEM32]).hash(noescape(unsafe.Pointer(&i)), 4, seed)
+ return algarray[alg_MEM32].hash(noescape(unsafe.Pointer(&i)), 4, seed)
}
func int64Hash(i uint64, seed uintptr) uintptr {
- return goalg(&algarray[alg_MEM64]).hash(noescape(unsafe.Pointer(&i)), 8, seed)
+ return algarray[alg_MEM64].hash(noescape(unsafe.Pointer(&i)), 8, seed)
+}
+
+func efaceHash(i interface{}, seed uintptr) uintptr {
+ return algarray[alg_NILINTER].hash(noescape(unsafe.Pointer(&i)), unsafe.Sizeof(i), seed)
+}
+
+func ifaceHash(i interface {
+ F()
+}, seed uintptr) uintptr {
+ return algarray[alg_INTER].hash(noescape(unsafe.Pointer(&i)), unsafe.Sizeof(i), seed)
+}
+
+// Testing adapter for memclr
+func memclrBytes(b []byte) {
+ s := (*sliceStruct)(unsafe.Pointer(&b))
+ memclr(s.array, uintptr(s.len))
+}
+
+// TODO(dvyukov): remove when Type is converted to Go and contains *typeAlg.
+func goalg(a unsafe.Pointer) *typeAlg {
+ return (*typeAlg)(a)
+}
+
+// used in asm_{386,amd64}.s
+const hashRandomBytes = 32
+
+var aeskeysched [hashRandomBytes]byte
+
+//go:noescape
+func get_random_data(rnd *unsafe.Pointer, n *int32)
+
+func init() {
+ if theGoos == "nacl" {
+ return
+ }
+
+ // Install aes hash algorithm if we have the instructions we need
+ if (cpuid_ecx&(1<<25)) != 0 && // aes (aesenc)
+ (cpuid_ecx&(1<<9)) != 0 && // sse3 (pshufb)
+ (cpuid_ecx&(1<<19)) != 0 { // sse4.1 (pinsr{d,q})
+ useAeshash = true
+ algarray[alg_MEM].hash = aeshash
+ algarray[alg_MEM8].hash = aeshash
+ algarray[alg_MEM16].hash = aeshash
+ algarray[alg_MEM32].hash = aeshash32
+ algarray[alg_MEM64].hash = aeshash64
+ algarray[alg_MEM128].hash = aeshash
+ algarray[alg_STRING].hash = aeshashstr
+ // Initialize with random data so hash collisions will be hard to engineer.
+ var rnd unsafe.Pointer
+ var n int32
+ get_random_data(&rnd, &n)
+ if n > hashRandomBytes {
+ n = hashRandomBytes
+ }
+ memmove(unsafe.Pointer(&aeskeysched[0]), rnd, uintptr(n))
+ if n < hashRandomBytes {
+ // Not very random, but better than nothing.
+ for t := nanotime(); n < hashRandomBytes; n++ {
+ aeskeysched[n] = byte(t >> uint(8*(n%8)))
+ }
+ }
+ }
}
diff --git a/src/pkg/runtime/alg.goc b/src/pkg/runtime/alg.goc
deleted file mode 100644
index 70c877ebbb..0000000000
--- a/src/pkg/runtime/alg.goc
+++ /dev/null
@@ -1,423 +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
-#include "runtime.h"
-#include "type.h"
-#include "../../cmd/ld/textflag.h"
-
-bool runtime·use_aeshash;
-
-void
-runtime·memequal(bool *eq, uintptr s, void *a, void *b)
-{
- if(a == b) {
- *eq = 1;
- return;
- }
- *eq = runtime·memeq(a, b, s);
-}
-
-void
-runtime·memprint(uintptr s, void *a)
-{
- uint64 v;
-
- v = 0xbadb00b;
- switch(s) {
- case 1:
- v = *(uint8*)a;
- break;
- case 2:
- v = *(uint16*)a;
- break;
- case 4:
- v = *(uint32*)a;
- break;
- case 8:
- v = *(uint64*)a;
- break;
- }
- runtime·printint_c(v);
-}
-
-void
-runtime·memcopy(uintptr s, void *a, void *b)
-{
- if(b == nil) {
- runtime·memclr(a, s);
- return;
- }
- runtime·memmove(a, b, s);
-}
-
-void
-runtime·memequal0(bool *eq, uintptr s, void *a, void *b)
-{
- USED(s);
- USED(a);
- USED(b);
- *eq = true;
-}
-
-void
-runtime·memcopy0(uintptr s, void *a, void *b)
-{
- USED(s);
- USED(a);
- USED(b);
-}
-
-void
-runtime·memequal8(bool *eq, uintptr s, void *a, void *b)
-{
- USED(s);
- *eq = *(uint8*)a == *(uint8*)b;
-}
-
-void
-runtime·memcopy8(uintptr s, void *a, void *b)
-{
- USED(s);
- if(b == nil) {
- *(uint8*)a = 0;
- return;
- }
- *(uint8*)a = *(uint8*)b;
-}
-
-void
-runtime·memequal16(bool *eq, uintptr s, void *a, void *b)
-{
- USED(s);
- *eq = *(uint16*)a == *(uint16*)b;
-}
-
-void
-runtime·memcopy16(uintptr s, void *a, void *b)
-{
- USED(s);
- if(b == nil) {
- *(uint16*)a = 0;
- return;
- }
- *(uint16*)a = *(uint16*)b;
-}
-
-void
-runtime·memequal32(bool *eq, uintptr s, void *a, void *b)
-{
- USED(s);
- *eq = *(uint32*)a == *(uint32*)b;
-}
-
-void
-runtime·memcopy32(uintptr s, void *a, void *b)
-{
- USED(s);
- if(b == nil) {
- *(uint32*)a = 0;
- return;
- }
- *(uint32*)a = *(uint32*)b;
-}
-
-void
-runtime·memequal64(bool *eq, uintptr s, void *a, void *b)
-{
- USED(s);
- *eq = *(uint64*)a == *(uint64*)b;
-}
-
-void
-runtime·memcopy64(uintptr s, void *a, void *b)
-{
- USED(s);
- if(b == nil) {
- *(uint64*)a = 0;
- return;
- }
- *(uint64*)a = *(uint64*)b;
-}
-
-void
-runtime·memequal128(bool *eq, uintptr s, void *a, void *b)
-{
- USED(s);
- *eq = ((uint64*)a)[0] == ((uint64*)b)[0] && ((uint64*)a)[1] == ((uint64*)b)[1];
-}
-
-void
-runtime·memcopy128(uintptr s, void *a, void *b)
-{
- USED(s);
- if(b == nil) {
- ((uint64*)a)[0] = 0;
- ((uint64*)a)[1] = 0;
- return;
- }
- ((uint64*)a)[0] = ((uint64*)b)[0];
- ((uint64*)a)[1] = ((uint64*)b)[1];
-}
-
-void
-runtime·f32equal(bool *eq, uintptr s, void *a, void *b)
-{
- USED(s);
- *eq = *(float32*)a == *(float32*)b;
-}
-
-void
-runtime·f64equal(bool *eq, uintptr s, void *a, void *b)
-{
- USED(s);
- *eq = *(float64*)a == *(float64*)b;
-}
-
-void
-runtime·c64equal(bool *eq, uintptr s, void *a, void *b)
-{
- Complex64 *ca, *cb;
-
- USED(s);
- ca = a;
- cb = b;
- *eq = ca->real == cb->real && ca->imag == cb->imag;
-}
-
-void
-runtime·c128equal(bool *eq, uintptr s, void *a, void *b)
-{
- Complex128 *ca, *cb;
-
- USED(s);
- ca = a;
- cb = b;
- *eq = ca->real == cb->real && ca->imag == cb->imag;
-}
-
-void
-runtime·algslicecopy(uintptr s, void *a, void *b)
-{
- USED(s);
- if(b == nil) {
- ((Slice*)a)->array = 0;
- ((Slice*)a)->len = 0;
- ((Slice*)a)->cap = 0;
- return;
- }
- ((Slice*)a)->array = ((Slice*)b)->array;
- ((Slice*)a)->len = ((Slice*)b)->len;
- ((Slice*)a)->cap = ((Slice*)b)->cap;
-}
-
-void
-runtime·strequal(bool *eq, uintptr s, void *a, void *b)
-{
- intgo alen;
- byte *s1, *s2;
-
- USED(s);
- alen = ((String*)a)->len;
- if(alen != ((String*)b)->len) {
- *eq = false;
- return;
- }
- s1 = ((String*)a)->str;
- s2 = ((String*)b)->str;
- if(s1 == s2) {
- *eq = true;
- return;
- }
- *eq = runtime·memeq(s1, s2, alen);
-}
-
-void
-runtime·strprint(uintptr s, void *a)
-{
- USED(s);
- runtime·printstring_c(*(String*)a);
-}
-
-void
-runtime·strcopy(uintptr s, void *a, void *b)
-{
- USED(s);
- if(b == nil) {
- ((String*)a)->str = 0;
- ((String*)a)->len = 0;
- return;
- }
- ((String*)a)->str = ((String*)b)->str;
- ((String*)a)->len = ((String*)b)->len;
-}
-
-void
-runtime·interprint(uintptr s, void *a)
-{
- USED(s);
- runtime·printiface_c(*(Iface*)a);
-}
-
-void
-runtime·interequal(bool *eq, uintptr s, void *a, void *b)
-{
- USED(s);
- *eq = runtime·ifaceeq_c(*(Iface*)a, *(Iface*)b);
-}
-
-void
-runtime·intercopy(uintptr s, void *a, void *b)
-{
- USED(s);
- if(b == nil) {
- ((Iface*)a)->tab = 0;
- ((Iface*)a)->data = 0;
- return;
- }
- ((Iface*)a)->tab = ((Iface*)b)->tab;
- ((Iface*)a)->data = ((Iface*)b)->data;
-}
-
-void
-runtime·nilinterprint(uintptr s, void *a)
-{
- USED(s);
- runtime·printeface_c(*(Eface*)a);
-}
-
-void
-runtime·nilinterequal(bool *eq, uintptr s, void *a, void *b)
-{
- USED(s);
- *eq = runtime·efaceeq_c(*(Eface*)a, *(Eface*)b);
-}
-
-void
-runtime·nilintercopy(uintptr s, void *a, void *b)
-{
- USED(s);
- if(b == nil) {
- ((Eface*)a)->type = 0;
- ((Eface*)a)->data = 0;
- return;
- }
- ((Eface*)a)->type = ((Eface*)b)->type;
- ((Eface*)a)->data = ((Eface*)b)->data;
-}
-
-extern uintptr runtime·nohashcode;
-
-void
-runtime·noequal(bool *eq, uintptr s, void *a, void *b)
-{
- USED(s);
- USED(a);
- USED(b);
- USED(eq);
- runtime·panicstring("comparing uncomparable types");
-}
-
-static FuncVal memhashfunc = {(void*)runtime·memhash};
-static FuncVal nohashfunc = {(void*)runtime·nohash};
-static FuncVal strhashfunc = {(void*)runtime·strhash};
-static FuncVal interhashfunc = {(void*)runtime·interhash};
-static FuncVal nilinterhashfunc = {(void*)runtime·nilinterhash};
-static FuncVal f32hashfunc = {(void*)runtime·f32hash};
-static FuncVal f64hashfunc = {(void*)runtime·f64hash};
-static FuncVal c64hashfunc = {(void*)runtime·c64hash};
-static FuncVal c128hashfunc = {(void*)runtime·c128hash};
-
-static FuncVal aeshashfunc = {(void*)runtime·aeshash};
-static FuncVal aeshash32func = {(void*)runtime·aeshash32};
-static FuncVal aeshash64func = {(void*)runtime·aeshash64};
-static FuncVal aeshashstrfunc = {(void*)runtime·aeshashstr};
-
-Alg
-runtime·algarray[] =
-{
-[AMEM] { &memhashfunc, runtime·memequal, runtime·memprint, runtime·memcopy },
-[ANOEQ] { &nohashfunc, runtime·noequal, runtime·memprint, runtime·memcopy },
-[ASTRING] { &strhashfunc, runtime·strequal, runtime·strprint, runtime·strcopy },
-[AINTER] { &interhashfunc, runtime·interequal, runtime·interprint, runtime·intercopy },
-[ANILINTER] { &nilinterhashfunc, runtime·nilinterequal, runtime·nilinterprint, runtime·nilintercopy },
-[ASLICE] { &nohashfunc, runtime·noequal, runtime·memprint, runtime·algslicecopy },
-[AFLOAT32] { &f32hashfunc, runtime·f32equal, runtime·memprint, runtime·memcopy },
-[AFLOAT64] { &f64hashfunc, runtime·f64equal, runtime·memprint, runtime·memcopy },
-[ACPLX64] { &c64hashfunc, runtime·c64equal, runtime·memprint, runtime·memcopy },
-[ACPLX128] { &c128hashfunc, runtime·c128equal, runtime·memprint, runtime·memcopy },
-[AMEM0] { &memhashfunc, runtime·memequal0, runtime·memprint, runtime·memcopy0 },
-[AMEM8] { &memhashfunc, runtime·memequal8, runtime·memprint, runtime·memcopy8 },
-[AMEM16] { &memhashfunc, runtime·memequal16, runtime·memprint, runtime·memcopy16 },
-[AMEM32] { &memhashfunc, runtime·memequal32, runtime·memprint, runtime·memcopy32 },
-[AMEM64] { &memhashfunc, runtime·memequal64, runtime·memprint, runtime·memcopy64 },
-[AMEM128] { &memhashfunc, runtime·memequal128, runtime·memprint, runtime·memcopy128 },
-[ANOEQ0] { &nohashfunc, runtime·noequal, runtime·memprint, runtime·memcopy0 },
-[ANOEQ8] { &nohashfunc, runtime·noequal, runtime·memprint, runtime·memcopy8 },
-[ANOEQ16] { &nohashfunc, runtime·noequal, runtime·memprint, runtime·memcopy16 },
-[ANOEQ32] { &nohashfunc, runtime·noequal, runtime·memprint, runtime·memcopy32 },
-[ANOEQ64] { &nohashfunc, runtime·noequal, runtime·memprint, runtime·memcopy64 },
-[ANOEQ128] { &nohashfunc, runtime·noequal, runtime·memprint, runtime·memcopy128 },
-};
-
-// Runtime helpers.
-
-// used in asm_{386,amd64}.s
-#pragma dataflag NOPTR
-byte runtime·aeskeysched[HashRandomBytes];
-
-void
-runtime·hashinit(void)
-{
- runtime·nohashcode = (uintptr)runtime·nohash;
- if(NaCl)
- return;
-
- // Install aes hash algorithm if we have the instructions we need
- if((runtime·cpuid_ecx & (1 << 25)) != 0 && // aes (aesenc)
- (runtime·cpuid_ecx & (1 << 9)) != 0 && // sse3 (pshufb)
- (runtime·cpuid_ecx & (1 << 19)) != 0) { // sse4.1 (pinsr{d,q})
- byte *rnd;
- int32 n;
- runtime·use_aeshash = true;
- runtime·algarray[AMEM].hash = &aeshashfunc;
- runtime·algarray[AMEM8].hash = &aeshashfunc;
- runtime·algarray[AMEM16].hash = &aeshashfunc;
- runtime·algarray[AMEM32].hash = &aeshash32func;
- runtime·algarray[AMEM64].hash = &aeshash64func;
- runtime·algarray[AMEM128].hash = &aeshashfunc;
- runtime·algarray[ASTRING].hash = &aeshashstrfunc;
- // Initialize with random data so hash collisions will be hard to engineer.
- runtime·get_random_data(&rnd, &n);
- if(n > HashRandomBytes)
- n = HashRandomBytes;
- runtime·memmove(runtime·aeskeysched, rnd, n);
- if(n < HashRandomBytes) {
- // Not very random, but better than nothing.
- int64 t = runtime·nanotime();
- while (n < HashRandomBytes) {
- runtime·aeskeysched[n++] = (int8)(t >> (8 * (n % 8)));
- }
- }
- }
-}
-
-// func equal(t *Type, x T, y T) (ret bool)
-#pragma textflag NOSPLIT
-void
-runtime·equal(Type *t, ...)
-{
- byte *x, *y;
- bool *ret;
-
- x = (byte*)ROUND((uintptr)(&t+1), t->align);
- y = x + t->size;
- ret = (bool*)ROUND((uintptr)(y+t->size), Structrnd);
- t->alg->equal(ret, t->size, x, y);
-}
-
-// Testing adapter for memclr
-func memclrBytes(s Slice) {
- runtime·memclr(s.array, s.len);
-}
diff --git a/src/pkg/runtime/arch_386.go b/src/pkg/runtime/arch_386.go
new file mode 100644
index 0000000000..287b67e270
--- /dev/null
+++ b/src/pkg/runtime/arch_386.go
@@ -0,0 +1,12 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime
+
+const (
+ cacheLineSize = 64
+)
+
+type uintreg uint32
+type intptr int32 // TODO(rsc): remove
diff --git a/src/pkg/runtime/arch_amd64.go b/src/pkg/runtime/arch_amd64.go
new file mode 100644
index 0000000000..fe60c70660
--- /dev/null
+++ b/src/pkg/runtime/arch_amd64.go
@@ -0,0 +1,12 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime
+
+const (
+ cacheLineSize = 64
+)
+
+type uintreg uint64
+type intptr int64 // TODO(rsc): remove
diff --git a/src/pkg/runtime/arch_amd64p32.go b/src/pkg/runtime/arch_amd64p32.go
new file mode 100644
index 0000000000..90766b404f
--- /dev/null
+++ b/src/pkg/runtime/arch_amd64p32.go
@@ -0,0 +1,12 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime
+
+const (
+ cacheLineSize = 64
+)
+
+type uintreg uint64
+type intptr int32 // TODO(rsc): remove
diff --git a/src/pkg/runtime/arch_arm.go b/src/pkg/runtime/arch_arm.go
new file mode 100644
index 0000000000..23f2711f6d
--- /dev/null
+++ b/src/pkg/runtime/arch_arm.go
@@ -0,0 +1,12 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime
+
+const (
+ cacheLineSize = 32
+)
+
+type uintreg uint32
+type intptr int32 // TODO(rsc): remove
diff --git a/src/pkg/runtime/arch_arm.h b/src/pkg/runtime/arch_arm.h
index 3868d78623..637a334a0b 100644
--- a/src/pkg/runtime/arch_arm.h
+++ b/src/pkg/runtime/arch_arm.h
@@ -6,7 +6,7 @@ enum {
thechar = '5',
BigEndian = 0,
CacheLineSize = 32,
- RuntimeGogoBytes = 84,
+ RuntimeGogoBytes = 60,
#ifdef GOOS_nacl
PhysPageSize = 65536,
#else
diff --git a/src/pkg/runtime/asm_386.s b/src/pkg/runtime/asm_386.s
index d2c6e30469..25026417bf 100644
--- a/src/pkg/runtime/asm_386.s
+++ b/src/pkg/runtime/asm_386.s
@@ -4,9 +4,9 @@
#include "zasm_GOOS_GOARCH.h"
#include "funcdata.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
-TEXT _rt0_go(SB),NOSPLIT,$0
+TEXT runtime·rt0_go(SB),NOSPLIT,$0
// copy arguments forward on an even stack
MOVL argc+0(FP), AX
MOVL argv+4(FP), BX
@@ -92,7 +92,6 @@ ok:
MOVL AX, 4(SP)
CALL runtime·args(SB)
CALL runtime·osinit(SB)
- CALL runtime·hashinit(SB)
CALL runtime·schedinit(SB)
// create a new goroutine to start program
@@ -134,8 +133,8 @@ TEXT runtime·asminit(SB),NOSPLIT,$0-0
// void gosave(Gobuf*)
// save state in Gobuf; setjmp
TEXT runtime·gosave(SB), NOSPLIT, $0-4
- MOVL 4(SP), AX // gobuf
- LEAL 4(SP), BX // caller's SP
+ MOVL buf+0(FP), AX // gobuf
+ LEAL buf+0(FP), BX // caller's SP
MOVL BX, gobuf_sp(AX)
MOVL 0(SP), BX // caller's PC
MOVL BX, gobuf_pc(AX)
@@ -149,7 +148,7 @@ TEXT runtime·gosave(SB), NOSPLIT, $0-4
// void gogo(Gobuf*)
// restore state from Gobuf; longjmp
TEXT runtime·gogo(SB), NOSPLIT, $0-4
- MOVL 4(SP), BX // gobuf
+ MOVL buf+0(FP), BX // gobuf
MOVL gobuf_g(BX), DX
MOVL 0(DX), CX // make sure g != nil
get_tls(CX)
@@ -163,7 +162,7 @@ TEXT runtime·gogo(SB), NOSPLIT, $0-4
MOVL gobuf_pc(BX), BX
JMP BX
-// void mcall(void (*fn)(G*))
+// func 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.
@@ -174,7 +173,7 @@ TEXT runtime·mcall(SB), NOSPLIT, $0-4
MOVL g(CX), AX // save state in g->sched
MOVL 0(SP), BX // caller's PC
MOVL BX, (g_sched+gobuf_pc)(AX)
- LEAL 4(SP), BX // caller's SP
+ LEAL fn+0(FP), BX // caller's SP
MOVL BX, (g_sched+gobuf_sp)(AX)
MOVL AX, (g_sched+gobuf_g)(AX)
@@ -189,6 +188,8 @@ TEXT runtime·mcall(SB), NOSPLIT, $0-4
MOVL SI, g(CX) // g = m->g0
MOVL (g_sched+gobuf_sp)(SI), SP // sp = m->g0->sched.sp
PUSHL AX
+ MOVL DI, DX
+ MOVL 0(DI), DI
CALL DI
POPL AX
MOVL $runtime·badmcall2(SB), AX
@@ -203,19 +204,27 @@ TEXT runtime·mcall(SB), NOSPLIT, $0-4
TEXT runtime·switchtoM(SB), NOSPLIT, $0-4
RET
-// void onM(void (*fn)())
-// calls fn() on the M stack.
-// switches to the M stack if not already on it, and
-// switches back when fn() returns.
+// func onM(fn func())
TEXT runtime·onM(SB), NOSPLIT, $0-4
MOVL fn+0(FP), DI // DI = fn
get_tls(CX)
MOVL g(CX), AX // AX = g
MOVL g_m(AX), BX // BX = m
+
MOVL m_g0(BX), DX // DX = g0
CMPL AX, DX
JEQ onm
+ MOVL m_curg(BX), BP
+ CMPL AX, BP
+ JEQ oncurg
+
+ // Not g0, not curg. Must be gsignal, but that's not allowed.
+ // Hide call from linker nosplit analysis.
+ MOVL $runtime·badonm(SB), AX
+ CALL AX
+
+oncurg:
// save our state in g->sched. Pretend to
// be switchtoM if the G stack is scanned.
MOVL $runtime·switchtoM(SB), (g_sched+gobuf_pc)(AX)
@@ -224,10 +233,17 @@ TEXT runtime·onM(SB), NOSPLIT, $0-4
// switch to g0
MOVL DX, g(CX)
- MOVL (g_sched+gobuf_sp)(DX), SP
+ MOVL (g_sched+gobuf_sp)(DX), BX
+ // make it look like mstart called onM on g0, to stop traceback
+ SUBL $4, BX
+ MOVL $runtime·mstart(SB), DX
+ MOVL DX, 0(BX)
+ MOVL BX, SP
// call target function
ARGSIZE(0)
+ MOVL DI, DX
+ MOVL 0(DI), DI
CALL DI
// switch back to g
@@ -242,6 +258,8 @@ TEXT runtime·onM(SB), NOSPLIT, $0-4
onm:
// already on m stack, just call directly
+ MOVL DI, DX
+ MOVL 0(DI), DI
CALL DI
RET
@@ -265,6 +283,12 @@ TEXT runtime·morestack(SB),NOSPLIT,$0-0
JNE 2(PC)
INT $3
+ // Cannot grow signal stack.
+ MOVL m_gsignal(BX), SI
+ CMPL g(CX), SI
+ JNE 2(PC)
+ INT $3
+
// frame size in DI
// arg size in AX
// Save in m.
@@ -304,56 +328,8 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT,$0-0
MOVL $0, DX
JMP runtime·morestack(SB)
-// Called from panic. Mimics morestack,
-// reuses stack growth code to create a frame
-// with the desired args running the desired function.
-//
-// func call(fn *byte, arg *byte, argsize uint32).
-TEXT runtime·newstackcall(SB), NOSPLIT, $0-12
- get_tls(CX)
- MOVL g(CX), BX
- MOVL g_m(BX), BX
-
- // Save our caller's state as the PC and SP to
- // restore when returning from f.
- MOVL 0(SP), AX // our caller's PC
- MOVL AX, (m_morebuf+gobuf_pc)(BX)
- LEAL 4(SP), AX // our caller's SP
- MOVL AX, (m_morebuf+gobuf_sp)(BX)
- MOVL g(CX), AX
- MOVL AX, (m_morebuf+gobuf_g)(BX)
-
- // Save our own state as the PC and SP to restore
- // if this goroutine needs to be restarted.
- MOVL $runtime·newstackcall(SB), (g_sched+gobuf_pc)(AX)
- MOVL SP, (g_sched+gobuf_sp)(AX)
-
- // Set up morestack arguments to call f on a new stack.
- // We set f's frame size to 1, as a hint to newstack
- // that this is a call from runtime·newstackcall.
- // If it turns out that f needs a larger frame than
- // the default stack, f's usual stack growth prolog will
- // allocate a new segment (and recopy the arguments).
- MOVL 4(SP), AX // fn
- MOVL 8(SP), DX // arg frame
- MOVL 12(SP), CX // arg size
-
- MOVL AX, m_cret(BX) // f's PC
- MOVL DX, m_moreargp(BX) // f's argument pointer
- MOVL CX, m_moreargsize(BX) // f's argument size
- MOVL $1, m_moreframesize(BX) // f's frame size
-
- // Call newstack on m->g0's stack.
- MOVL m_g0(BX), BP
- get_tls(CX)
- MOVL BP, g(CX)
- MOVL (g_sched+gobuf_sp)(BP), SP
- CALL runtime·newstack(SB)
- MOVL $0, 0x1103 // crash if newstack returns
- RET
-
// reflect·call: call a function with the given argument list
-// func call(f *FuncVal, arg *byte, argsize uint32).
+// func call(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!
@@ -361,7 +337,7 @@ TEXT runtime·newstackcall(SB), NOSPLIT, $0-12
#define DISPATCH(NAME,MAXSIZE) \
CMPL CX, $MAXSIZE; \
JA 3(PC); \
- MOVL $NAME(SB), AX; \
+ MOVL $NAME(SB), AX; \
JMP AX
// Note: can't just "JMP NAME(SB)" - bad inlining results.
@@ -397,11 +373,10 @@ TEXT reflect·call(SB), NOSPLIT, $0-16
MOVL $runtime·badreflectcall(SB), AX
JMP AX
-// Argument map for the callXX frames. Each has one
-// stack map (for the single call) with 3 arguments.
+// Argument map for the callXX frames. Each has one stack map.
DATA gcargs_reflectcall<>+0x00(SB)/4, $1 // 1 stackmap
-DATA gcargs_reflectcall<>+0x04(SB)/4, $6 // 3 args
-DATA gcargs_reflectcall<>+0x08(SB)/4, $(const_BitsPointer+(const_BitsPointer<<2)+(const_BitsScalar<<4))
+DATA gcargs_reflectcall<>+0x04(SB)/4, $8 // 4 words
+DATA gcargs_reflectcall<>+0x08(SB)/1, $(const_BitsPointer+(const_BitsPointer<<2)+(const_BitsScalar<<4)+(const_BitsScalar<<6))
GLOBL gcargs_reflectcall<>(SB),RODATA,$12
// callXX frames have no locals
@@ -481,7 +456,6 @@ TEXT runtime·lessstack(SB), NOSPLIT, $0-0
MOVL $0, 0x1004 // crash if oldstack returns
RET
-
// bool cas(int32 *val, int32 old, int32 new)
// Atomically:
// if(*val == old){
@@ -489,18 +463,29 @@ TEXT runtime·lessstack(SB), NOSPLIT, $0-0
// return 1;
// }else
// return 0;
-TEXT runtime·cas(SB), NOSPLIT, $0-12
- MOVL 4(SP), BX
- MOVL 8(SP), AX
- MOVL 12(SP), CX
+TEXT runtime·cas(SB), NOSPLIT, $0-13
+ MOVL ptr+0(FP), BX
+ MOVL old+4(FP), AX
+ MOVL new+8(FP), CX
LOCK
CMPXCHGL CX, 0(BX)
- JZ 3(PC)
+ JZ 4(PC)
MOVL $0, AX
+ MOVB AX, ret+12(FP)
RET
MOVL $1, AX
+ MOVB AX, ret+12(FP)
RET
+TEXT runtime·casuintptr(SB), NOSPLIT, $0-13
+ JMP runtime·cas(SB)
+
+TEXT runtime·atomicloaduintptr(SB), NOSPLIT, $0-8
+ JMP runtime·atomicload(SB)
+
+TEXT runtime·atomicloaduint(SB), NOSPLIT, $0-8
+ JMP runtime·atomicload(SB)
+
// bool runtime·cas64(uint64 *val, uint64 old, uint64 new)
// Atomically:
// if(*val == *old){
@@ -509,19 +494,21 @@ TEXT runtime·cas(SB), NOSPLIT, $0-12
// } else {
// return 0;
// }
-TEXT runtime·cas64(SB), NOSPLIT, $0-20
- MOVL 4(SP), BP
- MOVL 8(SP), AX
- MOVL 12(SP), DX
- MOVL 16(SP), BX
- MOVL 20(SP), CX
+TEXT runtime·cas64(SB), NOSPLIT, $0-21
+ MOVL ptr+0(FP), BP
+ MOVL old_lo+4(FP), AX
+ MOVL old_hi+8(FP), DX
+ MOVL new_lo+12(FP), BX
+ MOVL new_hi+16(FP), CX
LOCK
CMPXCHG8B 0(BP)
JNZ cas64_fail
MOVL $1, AX
+ MOVB AX, ret+20(FP)
RET
cas64_fail:
MOVL $0, AX
+ MOVB AX, ret+20(FP)
RET
// bool casp(void **p, void *old, void *new)
@@ -531,45 +518,53 @@ cas64_fail:
// return 1;
// }else
// return 0;
-TEXT runtime·casp(SB), NOSPLIT, $0-12
- MOVL 4(SP), BX
- MOVL 8(SP), AX
- MOVL 12(SP), CX
+TEXT runtime·casp(SB), NOSPLIT, $0-13
+ MOVL ptr+0(FP), BX
+ MOVL old+4(FP), AX
+ MOVL new+8(FP), CX
LOCK
CMPXCHGL CX, 0(BX)
- JZ 3(PC)
+ JZ 4(PC)
MOVL $0, AX
+ MOVB AX, ret+12(FP)
RET
MOVL $1, AX
+ MOVB AX, ret+12(FP)
RET
// uint32 xadd(uint32 volatile *val, int32 delta)
// Atomically:
// *val += delta;
// return *val;
-TEXT runtime·xadd(SB), NOSPLIT, $0-8
- MOVL 4(SP), BX
- MOVL 8(SP), AX
+TEXT runtime·xadd(SB), NOSPLIT, $0-12
+ MOVL ptr+0(FP), BX
+ MOVL delta+4(FP), AX
MOVL AX, CX
LOCK
XADDL AX, 0(BX)
ADDL CX, AX
+ MOVL AX, ret+8(FP)
RET
-TEXT runtime·xchg(SB), NOSPLIT, $0-8
- MOVL 4(SP), BX
- MOVL 8(SP), AX
+TEXT runtime·xchg(SB), NOSPLIT, $0-12
+ MOVL ptr+0(FP), BX
+ MOVL new+4(FP), AX
XCHGL AX, 0(BX)
+ MOVL AX, ret+8(FP)
RET
-TEXT runtime·xchgp(SB), NOSPLIT, $0-8
- MOVL 4(SP), BX
- MOVL 8(SP), AX
+TEXT runtime·xchgp(SB), NOSPLIT, $0-12
+ MOVL ptr+0(FP), BX
+ MOVL new+4(FP), AX
XCHGL AX, 0(BX)
+ MOVL AX, ret+8(FP)
RET
+TEXT runtime·xchguintptr(SB), NOSPLIT, $0-12
+ JMP runtime·xchg(SB)
+
TEXT runtime·procyield(SB),NOSPLIT,$0-0
- MOVL 4(SP), AX
+ MOVL cycles+0(FP), AX
again:
PAUSE
SUBL $1, AX
@@ -577,23 +572,21 @@ again:
RET
TEXT runtime·atomicstorep(SB), NOSPLIT, $0-8
- MOVL 4(SP), BX
- MOVL 8(SP), AX
+ MOVL ptr+0(FP), BX
+ MOVL val+4(FP), AX
XCHGL AX, 0(BX)
RET
TEXT runtime·atomicstore(SB), NOSPLIT, $0-8
- MOVL 4(SP), BX
- MOVL 8(SP), AX
+ MOVL ptr+0(FP), BX
+ MOVL val+4(FP), AX
XCHGL AX, 0(BX)
RET
// uint64 atomicload64(uint64 volatile* addr);
-// so actually
-// void atomicload64(uint64 *res, uint64 volatile *addr);
-TEXT runtime·atomicload64(SB), NOSPLIT, $0-8
- MOVL 4(SP), BX
- MOVL 8(SP), AX
+TEXT runtime·atomicload64(SB), NOSPLIT, $0-12
+ MOVL ptr+0(FP), AX
+ LEAL ret_lo+4(FP), BX
// MOVQ (%EAX), %MM0
BYTE $0x0f; BYTE $0x6f; BYTE $0x00
// MOVQ %MM0, 0(%EBX)
@@ -604,7 +597,7 @@ TEXT runtime·atomicload64(SB), NOSPLIT, $0-8
// void runtime·atomicstore64(uint64 volatile* addr, uint64 v);
TEXT runtime·atomicstore64(SB), NOSPLIT, $0-12
- MOVL 4(SP), AX
+ MOVL ptr+0(FP), AX
// MOVQ and EMMS were introduced on the Pentium MMX.
// MOVQ 0x8(%ESP), %MM0
BYTE $0x0f; BYTE $0x6f; BYTE $0x44; BYTE $0x24; BYTE $0x08
@@ -619,14 +612,22 @@ TEXT runtime·atomicstore64(SB), NOSPLIT, $0-12
XADDL AX, (SP)
RET
+// void runtime·atomicor8(byte volatile*, byte);
+TEXT runtime·atomicor8(SB), NOSPLIT, $0-5
+ MOVL ptr+0(FP), AX
+ MOVB val+4(FP), BX
+ LOCK
+ ORB BX, (AX)
+ RET
+
// void jmpdefer(fn, sp);
// called from deferreturn.
// 1. pop the caller
// 2. sub 5 bytes from the callers return
// 3. jmp to the argument
TEXT runtime·jmpdefer(SB), NOSPLIT, $0-8
- MOVL 4(SP), DX // fn
- MOVL 8(SP), BX // caller sp
+ MOVL fv+0(FP), DX // fn
+ MOVL argp+4(FP), BX // caller sp
LEAL -4(BX), SP // caller sp after CALL
SUBL $5, (SP) // return to CALL again
MOVL 0(DX), BX
@@ -655,6 +656,18 @@ TEXT gosave<>(SB),NOSPLIT,$0
TEXT runtime·asmcgocall(SB),NOSPLIT,$0-8
MOVL fn+0(FP), AX
MOVL arg+4(FP), BX
+ CALL asmcgocall<>(SB)
+ RET
+
+TEXT runtime·asmcgocall_errno(SB),NOSPLIT,$0-12
+ MOVL fn+0(FP), AX
+ MOVL arg+4(FP), BX
+ CALL asmcgocall<>(SB)
+ MOVL AX, ret+8(FP)
+ RET
+
+TEXT asmcgocall<>(SB),NOSPLIT,$0-12
+ // fn in AX, arg in BX
MOVL SP, DX
// Figure out if we need to switch to m->g0 stack.
@@ -796,7 +809,7 @@ havem:
RET
// void setg(G*); set g. for use by needm.
-TEXT runtime·setg(SB), NOSPLIT, $0-8
+TEXT runtime·setg(SB), NOSPLIT, $0-4
MOVL gg+0(FP), BX
#ifdef GOOS_windows
CMPL BX, $0
@@ -831,9 +844,10 @@ TEXT runtime·stackcheck(SB), NOSPLIT, $0-0
INT $3
RET
-TEXT runtime·getcallerpc(SB),NOSPLIT,$0-4
- MOVL x+0(FP),AX // addr of first arg
+TEXT runtime·getcallerpc(SB),NOSPLIT,$0-8
+ MOVL argp+0(FP),AX // addr of first arg
MOVL -4(AX),AX // get calling pc
+ MOVL AX, ret+4(FP)
RET
TEXT runtime·gogetcallerpc(SB),NOSPLIT,$0-8
@@ -843,22 +857,34 @@ TEXT runtime·gogetcallerpc(SB),NOSPLIT,$0-8
RET
TEXT runtime·setcallerpc(SB),NOSPLIT,$0-8
- MOVL x+0(FP),AX // addr of first arg
- MOVL x+4(FP), BX
+ MOVL argp+0(FP),AX // addr of first arg
+ MOVL pc+4(FP), BX
MOVL BX, -4(AX) // set calling pc
RET
-TEXT runtime·getcallersp(SB), NOSPLIT, $0-4
- MOVL sp+0(FP), AX
+TEXT runtime·getcallersp(SB), NOSPLIT, $0-8
+ MOVL argp+0(FP), AX
+ MOVL AX, ret+4(FP)
+ RET
+
+// func gogetcallersp(p unsafe.Pointer) uintptr
+TEXT runtime·gogetcallersp(SB),NOSPLIT,$0-8
+ MOVL p+0(FP),AX // addr of first arg
+ MOVL AX, ret+4(FP)
RET
// int64 runtime·cputicks(void), so really
// void runtime·cputicks(int64 *ticks)
-TEXT runtime·cputicks(SB),NOSPLIT,$0-4
+TEXT runtime·cputicks(SB),NOSPLIT,$0-8
+ RDTSC
+ MOVL AX, ret_lo+0(FP)
+ MOVL DX, ret_hi+4(FP)
+ RET
+
+TEXT runtime·gocputicks(SB),NOSPLIT,$0-8
RDTSC
- MOVL ret+0(FP), DI
- MOVL AX, 0(DI)
- MOVL DX, 4(DI)
+ MOVL AX, ret_lo+0(FP)
+ MOVL DX, ret_hi+4(FP)
RET
TEXT runtime·ldt0setup(SB),NOSPLIT,$16-0
@@ -956,7 +982,7 @@ finalize:
AESENC X2, X0
AESENC X3, X0
AESENC X2, X0
- MOVL X0, res+12(FP)
+ MOVL X0, ret+12(FP)
RET
TEXT runtime·aeshash32(SB),NOSPLIT,$0-16
@@ -967,7 +993,7 @@ TEXT runtime·aeshash32(SB),NOSPLIT,$0-16
AESENC runtime·aeskeysched+0(SB), X0
AESENC runtime·aeskeysched+16(SB), X0
AESENC runtime·aeskeysched+0(SB), X0
- MOVL X0, res+12(FP)
+ MOVL X0, ret+12(FP)
RET
TEXT runtime·aeshash64(SB),NOSPLIT,$0-16
@@ -978,7 +1004,7 @@ TEXT runtime·aeshash64(SB),NOSPLIT,$0-16
AESENC runtime·aeskeysched+0(SB), X0
AESENC runtime·aeskeysched+16(SB), X0
AESENC runtime·aeskeysched+0(SB), X0
- MOVL X0, res+12(FP)
+ MOVL X0, ret+12(FP)
RET
// simple mask to get rid of data in the high part of the register.
@@ -1149,13 +1175,7 @@ DATA shifts<>+0xfc(SB)/4, $0xff0f0e0d
GLOBL shifts<>(SB),RODATA,$256
-TEXT runtime·memeq(SB),NOSPLIT,$0-12
- MOVL a+0(FP), SI
- MOVL b+4(FP), DI
- MOVL count+8(FP), BX
- JMP runtime·memeqbody(SB)
-
-TEXT runtime·gomemeq(SB),NOSPLIT,$0-13
+TEXT runtime·memeq(SB),NOSPLIT,$0-13
MOVL a+0(FP), SI
MOVL b+4(FP), DI
MOVL size+8(FP), BX
@@ -1165,7 +1185,7 @@ TEXT runtime·gomemeq(SB),NOSPLIT,$0-13
// eqstring tests whether two strings are equal.
// See runtime_test.go:eqstring_generic for
-// equivlaent Go code.
+// equivalent Go code.
TEXT runtime·eqstring(SB),NOSPLIT,$0-17
MOVL s1len+4(FP), AX
MOVL s2len+12(FP), BX
@@ -1295,21 +1315,21 @@ equal:
RET
TEXT runtime·cmpstring(SB),NOSPLIT,$0-20
- MOVL s1+0(FP), SI
- MOVL s1+4(FP), BX
- MOVL s2+8(FP), DI
- MOVL s2+12(FP), DX
+ MOVL s1_base+0(FP), SI
+ MOVL s1_len+4(FP), BX
+ MOVL s2_base+8(FP), DI
+ MOVL s2_len+12(FP), DX
CALL runtime·cmpbody(SB)
- MOVL AX, res+16(FP)
+ MOVL AX, ret+16(FP)
RET
-TEXT bytes·Compare(SB),NOSPLIT,$0-28
+TEXT runtime·cmpbytes(SB),NOSPLIT,$0-28
MOVL s1+0(FP), SI
MOVL s1+4(FP), BX
MOVL s2+12(FP), DI
MOVL s2+16(FP), DX
CALL runtime·cmpbody(SB)
- MOVL AX, res+24(FP)
+ MOVL AX, ret+24(FP)
RET
TEXT bytes·IndexByte(SB),NOSPLIT,$0
@@ -2254,7 +2274,7 @@ TEXT runtime·duffcopy(SB), NOSPLIT, $0-0
TEXT runtime·timenow(SB), NOSPLIT, $0-0
JMP time·now(SB)
-TEXT runtime·fastrand2(SB), NOSPLIT, $0-4
+TEXT runtime·fastrand1(SB), NOSPLIT, $0-4
get_tls(CX)
MOVL g(CX), AX
MOVL g_m(AX), AX
@@ -2267,37 +2287,6 @@ TEXT runtime·fastrand2(SB), NOSPLIT, $0-4
MOVL DX, ret+0(FP)
RET
-// The goeq trampoline is necessary while we have
-// both Go and C calls to alg functions. Once we move all call
-// sites to Go, we can redo the eq functions to use the
-// Go calling convention and remove this.
-
-// convert call to:
-// func (alg unsafe.Pointer, p, q unsafe.Pointer, size uintptr) bool
-// to:
-// func (eq *bool, size uintptr, p, q unsafe.Pointer)
-TEXT runtime·goeq(SB), NOSPLIT, $16-17
- FUNCDATA $FUNCDATA_ArgsPointerMaps,gcargs_goeq<>(SB)
- FUNCDATA $FUNCDATA_LocalsPointerMaps,gclocals_goeq<>(SB)
- MOVL alg+0(FP), AX
- MOVL alg_equal(AX), AX
- MOVL p+4(FP), CX
- MOVL q+8(FP), DX
- MOVL size+12(FP), DI
- LEAL ret+16(FP), SI
- MOVL SI, 0(SP)
- MOVL DI, 4(SP)
- MOVL CX, 8(SP)
- MOVL DX, 12(SP)
- PCDATA $PCDATA_StackMapIndex, $0
- CALL *AX
- RET
-
-DATA gcargs_goeq<>+0x00(SB)/4, $1 // 1 stackmap
-DATA gcargs_goeq<>+0x04(SB)/4, $10 // 5 args
-DATA gcargs_goeq<>+0x08(SB)/4, $(const_BitsPointer+(const_BitsPointer<<2)+(const_BitsPointer<<4))
-GLOBL gcargs_goeq<>(SB),RODATA,$12
-
-DATA gclocals_goeq<>+0x00(SB)/4, $1 // 1 stackmap
-DATA gclocals_goeq<>+0x04(SB)/4, $0 // 0 locals
-GLOBL gclocals_goeq<>(SB),RODATA,$8
+TEXT runtime·return0(SB), NOSPLIT, $0
+ MOVL $0, AX
+ RET
diff --git a/src/pkg/runtime/asm_amd64.s b/src/pkg/runtime/asm_amd64.s
index 19e9f1d3a2..cc32ad8a18 100644
--- a/src/pkg/runtime/asm_amd64.s
+++ b/src/pkg/runtime/asm_amd64.s
@@ -4,9 +4,9 @@
#include "zasm_GOOS_GOARCH.h"
#include "funcdata.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
-TEXT _rt0_go(SB),NOSPLIT,$0
+TEXT runtime·rt0_go(SB),NOSPLIT,$0
// copy arguments forward on an even stack
MOVQ DI, AX // argc
MOVQ SI, BX // argv
@@ -88,11 +88,11 @@ ok:
MOVQ AX, 8(SP)
CALL runtime·args(SB)
CALL runtime·osinit(SB)
- CALL runtime·hashinit(SB)
CALL runtime·schedinit(SB)
// create a new goroutine to start program
- PUSHQ $runtime·main·f(SB) // entry
+ MOVQ $runtime·main·f(SB), BP // entry
+ PUSHQ BP
PUSHQ $0 // arg size
ARGSIZE(16)
CALL runtime·newproc(SB)
@@ -124,8 +124,8 @@ TEXT runtime·asminit(SB),NOSPLIT,$0-0
// void gosave(Gobuf*)
// save state in Gobuf; setjmp
TEXT runtime·gosave(SB), NOSPLIT, $0-8
- MOVQ 8(SP), AX // gobuf
- LEAQ 8(SP), BX // caller's SP
+ MOVQ buf+0(FP), AX // gobuf
+ LEAQ buf+0(FP), BX // caller's SP
MOVQ BX, gobuf_sp(AX)
MOVQ 0(SP), BX // caller's PC
MOVQ BX, gobuf_pc(AX)
@@ -139,7 +139,7 @@ TEXT runtime·gosave(SB), NOSPLIT, $0-8
// void gogo(Gobuf*)
// restore state from Gobuf; longjmp
TEXT runtime·gogo(SB), NOSPLIT, $0-8
- MOVQ 8(SP), BX // gobuf
+ MOVQ buf+0(FP), BX // gobuf
MOVQ gobuf_g(BX), DX
MOVQ 0(DX), CX // make sure g != nil
get_tls(CX)
@@ -153,7 +153,7 @@ TEXT runtime·gogo(SB), NOSPLIT, $0-8
MOVQ gobuf_pc(BX), BX
JMP BX
-// void mcall(void (*fn)(G*))
+// func 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.
@@ -164,7 +164,7 @@ TEXT runtime·mcall(SB), NOSPLIT, $0-8
MOVQ g(CX), AX // save state in g->sched
MOVQ 0(SP), BX // caller's PC
MOVQ BX, (g_sched+gobuf_pc)(AX)
- LEAQ 8(SP), BX // caller's SP
+ LEAQ fn+0(FP), BX // caller's SP
MOVQ BX, (g_sched+gobuf_sp)(AX)
MOVQ AX, (g_sched+gobuf_g)(AX)
@@ -180,6 +180,8 @@ TEXT runtime·mcall(SB), NOSPLIT, $0-8
MOVQ (g_sched+gobuf_sp)(SI), SP // sp = m->g0->sched.sp
PUSHQ AX
ARGSIZE(8)
+ MOVQ DI, DX
+ MOVQ 0(DI), DI
CALL DI
POPQ AX
MOVQ $runtime·badmcall2(SB), AX
@@ -194,31 +196,47 @@ TEXT runtime·mcall(SB), NOSPLIT, $0-8
TEXT runtime·switchtoM(SB), NOSPLIT, $0-8
RET
-// void onM(void (*fn)())
-// calls fn() on the M stack.
-// switches to the M stack if not already on it, and
-// switches back when fn() returns.
+// func onM(fn func())
TEXT runtime·onM(SB), NOSPLIT, $0-8
MOVQ fn+0(FP), DI // DI = fn
get_tls(CX)
MOVQ g(CX), AX // AX = g
MOVQ g_m(AX), BX // BX = m
+
MOVQ m_g0(BX), DX // DX = g0
CMPQ AX, DX
JEQ onm
+ MOVQ m_curg(BX), BP
+ CMPQ AX, BP
+ JEQ oncurg
+
+ // Not g0, not curg. Must be gsignal, but that's not allowed.
+ // Hide call from linker nosplit analysis.
+ MOVQ $runtime·badonm(SB), AX
+ CALL AX
+
+oncurg:
// save our state in g->sched. Pretend to
// be switchtoM if the G stack is scanned.
- MOVQ $runtime·switchtoM(SB), (g_sched+gobuf_pc)(AX)
+ MOVQ $runtime·switchtoM(SB), BP
+ MOVQ BP, (g_sched+gobuf_pc)(AX)
MOVQ SP, (g_sched+gobuf_sp)(AX)
MOVQ AX, (g_sched+gobuf_g)(AX)
// switch to g0
MOVQ DX, g(CX)
- MOVQ (g_sched+gobuf_sp)(DX), SP
+ MOVQ (g_sched+gobuf_sp)(DX), BX
+ // make it look like mstart called onM on g0, to stop traceback
+ SUBQ $8, BX
+ MOVQ $runtime·mstart(SB), DX
+ MOVQ DX, 0(BX)
+ MOVQ BX, SP
// call target function
ARGSIZE(0)
+ MOVQ DI, DX
+ MOVQ 0(DI), DI
CALL DI
// switch back to g
@@ -233,6 +251,8 @@ TEXT runtime·onM(SB), NOSPLIT, $0-8
onm:
// already on m stack, just call directly
+ MOVQ DI, DX
+ MOVQ 0(DI), DI
CALL DI
RET
@@ -254,6 +274,12 @@ TEXT runtime·morestack(SB),NOSPLIT,$0-0
JNE 2(PC)
INT $3
+ // Cannot grow signal stack (m->gsignal).
+ MOVQ m_gsignal(BX), SI
+ CMPQ g(CX), SI
+ JNE 2(PC)
+ INT $3
+
// Called from f.
// Set m->morebuf to f's caller.
MOVQ 8(SP), AX // f's caller's PC
@@ -281,56 +307,8 @@ TEXT runtime·morestack(SB),NOSPLIT,$0-0
MOVQ $0, 0x1003 // crash if newstack returns
RET
-// Called from panic. Mimics morestack,
-// reuses stack growth code to create a frame
-// with the desired args running the desired function.
-//
-// func call(fn *byte, arg *byte, argsize uint32).
-TEXT runtime·newstackcall(SB), NOSPLIT, $0-20
- get_tls(CX)
- MOVQ g(CX), BX
- MOVQ g_m(BX), BX
-
- // Save our caller's state as the PC and SP to
- // restore when returning from f.
- MOVQ 0(SP), AX // our caller's PC
- MOVQ AX, (m_morebuf+gobuf_pc)(BX)
- LEAQ 8(SP), AX // our caller's SP
- MOVQ AX, (m_morebuf+gobuf_sp)(BX)
- MOVQ g(CX), AX
- MOVQ AX, (m_morebuf+gobuf_g)(BX)
-
- // Save our own state as the PC and SP to restore
- // if this goroutine needs to be restarted.
- MOVQ $runtime·newstackcall(SB), (g_sched+gobuf_pc)(AX)
- MOVQ SP, (g_sched+gobuf_sp)(AX)
-
- // Set up morestack arguments to call f on a new stack.
- // We set f's frame size to 1, as a hint to newstack
- // that this is a call from runtime·newstackcall.
- // If it turns out that f needs a larger frame than
- // the default stack, f's usual stack growth prolog will
- // allocate a new segment (and recopy the arguments).
- MOVQ 8(SP), AX // fn
- MOVQ 16(SP), DX // arg frame
- MOVL 24(SP), CX // arg size
-
- MOVQ AX, m_cret(BX) // f's PC
- MOVQ DX, m_moreargp(BX) // argument frame pointer
- MOVL CX, m_moreargsize(BX) // f's argument size
- MOVL $1, m_moreframesize(BX) // f's frame size
-
- // Call newstack on m->g0's stack.
- MOVQ m_g0(BX), BP
- get_tls(CX)
- MOVQ BP, g(CX)
- MOVQ (g_sched+gobuf_sp)(BP), SP
- CALL runtime·newstack(SB)
- MOVQ $0, 0x1103 // crash if newstack returns
- RET
-
// reflect·call: call a function with the given argument list
-// func call(f *FuncVal, arg *byte, argsize uint32).
+// func call(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!
@@ -374,11 +352,10 @@ TEXT reflect·call(SB), NOSPLIT, $0-24
MOVQ $runtime·badreflectcall(SB), AX
JMP AX
-// Argument map for the callXX frames. Each has one
-// stack map (for the single call) with 3 arguments.
+// Argument map for the callXX frames. Each has one stack map.
DATA gcargs_reflectcall<>+0x00(SB)/4, $1 // 1 stackmap
-DATA gcargs_reflectcall<>+0x04(SB)/4, $6 // 3 args
-DATA gcargs_reflectcall<>+0x08(SB)/4, $(const_BitsPointer+(const_BitsPointer<<2)+(const_BitsScalar<<4))
+DATA gcargs_reflectcall<>+0x04(SB)/4, $6 // 3 words
+DATA gcargs_reflectcall<>+0x08(SB)/1, $(const_BitsPointer+(const_BitsPointer<<2)+(const_BitsScalar<<4))
GLOBL gcargs_reflectcall<>(SB),RODATA,$12
// callXX frames have no locals
@@ -387,7 +364,7 @@ DATA gclocals_reflectcall<>+0x04(SB)/4, $0 // 0 locals
GLOBL gclocals_reflectcall<>(SB),RODATA,$8
#define CALLFN(NAME,MAXSIZE) \
-TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \
+TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \
FUNCDATA $FUNCDATA_ArgsPointerMaps,gcargs_reflectcall<>(SB); \
FUNCDATA $FUNCDATA_LocalsPointerMaps,gclocals_reflectcall<>(SB);\
/* copy arguments to stack */ \
@@ -581,16 +558,18 @@ TEXT runtime·morestack48_noctxt(SB),NOSPLIT,$0
// return 1;
// } else
// return 0;
-TEXT runtime·cas(SB), NOSPLIT, $0-16
- MOVQ 8(SP), BX
- MOVL 16(SP), AX
- MOVL 20(SP), CX
+TEXT runtime·cas(SB), NOSPLIT, $0-17
+ MOVQ ptr+0(FP), BX
+ MOVL old+8(FP), AX
+ MOVL new+12(FP), CX
LOCK
CMPXCHGL CX, 0(BX)
- JZ 3(PC)
+ JZ 4(PC)
MOVL $0, AX
+ MOVB AX, ret+16(FP)
RET
MOVL $1, AX
+ MOVB AX, ret+16(FP)
RET
// bool runtime·cas64(uint64 *val, uint64 old, uint64 new)
@@ -601,18 +580,29 @@ TEXT runtime·cas(SB), NOSPLIT, $0-16
// } else {
// return 0;
// }
-TEXT runtime·cas64(SB), NOSPLIT, $0-24
- MOVQ 8(SP), BX
- MOVQ 16(SP), AX
- MOVQ 24(SP), CX
+TEXT runtime·cas64(SB), NOSPLIT, $0-25
+ MOVQ ptr+0(FP), BX
+ MOVQ old+8(FP), AX
+ MOVQ new+16(FP), CX
LOCK
CMPXCHGQ CX, 0(BX)
JNZ cas64_fail
MOVL $1, AX
+ MOVB AX, ret+24(FP)
RET
cas64_fail:
MOVL $0, AX
+ MOVB AX, ret+24(FP)
RET
+
+TEXT runtime·casuintptr(SB), NOSPLIT, $0-25
+ JMP runtime·cas64(SB)
+
+TEXT runtime·atomicloaduintptr(SB), NOSPLIT, $0-16
+ JMP runtime·atomicload64(SB)
+
+TEXT runtime·atomicloaduint(SB), NOSPLIT, $0-16
+ JMP runtime·atomicload64(SB)
// bool casp(void **val, void *old, void *new)
// Atomically:
@@ -621,60 +611,70 @@ cas64_fail:
// return 1;
// } else
// return 0;
-TEXT runtime·casp(SB), NOSPLIT, $0-24
- MOVQ 8(SP), BX
- MOVQ 16(SP), AX
- MOVQ 24(SP), CX
+TEXT runtime·casp(SB), NOSPLIT, $0-25
+ MOVQ ptr+0(FP), BX
+ MOVQ old+8(FP), AX
+ MOVQ new+16(FP), CX
LOCK
CMPXCHGQ CX, 0(BX)
- JZ 3(PC)
+ JZ 4(PC)
MOVL $0, AX
+ MOVB AX, ret+24(FP)
RET
MOVL $1, AX
+ MOVB AX, ret+24(FP)
RET
// uint32 xadd(uint32 volatile *val, int32 delta)
// Atomically:
// *val += delta;
// return *val;
-TEXT runtime·xadd(SB), NOSPLIT, $0-12
- MOVQ 8(SP), BX
- MOVL 16(SP), AX
+TEXT runtime·xadd(SB), NOSPLIT, $0-20
+ MOVQ ptr+0(FP), BX
+ MOVL delta+8(FP), AX
MOVL AX, CX
LOCK
XADDL AX, 0(BX)
ADDL CX, AX
+ MOVL AX, ret+16(FP)
RET
-TEXT runtime·xadd64(SB), NOSPLIT, $0-16
- MOVQ 8(SP), BX
- MOVQ 16(SP), AX
+TEXT runtime·xadd64(SB), NOSPLIT, $0-24
+ MOVQ ptr+0(FP), BX
+ MOVQ delta+8(FP), AX
MOVQ AX, CX
LOCK
XADDQ AX, 0(BX)
ADDQ CX, AX
+ MOVQ AX, ret+16(FP)
RET
-TEXT runtime·xchg(SB), NOSPLIT, $0-12
- MOVQ 8(SP), BX
- MOVL 16(SP), AX
+TEXT runtime·xchg(SB), NOSPLIT, $0-20
+ MOVQ ptr+0(FP), BX
+ MOVL new+8(FP), AX
XCHGL AX, 0(BX)
+ MOVL AX, ret+16(FP)
RET
-TEXT runtime·xchg64(SB), NOSPLIT, $0-16
- MOVQ 8(SP), BX
- MOVQ 16(SP), AX
+TEXT runtime·xchg64(SB), NOSPLIT, $0-24
+ MOVQ ptr+0(FP), BX
+ MOVQ new+8(FP), AX
XCHGQ AX, 0(BX)
+ MOVQ AX, ret+16(FP)
RET
-TEXT runtime·xchgp(SB), NOSPLIT, $0-16
- MOVQ 8(SP), BX
- MOVQ 16(SP), AX
+TEXT runtime·xchgp(SB), NOSPLIT, $0-24
+ MOVQ ptr+0(FP), BX
+ MOVQ new+8(FP), AX
XCHGQ AX, 0(BX)
+ MOVQ AX, ret+16(FP)
RET
+TEXT runtime·xchguintptr(SB), NOSPLIT, $0-24
+ JMP runtime·xchg64(SB)
+
TEXT runtime·procyield(SB),NOSPLIT,$0-0
- MOVL 8(SP), AX
+ MOVL cycles+0(FP), AX
again:
PAUSE
SUBL $1, AX
@@ -682,31 +682,39 @@ again:
RET
TEXT runtime·atomicstorep(SB), NOSPLIT, $0-16
- MOVQ 8(SP), BX
- MOVQ 16(SP), AX
+ MOVQ ptr+0(FP), BX
+ MOVQ val+8(FP), AX
XCHGQ AX, 0(BX)
RET
TEXT runtime·atomicstore(SB), NOSPLIT, $0-12
- MOVQ 8(SP), BX
- MOVL 16(SP), AX
+ MOVQ ptr+0(FP), BX
+ MOVL val+8(FP), AX
XCHGL AX, 0(BX)
RET
TEXT runtime·atomicstore64(SB), NOSPLIT, $0-16
- MOVQ 8(SP), BX
- MOVQ 16(SP), AX
+ MOVQ ptr+0(FP), BX
+ MOVQ val+8(FP), AX
XCHGQ AX, 0(BX)
RET
+// void runtime·atomicor8(byte volatile*, byte);
+TEXT runtime·atomicor8(SB), NOSPLIT, $0-9
+ MOVQ ptr+0(FP), AX
+ MOVB val+8(FP), BX
+ LOCK
+ ORB BX, (AX)
+ RET
+
// void jmpdefer(fn, sp);
// called from deferreturn.
// 1. pop the caller
// 2. sub 5 bytes from the callers return
// 3. jmp to the argument
TEXT runtime·jmpdefer(SB), NOSPLIT, $0-16
- MOVQ 8(SP), DX // fn
- MOVQ 16(SP), BX // caller sp
+ MOVQ fv+0(FP), DX // fn
+ MOVQ argp+8(FP), BX // caller sp
LEAQ -8(BX), SP // caller sp after CALL
SUBQ $5, (SP) // return to CALL again
MOVQ 0(DX), BX
@@ -731,6 +739,18 @@ TEXT gosave<>(SB),NOSPLIT,$0
TEXT runtime·asmcgocall(SB),NOSPLIT,$0-16
MOVQ fn+0(FP), AX
MOVQ arg+8(FP), BX
+ CALL asmcgocall<>(SB)
+ RET
+
+TEXT runtime·asmcgocall_errno(SB),NOSPLIT,$0-20
+ MOVQ fn+0(FP), AX
+ MOVQ arg+8(FP), BX
+ CALL asmcgocall<>(SB)
+ MOVL AX, ret+16(FP)
+ RET
+
+// asmcgocall common code. fn in AX, arg in BX. returns errno in AX.
+TEXT asmcgocall<>(SB),NOSPLIT,$0-0
MOVQ SP, DX
// Figure out if we need to switch to m->g0 stack.
@@ -880,7 +900,7 @@ havem:
RET
// void setg(G*); set g. for use by needm.
-TEXT runtime·setg(SB), NOSPLIT, $0-16
+TEXT runtime·setg(SB), NOSPLIT, $0-8
MOVQ gg+0(FP), BX
#ifdef GOOS_windows
CMPQ BX, $0
@@ -914,9 +934,10 @@ TEXT runtime·stackcheck(SB), NOSPLIT, $0-0
INT $3
RET
-TEXT runtime·getcallerpc(SB),NOSPLIT,$0-8
- MOVQ x+0(FP),AX // addr of first arg
+TEXT runtime·getcallerpc(SB),NOSPLIT,$0-16
+ MOVQ argp+0(FP),AX // addr of first arg
MOVQ -8(AX),AX // get calling pc
+ MOVQ AX, ret+8(FP)
RET
TEXT runtime·gogetcallerpc(SB),NOSPLIT,$0-16
@@ -926,13 +947,20 @@ TEXT runtime·gogetcallerpc(SB),NOSPLIT,$0-16
RET
TEXT runtime·setcallerpc(SB),NOSPLIT,$0-16
- MOVQ x+0(FP),AX // addr of first arg
- MOVQ x+8(FP), BX
+ MOVQ argp+0(FP),AX // addr of first arg
+ MOVQ pc+8(FP), BX
MOVQ BX, -8(AX) // set calling pc
RET
-TEXT runtime·getcallersp(SB),NOSPLIT,$0-8
- MOVQ sp+0(FP), AX
+TEXT runtime·getcallersp(SB),NOSPLIT,$0-16
+ MOVQ argp+0(FP), AX
+ MOVQ AX, ret+8(FP)
+ RET
+
+// func gogetcallersp(p unsafe.Pointer) uintptr
+TEXT runtime·gogetcallersp(SB),NOSPLIT,$0-16
+ MOVQ p+0(FP),AX // addr of first arg
+ MOVQ AX, ret+8(FP)
RET
// int64 runtime·cputicks(void)
@@ -940,6 +968,14 @@ TEXT runtime·cputicks(SB),NOSPLIT,$0-0
RDTSC
SHLQ $32, DX
ADDQ DX, AX
+ MOVQ AX, ret+0(FP)
+ RET
+
+TEXT runtime·gocputicks(SB),NOSPLIT,$0-8
+ RDTSC
+ SHLQ $32, DX
+ ADDQ DX, AX
+ MOVQ AX, ret+0(FP)
RET
TEXT runtime·stackguard(SB),NOSPLIT,$0-16
@@ -1002,7 +1038,8 @@ aessmall:
// a page boundary, so we can load it directly.
MOVOU (AX), X1
ADDQ CX, CX
- PAND masks<>(SB)(CX*8), X1
+ MOVQ $masks<>(SB), BP
+ PAND (BP)(CX*8), X1
JMP partial
highpartial:
// address ends in 1111xxxx. Might be up against
@@ -1010,7 +1047,8 @@ highpartial:
// Then shift bytes down using pshufb.
MOVOU -16(AX)(CX*1), X1
ADDQ CX, CX
- PSHUFB shifts<>(SB)(CX*8), X1
+ MOVQ $shifts<>(SB), BP
+ PSHUFB (BP)(CX*8), X1
partial:
// incorporate partial block into hash
AESENC X3, X0
@@ -1031,7 +1069,7 @@ TEXT runtime·aeshash32(SB),NOSPLIT,$0-32
AESENC runtime·aeskeysched+0(SB), X0
AESENC runtime·aeskeysched+16(SB), X0
AESENC runtime·aeskeysched+0(SB), X0
- MOVQ X0, res+24(FP)
+ MOVQ X0, ret+24(FP)
RET
TEXT runtime·aeshash64(SB),NOSPLIT,$0-32
@@ -1042,7 +1080,7 @@ TEXT runtime·aeshash64(SB),NOSPLIT,$0-32
AESENC runtime·aeskeysched+0(SB), X0
AESENC runtime·aeskeysched+16(SB), X0
AESENC runtime·aeskeysched+0(SB), X0
- MOVQ X0, res+24(FP)
+ MOVQ X0, ret+24(FP)
RET
// simple mask to get rid of data in the high part of the register.
@@ -1117,13 +1155,7 @@ DATA shifts<>+0xf0(SB)/8, $0x0807060504030201
DATA shifts<>+0xf8(SB)/8, $0xff0f0e0d0c0b0a09
GLOBL shifts<>(SB),RODATA,$256
-TEXT runtime·memeq(SB),NOSPLIT,$0-24
- MOVQ a+0(FP), SI
- MOVQ b+8(FP), DI
- MOVQ count+16(FP), BX
- JMP runtime·memeqbody(SB)
-
-TEXT runtime·gomemeq(SB),NOSPLIT,$0-25
+TEXT runtime·memeq(SB),NOSPLIT,$0-25
MOVQ a+0(FP), SI
MOVQ b+8(FP), DI
MOVQ size+16(FP), BX
@@ -1133,7 +1165,7 @@ TEXT runtime·gomemeq(SB),NOSPLIT,$0-25
// eqstring tests whether two strings are equal.
// See runtime_test.go:eqstring_generic for
-// equivlaent Go code.
+// equivalent Go code.
TEXT runtime·eqstring(SB),NOSPLIT,$0-33
MOVQ s1len+8(FP), AX
MOVQ s2len+24(FP), BX
@@ -1246,15 +1278,15 @@ equal:
RET
TEXT runtime·cmpstring(SB),NOSPLIT,$0-40
- MOVQ s1+0(FP), SI
- MOVQ s1+8(FP), BX
- MOVQ s2+16(FP), DI
- MOVQ s2+24(FP), DX
+ MOVQ s1_base+0(FP), SI
+ MOVQ s1_len+8(FP), BX
+ MOVQ s2_base+16(FP), DI
+ MOVQ s2_len+24(FP), DX
CALL runtime·cmpbody(SB)
- MOVQ AX, res+32(FP)
+ MOVQ AX, ret+32(FP)
RET
-TEXT bytes·Compare(SB),NOSPLIT,$0-56
+TEXT runtime·cmpbytes(SB),NOSPLIT,$0-56
MOVQ s1+0(FP), SI
MOVQ s1+8(FP), BX
MOVQ s2+24(FP), DI
@@ -2293,7 +2325,7 @@ TEXT runtime·duffcopy(SB), NOSPLIT, $0-0
TEXT runtime·timenow(SB), NOSPLIT, $0-0
JMP time·now(SB)
-TEXT runtime·fastrand2(SB), NOSPLIT, $0-4
+TEXT runtime·fastrand1(SB), NOSPLIT, $0-4
get_tls(CX)
MOVQ g(CX), AX
MOVQ g_m(AX), AX
@@ -2306,37 +2338,6 @@ TEXT runtime·fastrand2(SB), NOSPLIT, $0-4
MOVL DX, ret+0(FP)
RET
-// goeq trampoline is necessary while we have
-// both Go and C calls to alg functions. Once we move all call
-// sites to Go, we can redo the eq function to use the
-// Go calling convention and remove this.
-
-// convert call to:
-// func (alg unsafe.Pointer, p, q unsafe.Pointer, size uintptr) bool
-// to:
-// func (eq *bool, size uintptr, p, q unsafe.Pointer)
-TEXT runtime·goeq(SB), NOSPLIT, $32-33
- FUNCDATA $FUNCDATA_ArgsPointerMaps,gcargs_goeq<>(SB)
- FUNCDATA $FUNCDATA_LocalsPointerMaps,gclocals_goeq<>(SB)
- MOVQ alg+0(FP), AX
- MOVQ alg_equal(AX), AX
- MOVQ p+8(FP), CX
- MOVQ q+16(FP), DX
- MOVQ size+24(FP), DI
- LEAQ ret+32(FP), SI
- MOVQ SI, 0(SP)
- MOVQ DI, 8(SP)
- MOVQ CX, 16(SP)
- MOVQ DX, 24(SP)
- PCDATA $PCDATA_StackMapIndex, $0
- CALL *AX
- RET
-
-DATA gcargs_goeq<>+0x00(SB)/4, $1 // 1 stackmap
-DATA gcargs_goeq<>+0x04(SB)/4, $10 // 5 args
-DATA gcargs_goeq<>+0x08(SB)/4, $(const_BitsPointer+(const_BitsPointer<<2)+(const_BitsPointer<<4))
-GLOBL gcargs_goeq<>(SB),RODATA,$12
-
-DATA gclocals_goeq<>+0x00(SB)/4, $1 // 1 stackmap
-DATA gclocals_goeq<>+0x04(SB)/4, $0 // 0 locals
-GLOBL gclocals_goeq<>(SB),RODATA,$8
+TEXT runtime·return0(SB), NOSPLIT, $0
+ MOVL $0, AX
+ RET
diff --git a/src/pkg/runtime/asm_amd64p32.s b/src/pkg/runtime/asm_amd64p32.s
index f2a1f2a0bc..6c10bec5c3 100644
--- a/src/pkg/runtime/asm_amd64p32.s
+++ b/src/pkg/runtime/asm_amd64p32.s
@@ -4,9 +4,9 @@
#include "zasm_GOOS_GOARCH.h"
#include "funcdata.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
-TEXT _rt0_go(SB),NOSPLIT,$0
+TEXT runtime·rt0_go(SB),NOSPLIT,$0
// copy arguments forward on an even stack
MOVL argc+0(FP), AX
MOVL argv+4(FP), BX
@@ -68,7 +68,6 @@ ok:
MOVL AX, 4(SP)
CALL runtime·args(SB)
CALL runtime·osinit(SB)
- CALL runtime·hashinit(SB)
CALL runtime·schedinit(SB)
// create a new goroutine to start program
@@ -103,8 +102,8 @@ TEXT runtime·asminit(SB),NOSPLIT,$0-0
// void gosave(Gobuf*)
// save state in Gobuf; setjmp
TEXT runtime·gosave(SB), NOSPLIT, $0-4
- MOVL b+0(FP), AX // gobuf
- LEAL b+0(FP), BX // caller's SP
+ MOVL buf+0(FP), AX // gobuf
+ LEAL buf+0(FP), BX // caller's SP
MOVL BX, gobuf_sp(AX)
MOVL 0(SP), BX // caller's PC
MOVL BX, gobuf_pc(AX)
@@ -118,7 +117,7 @@ TEXT runtime·gosave(SB), NOSPLIT, $0-4
// void gogo(Gobuf*)
// restore state from Gobuf; longjmp
TEXT runtime·gogo(SB), NOSPLIT, $0-4
- MOVL b+0(FP), BX // gobuf
+ MOVL buf+0(FP), BX // gobuf
MOVL gobuf_g(BX), DX
MOVL 0(DX), CX // make sure g != nil
get_tls(CX)
@@ -132,7 +131,7 @@ TEXT runtime·gogo(SB), NOSPLIT, $0-4
MOVL gobuf_pc(BX), BX
JMP BX
-// void mcall(void (*fn)(G*))
+// func 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.
@@ -159,6 +158,8 @@ TEXT runtime·mcall(SB), NOSPLIT, $0-4
MOVL (g_sched+gobuf_sp)(SI), SP // sp = m->g0->sched.sp
PUSHQ AX
ARGSIZE(8)
+ MOVL DI, DX
+ MOVL 0(DI), DI
CALL DI
POPQ AX
MOVL $runtime·badmcall2(SB), AX
@@ -173,19 +174,27 @@ TEXT runtime·mcall(SB), NOSPLIT, $0-4
TEXT runtime·switchtoM(SB), NOSPLIT, $0-4
RET
-// void onM(void (*fn)())
-// calls fn() on the M stack.
-// switches to the M stack if not already on it, and
-// switches back when fn() returns.
+// func onM(fn func())
TEXT runtime·onM(SB), NOSPLIT, $0-4
MOVL fn+0(FP), DI // DI = fn
get_tls(CX)
MOVL g(CX), AX // AX = g
MOVL g_m(AX), BX // BX = m
+
MOVL m_g0(BX), DX // DX = g0
CMPL AX, DX
JEQ onm
+ MOVL m_curg(BX), R8
+ CMPL AX, R8
+ JEQ oncurg
+
+ // Not g0, not curg. Must be gsignal, but that's not allowed.
+ // Hide call from linker nosplit analysis.
+ MOVL $runtime·badonm(SB), AX
+ CALL AX
+
+oncurg:
// save our state in g->sched. Pretend to
// be switchtoM if the G stack is scanned.
MOVL $runtime·switchtoM(SB), SI
@@ -199,6 +208,8 @@ TEXT runtime·onM(SB), NOSPLIT, $0-4
// call target function
ARGSIZE(0)
+ MOVL DI, DX
+ MOVL 0(DI), DI
CALL DI
// switch back to g
@@ -213,6 +224,8 @@ TEXT runtime·onM(SB), NOSPLIT, $0-4
onm:
// already on m stack, just call directly
+ MOVL DI, DX
+ MOVL 0(DI), DI
CALL DI
RET
@@ -234,6 +247,12 @@ TEXT runtime·morestack(SB),NOSPLIT,$0-0
JNE 2(PC)
MOVL 0, AX
+ // Cannot grow signal stack (m->gsignal).
+ MOVL m_gsignal(BX), SI
+ CMPL g(CX), SI
+ JNE 2(PC)
+ MOVL 0, AX
+
// Called from f.
// Set m->morebuf to f's caller.
MOVL 8(SP), AX // f's caller's PC
@@ -261,57 +280,8 @@ TEXT runtime·morestack(SB),NOSPLIT,$0-0
MOVL $0, 0x1003 // crash if newstack returns
RET
-// Called from panic. Mimics morestack,
-// reuses stack growth code to create a frame
-// with the desired args running the desired function.
-//
-// func call(fn *byte, arg *byte, argsize uint32).
-TEXT runtime·newstackcall(SB), NOSPLIT, $0-20
- get_tls(CX)
- MOVL g(CX), BX
- MOVL g_m(BX), BX
-
- // Save our caller's state as the PC and SP to
- // restore when returning from f.
- MOVL 0(SP), AX // our caller's PC
- MOVL AX, (m_morebuf+gobuf_pc)(BX)
- LEAL 8(SP), AX // our caller's SP
- MOVL AX, (m_morebuf+gobuf_sp)(BX)
- MOVL g(CX), AX
- MOVL AX, (m_morebuf+gobuf_g)(BX)
-
- // Save our own state as the PC and SP to restore
- // if this goroutine needs to be restarted.
- MOVL $runtime·newstackcall(SB), DI
- MOVL DI, (g_sched+gobuf_pc)(AX)
- MOVL SP, (g_sched+gobuf_sp)(AX)
-
- // Set up morestack arguments to call f on a new stack.
- // We set f's frame size to 1, as a hint to newstack
- // that this is a call from runtime·newstackcall.
- // If it turns out that f needs a larger frame than
- // the default stack, f's usual stack growth prolog will
- // allocate a new segment (and recopy the arguments).
- MOVL 8(SP), AX // fn
- MOVL 12(SP), DX // arg frame
- MOVL 16(SP), CX // arg size
-
- MOVQ AX, m_cret(BX) // f's PC
- MOVL DX, m_moreargp(BX) // argument frame pointer
- MOVL CX, m_moreargsize(BX) // f's argument size
- MOVL $1, m_moreframesize(BX) // f's frame size
-
- // Call newstack on m->g0's stack.
- MOVL m_g0(BX), BX
- get_tls(CX)
- MOVL BX, g(CX)
- MOVL (g_sched+gobuf_sp)(BX), SP
- CALL runtime·newstack(SB)
- MOVL $0, 0x1103 // crash if newstack returns
- RET
-
// reflect·call: call a function with the given argument list
-// func call(f *FuncVal, arg *byte, argsize uint32).
+// func call(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!
@@ -323,7 +293,7 @@ TEXT runtime·newstackcall(SB), NOSPLIT, $0-20
JMP AX
// Note: can't just "JMP NAME(SB)" - bad inlining results.
-TEXT reflect·call(SB), NOSPLIT, $0-20
+TEXT reflect·call(SB), NOSPLIT, $0-16
MOVLQZX argsize+8(FP), CX
DISPATCH(runtime·call16, 16)
DISPATCH(runtime·call32, 32)
@@ -355,8 +325,22 @@ TEXT reflect·call(SB), NOSPLIT, $0-20
MOVL $runtime·badreflectcall(SB), AX
JMP AX
+// Argument map for the callXX frames. Each has one stack map.
+DATA gcargs_reflectcall<>+0x00(SB)/4, $1 // 1 stackmap
+DATA gcargs_reflectcall<>+0x04(SB)/4, $10 // 5 words
+DATA gcargs_reflectcall<>+0x08(SB)/1, $(const_BitsPointer+(const_BitsPointer<<2)+(const_BitsScalar<<4)+(const_BitsScalar<<6))
+DATA gcargs_reflectcall<>+0x09(SB)/1, $(const_BitsPointer)
+GLOBL gcargs_reflectcall<>(SB),RODATA,$12
+
+// callXX frames have no locals
+DATA gclocals_reflectcall<>+0x00(SB)/4, $1 // 1 stackmap
+DATA gclocals_reflectcall<>+0x04(SB)/4, $0 // 0 locals
+GLOBL gclocals_reflectcall<>(SB),RODATA,$8
+
#define CALLFN(NAME,MAXSIZE) \
TEXT NAME(SB), WRAPPER, $MAXSIZE-16; \
+ FUNCDATA $FUNCDATA_ArgsPointerMaps,gcargs_reflectcall<>(SB); \
+ FUNCDATA $FUNCDATA_LocalsPointerMaps,gclocals_reflectcall<>(SB);\
/* copy arguments to stack */ \
MOVL argptr+4(FP), SI; \
MOVL argsize+8(FP), CX; \
@@ -548,18 +532,29 @@ TEXT runtime·morestack48_noctxt(SB),NOSPLIT,$0
// return 1;
// } else
// return 0;
-TEXT runtime·cas(SB), NOSPLIT, $0-12
- MOVL val+0(FP), BX
+TEXT runtime·cas(SB), NOSPLIT, $0-17
+ MOVL ptr+0(FP), BX
MOVL old+4(FP), AX
MOVL new+8(FP), CX
LOCK
CMPXCHGL CX, 0(BX)
- JZ 3(PC)
+ JZ 4(PC)
MOVL $0, AX
+ MOVB AX, ret+16(FP)
RET
MOVL $1, AX
+ MOVB AX, ret+16(FP)
RET
+TEXT runtime·casuintptr(SB), NOSPLIT, $0-17
+ JMP runtime·cas(SB)
+
+TEXT runtime·atomicloaduintptr(SB), NOSPLIT, $0-12
+ JMP runtime·atomicload(SB)
+
+TEXT runtime·atomicloaduint(SB), NOSPLIT, $0-12
+ JMP runtime·atomicload(SB)
+
// bool runtime·cas64(uint64 *val, uint64 old, uint64 new)
// Atomically:
// if(*val == *old){
@@ -568,17 +563,19 @@ TEXT runtime·cas(SB), NOSPLIT, $0-12
// } else {
// return 0;
// }
-TEXT runtime·cas64(SB), NOSPLIT, $0-24
- MOVL val+0(FP), BX
+TEXT runtime·cas64(SB), NOSPLIT, $0-25
+ MOVL ptr+0(FP), BX
MOVQ old+8(FP), AX
MOVQ new+16(FP), CX
LOCK
CMPXCHGQ CX, 0(BX)
JNZ cas64_fail
MOVL $1, AX
+ MOVB AX, ret+24(FP)
RET
cas64_fail:
MOVL $0, AX
+ MOVB AX, ret+24(FP)
RET
// bool casp(void **val, void *old, void *new)
@@ -588,54 +585,70 @@ cas64_fail:
// return 1;
// } else
// return 0;
-TEXT runtime·casp(SB), NOSPLIT, $0-12
- MOVL val+0(FP), BX
+TEXT runtime·casp(SB), NOSPLIT, $0-17
+ MOVL ptr+0(FP), BX
MOVL old+4(FP), AX
MOVL new+8(FP), CX
LOCK
CMPXCHGL CX, 0(BX)
- JZ 3(PC)
+ JZ 4(PC)
MOVL $0, AX
+ MOVB AX, ret+16(FP)
RET
MOVL $1, AX
+ MOVB AX, ret+16(FP)
RET
// uint32 xadd(uint32 volatile *val, int32 delta)
// Atomically:
// *val += delta;
// return *val;
-TEXT runtime·xadd(SB), NOSPLIT, $0-8
- MOVL val+0(FP), BX
+TEXT runtime·xadd(SB), NOSPLIT, $0-12
+ MOVL ptr+0(FP), BX
MOVL delta+4(FP), AX
MOVL AX, CX
LOCK
XADDL AX, 0(BX)
ADDL CX, AX
+ MOVL AX, ret+8(FP)
RET
-TEXT runtime·xadd64(SB), NOSPLIT, $0-16
- MOVL val+0(FP), BX
+TEXT runtime·xadd64(SB), NOSPLIT, $0-24
+ MOVL ptr+0(FP), BX
MOVQ delta+8(FP), AX
MOVQ AX, CX
LOCK
XADDQ AX, 0(BX)
ADDQ CX, AX
+ MOVQ AX, ret+16(FP)
RET
-TEXT runtime·xchg(SB), NOSPLIT, $0-8
- MOVL val+0(FP), BX
+TEXT runtime·xchg(SB), NOSPLIT, $0-12
+ MOVL ptr+0(FP), BX
MOVL new+4(FP), AX
XCHGL AX, 0(BX)
+ MOVL AX, ret+8(FP)
RET
-TEXT runtime·xchg64(SB), NOSPLIT, $0-16
- MOVL val+0(FP), BX
+TEXT runtime·xchg64(SB), NOSPLIT, $0-24
+ MOVL ptr+0(FP), BX
MOVQ new+8(FP), AX
XCHGQ AX, 0(BX)
+ MOVQ AX, ret+16(FP)
+ RET
+
+TEXT runtime·xchgp(SB), NOSPLIT, $0-12
+ MOVL ptr+0(FP), BX
+ MOVL new+4(FP), AX
+ XCHGL AX, 0(BX)
+ MOVL AX, ret+8(FP)
RET
+TEXT runtime·xchguintptr(SB), NOSPLIT, $0-12
+ JMP runtime·xchg(SB)
+
TEXT runtime·procyield(SB),NOSPLIT,$0-0
- MOVL val+0(FP), AX
+ MOVL cycles+0(FP), AX
again:
PAUSE
SUBL $1, AX
@@ -660,14 +673,22 @@ TEXT runtime·atomicstore64(SB), NOSPLIT, $0-16
XCHGQ AX, 0(BX)
RET
+// void runtime·atomicor8(byte volatile*, byte);
+TEXT runtime·atomicor8(SB), NOSPLIT, $0-5
+ MOVL ptr+0(FP), BX
+ MOVB val+4(FP), AX
+ LOCK
+ ORB AX, 0(BX)
+ RET
+
// void jmpdefer(fn, sp);
// called from deferreturn.
// 1. pop the caller
// 2. sub 5 bytes from the callers return
// 3. jmp to the argument
-TEXT runtime·jmpdefer(SB), NOSPLIT, $0-16
- MOVL fn+0(FP), DX
- MOVL callersp+4(FP), BX
+TEXT runtime·jmpdefer(SB), NOSPLIT, $0-8
+ MOVL fv+0(FP), DX
+ MOVL argp+4(FP), BX
LEAL -8(BX), SP // caller sp after CALL
SUBL $5, (SP) // return to CALL again
MOVL 0(DX), BX
@@ -679,6 +700,12 @@ TEXT runtime·asmcgocall(SB),NOSPLIT,$0-8
MOVL 0, AX
RET
+// asmcgocall(void(*fn)(void*), void *arg)
+// Not implemented.
+TEXT runtime·asmcgocall_errno(SB),NOSPLIT,$0-12
+ MOVL 0, AX
+ RET
+
// cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
// Not implemented.
TEXT runtime·cgocallback(SB),NOSPLIT,$0-12
@@ -687,7 +714,7 @@ TEXT runtime·cgocallback(SB),NOSPLIT,$0-12
// void setg(G*); set g. for use by needm.
// Not implemented.
-TEXT runtime·setg(SB), NOSPLIT, $0-8
+TEXT runtime·setg(SB), NOSPLIT, $0-4
MOVL 0, AX
RET
@@ -718,9 +745,10 @@ TEXT runtime·memclr(SB),NOSPLIT,$0-8
STOSB
RET
-TEXT runtime·getcallerpc(SB),NOSPLIT,$0-8
- MOVL x+0(FP),AX // addr of first arg
+TEXT runtime·getcallerpc(SB),NOSPLIT,$0-12
+ MOVL argp+0(FP),AX // addr of first arg
MOVL -8(AX),AX // get calling pc
+ MOVL AX, ret+8(FP)
RET
TEXT runtime·gogetcallerpc(SB),NOSPLIT,$0-12
@@ -729,14 +757,21 @@ TEXT runtime·gogetcallerpc(SB),NOSPLIT,$0-12
MOVL AX, ret+8(FP)
RET
-TEXT runtime·setcallerpc(SB),NOSPLIT,$0-16
- MOVL x+0(FP),AX // addr of first arg
+TEXT runtime·setcallerpc(SB),NOSPLIT,$0-8
+ MOVL argp+0(FP),AX // addr of first arg
MOVL pc+4(FP), BX // pc to set
MOVQ BX, -8(AX) // set calling pc
RET
-TEXT runtime·getcallersp(SB),NOSPLIT,$0-8
- MOVL sp+0(FP), AX
+TEXT runtime·getcallersp(SB),NOSPLIT,$0-12
+ MOVL argp+0(FP), AX
+ MOVL AX, ret+8(FP)
+ RET
+
+// func gogetcallersp(p unsafe.Pointer) uintptr
+TEXT runtime·gogetcallersp(SB),NOSPLIT,$0-12
+ MOVL p+0(FP),AX // addr of first arg
+ MOVL AX, ret+8(FP)
RET
// int64 runtime·cputicks(void)
@@ -744,6 +779,14 @@ TEXT runtime·cputicks(SB),NOSPLIT,$0-0
RDTSC
SHLQ $32, DX
ADDQ DX, AX
+ MOVQ AX, ret+0(FP)
+ RET
+
+TEXT runtime·gocputicks(SB),NOSPLIT,$0-8
+ RDTSC
+ SHLQ $32, DX
+ ADDQ DX, AX
+ MOVQ AX, ret+0(FP)
RET
TEXT runtime·stackguard(SB),NOSPLIT,$0-8
@@ -763,25 +806,23 @@ GLOBL runtime·tls0(SB), $64
// write the implementations. Can copy and adjust the ones
// in asm_amd64.s when the time comes.
-TEXT runtime·aeshash(SB),NOSPLIT,$0-24
+TEXT runtime·aeshash(SB),NOSPLIT,$0-20
+ MOVL AX, ret+16(FP)
RET
-TEXT runtime·aeshashstr(SB),NOSPLIT,$0-24
+TEXT runtime·aeshashstr(SB),NOSPLIT,$0-20
+ MOVL AX, ret+16(FP)
RET
-TEXT runtime·aeshash32(SB),NOSPLIT,$0-24
+TEXT runtime·aeshash32(SB),NOSPLIT,$0-20
+ MOVL AX, ret+16(FP)
RET
-TEXT runtime·aeshash64(SB),NOSPLIT,$0-24
+TEXT runtime·aeshash64(SB),NOSPLIT,$0-20
+ MOVL AX, ret+16(FP)
RET
-TEXT runtime·memeq(SB),NOSPLIT,$0-12
- MOVL a+0(FP), SI
- MOVL b+4(FP), DI
- MOVL count+8(FP), BX
- JMP runtime·memeqbody(SB)
-
-TEXT runtime·gomemeq(SB),NOSPLIT,$0-17
+TEXT runtime·memeq(SB),NOSPLIT,$0-17
MOVL a+0(FP), SI
MOVL b+4(FP), DI
MOVL size+8(FP), BX
@@ -791,7 +832,7 @@ TEXT runtime·gomemeq(SB),NOSPLIT,$0-17
// eqstring tests whether two strings are equal.
// See runtime_test.go:eqstring_generic for
-// equivlaent Go code.
+// equivalent Go code.
TEXT runtime·eqstring(SB),NOSPLIT,$0-17
MOVL s1len+4(FP), AX
MOVL s2len+12(FP), BX
@@ -910,15 +951,15 @@ equal:
RET
TEXT runtime·cmpstring(SB),NOSPLIT,$0-20
- MOVL s1+0(FP), SI
- MOVL s1+4(FP), BX
- MOVL s2+8(FP), DI
- MOVL s2+12(FP), DX
+ MOVL s1_base+0(FP), SI
+ MOVL s1_len+4(FP), BX
+ MOVL s2_base+8(FP), DI
+ MOVL s2_len+12(FP), DX
CALL runtime·cmpbody(SB)
- MOVL AX, res+16(FP)
+ MOVL AX, ret+16(FP)
RET
-TEXT bytes·Compare(SB),NOSPLIT,$0-28
+TEXT runtime·cmpbytes(SB),NOSPLIT,$0-28
MOVL s1+0(FP), SI
MOVL s1+4(FP), BX
MOVL s2+12(FP), DI
@@ -1168,7 +1209,7 @@ eqret:
TEXT runtime·timenow(SB), NOSPLIT, $0-0
JMP time·now(SB)
-TEXT runtime·fastrand2(SB), NOSPLIT, $0-4
+TEXT runtime·fastrand1(SB), NOSPLIT, $0-4
get_tls(CX)
MOVL g(CX), AX
MOVL g_m(AX), AX
@@ -1181,37 +1222,6 @@ TEXT runtime·fastrand2(SB), NOSPLIT, $0-4
MOVL DX, ret+0(FP)
RET
-// The goeq trampoline is necessary while we have
-// both Go and C calls to alg functions. Once we move all call
-// sites to Go, we can redo the eq functions to use the
-// Go calling convention and remove this.
-
-// convert call to:
-// func (alg unsafe.Pointer, p, q unsafe.Pointer, size uintptr) bool
-// to:
-// func (eq *bool, size uintptr, p, q unsafe.Pointer)
-TEXT runtime·goeq(SB), NOSPLIT, $16-17
- FUNCDATA $FUNCDATA_ArgsPointerMaps,gcargs_goeq<>(SB)
- FUNCDATA $FUNCDATA_LocalsPointerMaps,gclocals_goeq<>(SB)
- MOVL alg+0(FP), AX
- MOVL alg_equal(AX), AX
- MOVL p+4(FP), CX
- MOVL q+8(FP), DX
- MOVL size+12(FP), DI
- LEAL ret+16(FP), SI
- MOVL SI, 0(SP)
- MOVL DI, 4(SP)
- MOVL CX, 8(SP)
- MOVL DX, 12(SP)
- PCDATA $PCDATA_StackMapIndex, $0
- CALL *AX
- RET
-
-DATA gcargs_goeq<>+0x00(SB)/4, $1 // 1 stackmap
-DATA gcargs_goeq<>+0x04(SB)/4, $10 // 5 args
-DATA gcargs_goeq<>+0x08(SB)/4, $(const_BitsPointer+(const_BitsPointer<<2)+(const_BitsPointer<<4))
-GLOBL gcargs_goeq<>(SB),RODATA,$12
-
-DATA gclocals_goeq<>+0x00(SB)/4, $1 // 1 stackmap
-DATA gclocals_goeq<>+0x04(SB)/4, $0 // 0 locals
-GLOBL gclocals_goeq<>(SB),RODATA,$8
+TEXT runtime·return0(SB), NOSPLIT, $0
+ MOVL $0, AX
+ RET
diff --git a/src/pkg/runtime/asm_arm.s b/src/pkg/runtime/asm_arm.s
index 406a426078..3db907945c 100644
--- a/src/pkg/runtime/asm_arm.s
+++ b/src/pkg/runtime/asm_arm.s
@@ -4,10 +4,10 @@
#include "zasm_GOOS_GOARCH.h"
#include "funcdata.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// using frame size $-4 means do not save LR on stack.
-TEXT _rt0_go(SB),NOSPLIT,$-4
+TEXT runtime·rt0_go(SB),NOSPLIT,$-4
MOVW $0xcafebabe, R12
// copy arguments forward on an even stack
@@ -64,7 +64,6 @@ nocgo:
MOVW R1, 8(R13)
BL runtime·args(SB)
BL runtime·osinit(SB)
- BL runtime·hashinit(SB)
BL runtime·schedinit(SB)
// create a new goroutine to start program
@@ -115,7 +114,7 @@ TEXT runtime·asminit(SB),NOSPLIT,$0-0
// void gosave(Gobuf*)
// save state in Gobuf; setjmp
-TEXT runtime·gosave(SB), NOSPLIT, $-4-4
+TEXT runtime·gosave(SB),NOSPLIT,$-4-4
MOVW 0(FP), R0 // gobuf
MOVW SP, gobuf_sp(R0)
MOVW LR, gobuf_pc(R0)
@@ -128,13 +127,21 @@ TEXT runtime·gosave(SB), NOSPLIT, $-4-4
// void gogo(Gobuf*)
// restore state from Gobuf; longjmp
-TEXT runtime·gogo(SB), NOSPLIT, $-4-4
+TEXT runtime·gogo(SB),NOSPLIT,$-4-4
MOVW 0(FP), R1 // gobuf
- MOVW gobuf_g(R1), g
- MOVW 0(g), R2 // make sure g != nil
- MOVB runtime·iscgo(SB), R2
- CMP $0, R2 // if in Cgo, we have to save g
- BL.NE runtime·save_g(SB) // this call will clobber R0
+ MOVW gobuf_g(R1), R0
+ BL setg<>(SB)
+
+ // NOTE: We updated g above, and we are about to update SP.
+ // Until LR and PC are also updated, the g/SP/LR/PC quadruple
+ // are out of sync and must not be used as the basis of a traceback.
+ // Sigprof skips the traceback when SP is not within g's bounds,
+ // and when the PC is inside this function, runtime.gogo.
+ // Since we are about to update SP, until we complete runtime.gogo
+ // we must not leave this function. In particular, no calls
+ // after this point: it must be straight-line code until the
+ // final B instruction.
+ // See large comment in sigprof for more details.
MOVW gobuf_sp(R1), SP // restore SP
MOVW gobuf_lr(R1), LR
MOVW gobuf_ret(R1), R0
@@ -144,15 +151,15 @@ TEXT runtime·gogo(SB), NOSPLIT, $-4-4
MOVW R11, gobuf_ret(R1)
MOVW R11, gobuf_lr(R1)
MOVW R11, gobuf_ctxt(R1)
- CMP R11, R11 // set condition codes for == test, needed by stack split
MOVW gobuf_pc(R1), R11
+ CMP R11, R11 // set condition codes for == test, needed by stack split
B (R11)
-// void mcall(void (*fn)(G*))
+// func 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, $-4-4
+TEXT runtime·mcall(SB),NOSPLIT,$-4-4
// Save caller state in g->sched.
MOVW SP, (g_sched+gobuf_sp)(g)
MOVW LR, (g_sched+gobuf_pc)(g)
@@ -163,7 +170,8 @@ TEXT runtime·mcall(SB), NOSPLIT, $-4-4
// Switch to m->g0 & its stack, call fn.
MOVW g, R1
MOVW g_m(g), R8
- MOVW m_g0(R8), g
+ MOVW m_g0(R8), R0
+ BL setg<>(SB)
CMP g, R1
B.NE 2(PC)
B runtime·badmcall(SB)
@@ -174,6 +182,8 @@ TEXT runtime·mcall(SB), NOSPLIT, $-4-4
MOVW (g_sched+gobuf_sp)(g), SP
SUB $8, SP
MOVW R1, 4(SP)
+ MOVW R0, R7
+ MOVW 0(R0), R0
BL (R0)
B runtime·badmcall2(SB)
RET
@@ -183,22 +193,30 @@ TEXT runtime·mcall(SB), NOSPLIT, $-4-4
// lives at the bottom of the G stack from the one that lives
// at the top of the M stack because the one at the top of
// the M stack terminates the stack walk (see topofstack()).
-TEXT runtime·switchtoM(SB), NOSPLIT, $0-4
+TEXT runtime·switchtoM(SB),NOSPLIT,$0-4
MOVW $0, R0
BL (R0) // clobber lr to ensure push {lr} is kept
RET
-// void onM(void (*fn)())
-// calls fn() on the M stack.
-// switches to the M stack if not already on it, and
-// switches back when fn() returns.
-TEXT runtime·onM(SB), NOSPLIT, $0-4
+// func onM(fn func())
+TEXT runtime·onM(SB),NOSPLIT,$0-4
MOVW fn+0(FP), R0 // R0 = fn
MOVW g_m(g), R1 // R1 = m
+
MOVW m_g0(R1), R2 // R2 = g0
CMP g, R2
B.EQ onm
-
+
+ MOVW m_curg(R1), R3
+ CMP g, R3
+ B.EQ oncurg
+
+ // Not g0, not curg. Must be gsignal, but that's not allowed.
+ // Hide call from linker nosplit analysis.
+ MOVW $runtime·badonm(SB), R0
+ BL (R0)
+
+oncurg:
// save our state in g->sched. Pretend to
// be switchtoM if the G stack is scanned.
MOVW $runtime·switchtoM(SB), R3
@@ -209,22 +227,35 @@ TEXT runtime·onM(SB), NOSPLIT, $0-4
MOVW g, (g_sched+gobuf_g)(g)
// switch to g0
- MOVW R2, g
- MOVW (g_sched+gobuf_sp)(R2), SP
+ MOVW R0, R5
+ MOVW R2, R0
+ BL setg<>(SB)
+ MOVW R5, R0
+ MOVW (g_sched+gobuf_sp)(R2), R3
+ // make it look like mstart called onM on g0, to stop traceback
+ SUB $4, R3, R3
+ MOVW $runtime·mstart(SB), R4
+ MOVW R4, 0(R3)
+ MOVW R3, SP
// call target function
ARGSIZE(0)
+ MOVW R0, R7
+ MOVW 0(R0), R0
BL (R0)
// switch back to g
MOVW g_m(g), R1
- MOVW m_curg(R1), g
+ MOVW m_curg(R1), R0
+ BL setg<>(SB)
MOVW (g_sched+gobuf_sp)(g), SP
MOVW $0, R3
MOVW R3, (g_sched+gobuf_sp)(g)
RET
onm:
+ MOVW R0, R7
+ MOVW 0(R0), R0
BL (R0)
RET
@@ -251,6 +282,11 @@ TEXT runtime·morestack(SB),NOSPLIT,$-4-0
CMP g, R4
BL.EQ runtime·abort(SB)
+ // Cannot grow signal stack (m->gsignal).
+ MOVW m_gsignal(R8), R4
+ CMP g, R4
+ BL.EQ runtime·abort(SB)
+
MOVW R1, m_moreframesize(R8)
MOVW R2, m_moreargsize(R8)
@@ -270,7 +306,8 @@ TEXT runtime·morestack(SB),NOSPLIT,$-4-0
MOVW g, (m_morebuf+gobuf_g)(R8)
// Call newstack on m->g0's stack.
- MOVW m_g0(R8), g
+ MOVW m_g0(R8), R0
+ BL setg<>(SB)
MOVW (g_sched+gobuf_sp)(g), SP
BL runtime·newstack(SB)
@@ -282,49 +319,8 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT,$-4-0
MOVW $0, R7
B runtime·morestack(SB)
-// Called from panic. Mimics morestack,
-// reuses stack growth code to create a frame
-// with the desired args running the desired function.
-//
-// func call(fn *byte, arg *byte, argsize uint32).
-TEXT runtime·newstackcall(SB), NOSPLIT, $-4-12
- // Save our caller's state as the PC and SP to
- // restore when returning from f.
- MOVW g_m(g), R8
- MOVW LR, (m_morebuf+gobuf_pc)(R8) // our caller's PC
- MOVW SP, (m_morebuf+gobuf_sp)(R8) // our caller's SP
- MOVW g, (m_morebuf+gobuf_g)(R8)
-
- // Save our own state as the PC and SP to restore
- // if this goroutine needs to be restarted.
- MOVW $runtime·newstackcall(SB), R11
- MOVW R11, (g_sched+gobuf_pc)(g)
- MOVW LR, (g_sched+gobuf_lr)(g)
- MOVW SP, (g_sched+gobuf_sp)(g)
-
- // Set up morestack arguments to call f on a new stack.
- // We set f's frame size to 1, as a hint to newstack
- // that this is a call from runtime·newstackcall.
- // If it turns out that f needs a larger frame than
- // the default stack, f's usual stack growth prolog will
- // allocate a new segment (and recopy the arguments).
- MOVW 4(SP), R0 // fn
- MOVW 8(SP), R1 // arg frame
- MOVW 12(SP), R2 // arg size
-
- MOVW R0, m_cret(R8) // f's PC
- MOVW R1, m_moreargp(R8) // f's argument pointer
- MOVW R2, m_moreargsize(R8) // f's argument size
- MOVW $1, R3
- MOVW R3, m_moreframesize(R8) // f's frame size
-
- // Call newstack on m->g0's stack.
- MOVW m_g0(R8), g
- MOVW (g_sched+gobuf_sp)(g), SP
- B runtime·newstack(SB)
-
// reflect·call: call a function with the given argument list
-// func call(f *FuncVal, arg *byte, argsize uint32).
+// func call(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!
@@ -332,10 +328,10 @@ TEXT runtime·newstackcall(SB), NOSPLIT, $-4-12
#define DISPATCH(NAME,MAXSIZE) \
CMP $MAXSIZE, R0; \
B.HI 3(PC); \
- MOVW $NAME(SB), R1; \
+ MOVW $NAME(SB), R1; \
B (R1)
-TEXT reflect·call(SB), NOSPLIT, $-4-16
+TEXT reflect·call(SB),NOSPLIT,$-4-16
MOVW argsize+8(FP), R0
DISPATCH(runtime·call16, 16)
DISPATCH(runtime·call32, 32)
@@ -367,11 +363,10 @@ TEXT reflect·call(SB), NOSPLIT, $-4-16
MOVW $runtime·badreflectcall(SB), R1
B (R1)
-// Argument map for the callXX frames. Each has one
-// stack map (for the single call) with 3 arguments.
+// Argument map for the callXX frames. Each has one stack map.
DATA gcargs_reflectcall<>+0x00(SB)/4, $1 // 1 stackmap
-DATA gcargs_reflectcall<>+0x04(SB)/4, $6 // 3 args
-DATA gcargs_reflectcall<>+0x08(SB)/4, $(const_BitsPointer+(const_BitsPointer<<2)+(const_BitsScalar<<4))
+DATA gcargs_reflectcall<>+0x04(SB)/4, $8 // 4 words
+DATA gcargs_reflectcall<>+0x08(SB)/1, $(const_BitsPointer+(const_BitsPointer<<2)+(const_BitsScalar<<4)+(const_BitsScalar<<6))
GLOBL gcargs_reflectcall<>(SB),RODATA,$12
// callXX frames have no locals
@@ -380,7 +375,7 @@ DATA gclocals_reflectcall<>+0x04(SB)/4, $0 // 0 locals
GLOBL gclocals_reflectcall<>(SB),RODATA,$8
#define CALLFN(NAME,MAXSIZE) \
-TEXT NAME(SB), WRAPPER, $MAXSIZE-16; \
+TEXT NAME(SB), WRAPPER, $MAXSIZE-16; \
FUNCDATA $FUNCDATA_ArgsPointerMaps,gcargs_reflectcall<>(SB); \
FUNCDATA $FUNCDATA_LocalsPointerMaps,gclocals_reflectcall<>(SB);\
/* copy arguments to stack */ \
@@ -446,13 +441,14 @@ CALLFN(runtime·call1073741824, 1073741824)
//
// Lessstack can appear in stack traces for the same reason
// as morestack; in that context, it has 0 arguments.
-TEXT runtime·lessstack(SB), NOSPLIT, $-4-0
+TEXT runtime·lessstack(SB),NOSPLIT,$-4-0
// Save return value in m->cret
MOVW g_m(g), R8
MOVW R0, m_cret(R8)
// Call oldstack on m->g0's stack.
- MOVW m_g0(R8), g
+ MOVW m_g0(R8), R0
+ BL setg<>(SB)
MOVW (g_sched+gobuf_sp)(g), SP
BL runtime·oldstack(SB)
@@ -465,10 +461,10 @@ TEXT runtime·lessstack(SB), NOSPLIT, $-4-0
// to load all registers simultaneously, so that a profiling
// interrupt can never see mismatched SP/LR/PC.
// (And double-check that pop is atomic in that way.)
-TEXT runtime·jmpdefer(SB), NOSPLIT, $0-8
+TEXT runtime·jmpdefer(SB),NOSPLIT,$0-8
MOVW 0(SP), LR
MOVW $-4(LR), LR // BL deferreturn
- MOVW fn+0(FP), R7
+ MOVW fv+0(FP), R7
MOVW argp+4(FP), SP
MOVW $-4(SP), SP // SP is 4 below argp, due to saved LR
MOVW 0(R7), R1
@@ -491,8 +487,20 @@ TEXT gosave<>(SB),NOSPLIT,$0
TEXT runtime·asmcgocall(SB),NOSPLIT,$0-8
MOVW fn+0(FP), R1
MOVW arg+4(FP), R0
+ BL asmcgocall<>(SB)
+ RET
+
+TEXT runtime·asmcgocall_errno(SB),NOSPLIT,$0-12
+ MOVW fn+0(FP), R1
+ MOVW arg+4(FP), R0
+ BL asmcgocall<>(SB)
+ MOVW R0, ret+8(FP)
+ RET
+
+TEXT asmcgocall<>(SB),NOSPLIT,$0-0
+ // fn in R1, arg in R0.
MOVW R13, R2
- MOVW g, R5
+ MOVW g, R4
// Figure out if we need to switch to m->g0 stack.
// We get called to create new OS threads too, and those
@@ -500,21 +508,27 @@ TEXT runtime·asmcgocall(SB),NOSPLIT,$0-8
MOVW g_m(g), R8
MOVW m_g0(R8), R3
CMP R3, g
- BEQ 4(PC)
+ BEQ asmcgocall_g0
BL gosave<>(SB)
- MOVW R3, g
+ MOVW R0, R5
+ MOVW R3, R0
+ BL setg<>(SB)
+ MOVW R5, R0
MOVW (g_sched+gobuf_sp)(g), R13
// Now on a scheduling stack (a pthread-created stack).
+asmcgocall_g0:
SUB $24, R13
BIC $0x7, R13 // alignment for gcc ABI
- MOVW R5, 20(R13) // save old g
+ MOVW R4, 20(R13) // save old g
MOVW R2, 16(R13) // save old SP
- // R0 already contains the first argument
BL (R1)
// Restore registers, g, stack pointer.
- MOVW 20(R13), g
+ MOVW R0, R5
+ MOVW 20(R13), R0
+ BL setg<>(SB)
+ MOVW R5, R0
MOVW 16(R13), R13
RET
@@ -579,10 +593,8 @@ havem:
// the earlier calls.
//
// In the new goroutine, -8(SP) and -4(SP) are unused.
- MOVW fn+4(FP), R0
- MOVW frame+8(FP), R1
- MOVW framesize+12(FP), R2
- MOVW m_curg(R8), g
+ MOVW m_curg(R8), R0
+ BL setg<>(SB)
MOVW (g_sched+gobuf_sp)(g), R4 // prepare stack as R4
MOVW (g_sched+gobuf_pc)(g), R5
MOVW R5, -12(R4)
@@ -599,7 +611,8 @@ havem:
// (Unlike m->curg, the g0 goroutine never uses sched.pc,
// so we do not have to restore it.)
MOVW g_m(g), R8
- MOVW m_g0(R8), g
+ MOVW m_g0(R8), R0
+ BL setg<>(SB)
MOVW (g_sched+gobuf_sp)(g), R13
MOVW savedsp-8(SP), R4
MOVW R4, (g_sched+gobuf_sp)(g)
@@ -616,18 +629,25 @@ havem:
RET
// void setg(G*); set g. for use by needm.
-TEXT runtime·setg(SB), NOSPLIT, $0-8
- MOVW gg+0(FP), g
+TEXT runtime·setg(SB),NOSPLIT,$-4-4
+ MOVW gg+0(FP), R0
+ B setg<>(SB)
+
+TEXT setg<>(SB),NOSPLIT,$-4-0
+ MOVW R0, g
// Save g to thread-local storage.
MOVB runtime·iscgo(SB), R0
CMP $0, R0
- BL.NE runtime·save_g(SB)
+ B.EQ 2(PC)
+ B runtime·save_g(SB)
+ MOVW g, R0
RET
TEXT runtime·getcallerpc(SB),NOSPLIT,$-4-4
MOVW 0(SP), R0
+ MOVW R0, ret+4(FP)
RET
TEXT runtime·gogetcallerpc(SB),NOSPLIT,$-4-8
@@ -635,13 +655,21 @@ TEXT runtime·gogetcallerpc(SB),NOSPLIT,$-4-8
RET
TEXT runtime·setcallerpc(SB),NOSPLIT,$-4-8
- MOVW x+4(FP), R0
+ MOVW pc+4(FP), R0
MOVW R0, 0(SP)
RET
TEXT runtime·getcallersp(SB),NOSPLIT,$-4-4
MOVW 0(FP), R0
MOVW $-4(R0), R0
+ MOVW R0, ret+4(FP)
+ RET
+
+// func gogetcallersp(p unsafe.Pointer) uintptr
+TEXT runtime·gogetcallersp(SB),NOSPLIT,$-4-8
+ MOVW 0(FP), R0
+ MOVW $-4(R0), R0
+ MOVW R0, ret+4(FP)
RET
TEXT runtime·emptyfunc(SB),0,$0-0
@@ -665,7 +693,7 @@ TEXT runtime·abort(SB),NOSPLIT,$-4-0
// TEXT runtime·cas(SB),NOSPLIT,$0
// B runtime·armcas(SB)
//
-TEXT runtime·armcas(SB),NOSPLIT,$0-12
+TEXT runtime·armcas(SB),NOSPLIT,$0-13
MOVW valptr+0(FP), R1
MOVW old+4(FP), R2
MOVW new+8(FP), R3
@@ -677,11 +705,22 @@ casl:
CMP $0, R0
BNE casl
MOVW $1, R0
+ MOVB R0, ret+12(FP)
RET
casfail:
MOVW $0, R0
+ MOVB R0, ret+12(FP)
RET
+TEXT runtime·casuintptr(SB),NOSPLIT,$0-13
+ B runtime·cas(SB)
+
+TEXT runtime·atomicloaduintptr(SB),NOSPLIT,$0-8
+ B runtime·atomicload(SB)
+
+TEXT runtime·atomicloaduint(SB),NOSPLIT,$0-8
+ B runtime·atomicload(SB)
+
TEXT runtime·stackguard(SB),NOSPLIT,$0-8
MOVW R13, R1
MOVW g_stackguard(g), R2
@@ -703,24 +742,7 @@ TEXT runtime·aeshashstr(SB),NOSPLIT,$-4-0
MOVW $0, R0
MOVW (R0), R1
-TEXT runtime·memeq(SB),NOSPLIT,$-4-12
- MOVW a+0(FP), R1
- MOVW b+4(FP), R2
- MOVW n+8(FP), R3
- ADD R1, R3, R6
- MOVW $1, R0
-_next:
- CMP R1, R6
- RET.EQ
- MOVBU.P 1(R1), R4
- MOVBU.P 1(R2), R5
- CMP R4, R5
- BEQ _next
-
- MOVW $0, R0
- RET
-
-TEXT runtime·gomemeq(SB),NOSPLIT,$-4-13
+TEXT runtime·memeq(SB),NOSPLIT,$-4-13
MOVW a+0(FP), R1
MOVW b+4(FP), R2
MOVW size+8(FP), R3
@@ -741,7 +763,7 @@ _next2:
// eqstring tests whether two strings are equal.
// See runtime_test.go:eqstring_generic for
-// equivlaent Go code.
+// equivalent Go code.
TEXT runtime·eqstring(SB),NOSPLIT,$-4-17
MOVW s1len+4(FP), R0
MOVW s2len+12(FP), R1
@@ -849,7 +871,7 @@ _sib_notfound:
MOVW R0, ret+12(FP)
RET
-TEXT runtime·timenow(SB), NOSPLIT, $0-0
+TEXT runtime·timenow(SB),NOSPLIT,$0-0
B time·now(SB)
// A Duff's device for zeroing memory.
@@ -860,7 +882,7 @@ TEXT runtime·timenow(SB), NOSPLIT, $0-0
// R0: zero
// R1: ptr to memory to be zeroed
// R1 is updated as a side effect.
-TEXT runtime·duffzero(SB), NOSPLIT, $0-0
+TEXT runtime·duffzero(SB),NOSPLIT,$0-0
MOVW.P R0, 4(R1)
MOVW.P R0, 4(R1)
MOVW.P R0, 4(R1)
@@ -1001,7 +1023,7 @@ TEXT runtime·duffzero(SB), NOSPLIT, $0-0
// R1: ptr to source memory
// R2: ptr to destination memory
// R1 and R2 are updated as a side effect
-TEXT runtime·duffcopy(SB), NOSPLIT, $0-0
+TEXT runtime·duffcopy(SB),NOSPLIT,$0-0
MOVW.P 4(R1), R0
MOVW.P R0, 4(R2)
MOVW.P 4(R1), R0
@@ -1260,7 +1282,7 @@ TEXT runtime·duffcopy(SB), NOSPLIT, $0-0
MOVW.P R0, 4(R2)
RET
-TEXT runtime·fastrand2(SB), NOSPLIT, $-4-4
+TEXT runtime·fastrand1(SB),NOSPLIT,$-4-4
MOVW g_m(g), R1
MOVW m_fastrand(R1), R0
ADD.S R0, R0
@@ -1269,37 +1291,19 @@ TEXT runtime·fastrand2(SB), NOSPLIT, $-4-4
MOVW R0, ret+0(FP)
RET
-// The goeq trampoline is necessary while we have
-// both Go and C calls to alg functions. Once we move all call
-// sites to Go, we can redo the eq functions to use the
-// Go calling convention and remove this.
-
-// convert call to:
-// func (alg unsafe.Pointer, p, q unsafe.Pointer, size uintptr) bool
-// to:
-// func (eq *bool, size uintptr, p, q unsafe.Pointer)
-TEXT runtime·goeq(SB), NOSPLIT, $16-17
- FUNCDATA $FUNCDATA_ArgsPointerMaps,gcargs_goeq<>(SB)
- FUNCDATA $FUNCDATA_LocalsPointerMaps,gclocals_goeq<>(SB)
- MOVW alg+0(FP), R0
- MOVW alg_equal(R0), R0
- MOVW p+4(FP), R1
- MOVW q+8(FP), R2
- MOVW size+12(FP), R3
- ADD $40, R13, R4
- MOVW R4, 4(R13)
- MOVW R3, 8(R13)
- MOVW R2, 12(R13)
- MOVW R1, 16(R13)
- PCDATA $PCDATA_StackMapIndex, $0
- BL (R0)
- RET
+TEXT runtime·gocputicks(SB),NOSPLIT,$0
+ B runtime·cputicks(SB)
-DATA gcargs_goeq<>+0x00(SB)/4, $1 // 1 stackmap
-DATA gcargs_goeq<>+0x04(SB)/4, $10 // 5 args
-DATA gcargs_goeq<>+0x08(SB)/4, $(const_BitsPointer+(const_BitsPointer<<2)+(const_BitsPointer<<4))
-GLOBL gcargs_goeq<>(SB),RODATA,$12
+TEXT runtime·return0(SB),NOSPLIT,$0
+ MOVW $0, R0
+ RET
-DATA gclocals_goeq<>+0x00(SB)/4, $1 // 1 stackmap
-DATA gclocals_goeq<>+0x04(SB)/4, $0 // 0 locals
-GLOBL gclocals_goeq<>(SB),RODATA,$8
+TEXT runtime·procyield(SB),NOSPLIT,$-4
+ MOVW cycles+0(FP), R1
+ MOVW $0, R0
+yieldloop:
+ CMP R0, R1
+ B.NE 2(PC)
+ RET
+ SUB $1, R1
+ B yieldloop
diff --git a/src/pkg/runtime/atomic.go b/src/pkg/runtime/atomic.go
new file mode 100644
index 0000000000..7e9d9b3aad
--- /dev/null
+++ b/src/pkg/runtime/atomic.go
@@ -0,0 +1,51 @@
+// 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 !arm
+
+package runtime
+
+import "unsafe"
+
+//go:noescape
+func xadd(ptr *uint32, delta int32) uint32
+
+//go:noescape
+func xadd64(ptr *uint64, delta int64) uint64
+
+//go:noescape
+func xchg(ptr *uint32, new uint32) uint32
+
+//go:noescape
+func xchg64(ptr *uint64, new uint64) uint64
+
+//go:noescape
+func xchgp(ptr unsafe.Pointer, new unsafe.Pointer) unsafe.Pointer
+
+//go:noescape
+func xchguintptr(ptr *uintptr, new uintptr) uintptr
+
+//go:noescape
+func atomicload(ptr *uint32) uint32
+
+//go:noescape
+func atomicload64(ptr *uint64) uint64
+
+//go:noescape
+func atomicloadp(ptr unsafe.Pointer) unsafe.Pointer
+
+//go:noescape
+func atomicor8(ptr *uint8, val uint8)
+
+//go:noescape
+func cas64(ptr *uint64, old, new uint64) bool
+
+//go:noescape
+func atomicstore(ptr *uint32, val uint32)
+
+//go:noescape
+func atomicstore64(ptr *uint64, val uint64)
+
+//go:noescape
+func atomicstorep(ptr unsafe.Pointer, val unsafe.Pointer)
diff --git a/src/pkg/runtime/atomic_386.c b/src/pkg/runtime/atomic_386.c
index d7162a1b8b..82d36f2d96 100644
--- a/src/pkg/runtime/atomic_386.c
+++ b/src/pkg/runtime/atomic_386.c
@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
#include "runtime.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
#pragma textflag NOSPLIT
uint32
diff --git a/src/pkg/runtime/atomic_amd64x.c b/src/pkg/runtime/atomic_amd64x.c
index 11b5789363..7be57ac95a 100644
--- a/src/pkg/runtime/atomic_amd64x.c
+++ b/src/pkg/runtime/atomic_amd64x.c
@@ -5,7 +5,7 @@
// +build amd64 amd64p32
#include "runtime.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
#pragma textflag NOSPLIT
uint32
diff --git a/src/pkg/runtime/atomic_arm.c b/src/pkg/runtime/atomic_arm.c
deleted file mode 100644
index d914475c7f..0000000000
--- a/src/pkg/runtime/atomic_arm.c
+++ /dev/null
@@ -1,169 +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.
-
-#include "runtime.h"
-#include "arch_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
-
-static struct {
- Lock l;
- byte pad[CacheLineSize-sizeof(Lock)];
-} locktab[57];
-
-#define LOCK(addr) (&locktab[((uintptr)(addr)>>3)%nelem(locktab)].l)
-
-// Atomic add and return new value.
-#pragma textflag NOSPLIT
-uint32
-runtime·xadd(uint32 volatile *val, int32 delta)
-{
- uint32 oval, nval;
-
- for(;;){
- oval = *val;
- nval = oval + delta;
- if(runtime·cas(val, oval, nval))
- return nval;
- }
-}
-
-#pragma textflag NOSPLIT
-uint32
-runtime·xchg(uint32 volatile* addr, uint32 v)
-{
- uint32 old;
-
- for(;;) {
- old = *addr;
- if(runtime·cas(addr, old, v))
- return old;
- }
-}
-
-#pragma textflag NOSPLIT
-void*
-runtime·xchgp(void* volatile* addr, void* v)
-{
- void *old;
-
- for(;;) {
- old = *addr;
- if(runtime·casp(addr, old, v))
- return old;
- }
-}
-
-#pragma textflag NOSPLIT
-void
-runtime·procyield(uint32 cnt)
-{
- uint32 volatile i;
-
- for(i = 0; i < cnt; i++) {
- }
-}
-
-#pragma textflag NOSPLIT
-uint32
-runtime·atomicload(uint32 volatile* addr)
-{
- return runtime·xadd(addr, 0);
-}
-
-#pragma textflag NOSPLIT
-void*
-runtime·atomicloadp(void* volatile* addr)
-{
- return (void*)runtime·xadd((uint32 volatile*)addr, 0);
-}
-
-#pragma textflag NOSPLIT
-void
-runtime·atomicstorep(void* volatile* addr, void* v)
-{
- void *old;
-
- for(;;) {
- old = *addr;
- if(runtime·casp(addr, old, v))
- return;
- }
-}
-
-#pragma textflag NOSPLIT
-void
-runtime·atomicstore(uint32 volatile* addr, uint32 v)
-{
- uint32 old;
-
- for(;;) {
- old = *addr;
- if(runtime·cas(addr, old, v))
- return;
- }
-}
-
-#pragma textflag NOSPLIT
-bool
-runtime·cas64(uint64 volatile *addr, uint64 old, uint64 new)
-{
- bool res;
-
- runtime·lock(LOCK(addr));
- if(*addr == old) {
- *addr = new;
- res = true;
- } else {
- res = false;
- }
- runtime·unlock(LOCK(addr));
- return res;
-}
-
-#pragma textflag NOSPLIT
-uint64
-runtime·xadd64(uint64 volatile *addr, int64 delta)
-{
- uint64 res;
-
- runtime·lock(LOCK(addr));
- res = *addr + delta;
- *addr = res;
- runtime·unlock(LOCK(addr));
- return res;
-}
-
-#pragma textflag NOSPLIT
-uint64
-runtime·xchg64(uint64 volatile *addr, uint64 v)
-{
- uint64 res;
-
- runtime·lock(LOCK(addr));
- res = *addr;
- *addr = v;
- runtime·unlock(LOCK(addr));
- return res;
-}
-
-#pragma textflag NOSPLIT
-uint64
-runtime·atomicload64(uint64 volatile *addr)
-{
- uint64 res;
-
- runtime·lock(LOCK(addr));
- res = *addr;
- runtime·unlock(LOCK(addr));
- return res;
-}
-
-#pragma textflag NOSPLIT
-void
-runtime·atomicstore64(uint64 volatile *addr, uint64 v)
-{
- runtime·lock(LOCK(addr));
- *addr = v;
- runtime·unlock(LOCK(addr));
-}
diff --git a/src/pkg/runtime/atomic_arm.go b/src/pkg/runtime/atomic_arm.go
new file mode 100644
index 0000000000..b1632cdd16
--- /dev/null
+++ b/src/pkg/runtime/atomic_arm.go
@@ -0,0 +1,155 @@
+// 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"
+
+var locktab [57]struct {
+ l mutex
+ pad [_CacheLineSize - unsafe.Sizeof(mutex{})]byte
+}
+
+func addrLock(addr *uint64) *mutex {
+ return &locktab[(uintptr(unsafe.Pointer(addr))>>3)%uintptr(len(locktab))].l
+}
+
+// Atomic add and return new value.
+//go:nosplit
+func xadd(val *uint32, delta int32) uint32 {
+ for {
+ oval := *val
+ nval := oval + uint32(delta)
+ if cas(val, oval, nval) {
+ return nval
+ }
+ }
+}
+
+//go:nosplit
+func xchg(addr *uint32, v uint32) uint32 {
+ for {
+ old := *addr
+ if cas(addr, old, v) {
+ return old
+ }
+ }
+}
+
+//go:nosplit
+func xchgp(addr *unsafe.Pointer, v unsafe.Pointer) unsafe.Pointer {
+ for {
+ old := *addr
+ if casp(addr, old, v) {
+ return old
+ }
+ }
+}
+
+//go:nosplit
+func xchguintptr(addr *uintptr, v uintptr) uintptr {
+ return uintptr(xchg((*uint32)(unsafe.Pointer(addr)), uint32(v)))
+}
+
+//go:nosplit
+func atomicload(addr *uint32) uint32 {
+ return xadd(addr, 0)
+}
+
+//go:nosplit
+func atomicloadp(addr unsafe.Pointer) unsafe.Pointer {
+ return unsafe.Pointer(uintptr(xadd((*uint32)(addr), 0)))
+}
+
+//go:nosplit
+func atomicstorep(addr unsafe.Pointer, v unsafe.Pointer) {
+ for {
+ old := *(*unsafe.Pointer)(addr)
+ if casp((*unsafe.Pointer)(addr), old, v) {
+ return
+ }
+ }
+}
+
+//go:nosplit
+func atomicstore(addr *uint32, v uint32) {
+ for {
+ old := *addr
+ if cas(addr, old, v) {
+ return
+ }
+ }
+}
+
+//go:nosplit
+func cas64(addr *uint64, old, new uint64) bool {
+ var ok bool
+ onM(func() {
+ lock(addrLock(addr))
+ if *addr == old {
+ *addr = new
+ ok = true
+ }
+ unlock(addrLock(addr))
+ })
+ return ok
+}
+
+//go:nosplit
+func xadd64(addr *uint64, delta int64) uint64 {
+ var r uint64
+ onM(func() {
+ lock(addrLock(addr))
+ r = *addr + uint64(delta)
+ *addr = r
+ unlock(addrLock(addr))
+ })
+ return r
+}
+
+//go:nosplit
+func xchg64(addr *uint64, v uint64) uint64 {
+ var r uint64
+ onM(func() {
+ lock(addrLock(addr))
+ r = *addr
+ *addr = v
+ unlock(addrLock(addr))
+ })
+ return r
+}
+
+//go:nosplit
+func atomicload64(addr *uint64) uint64 {
+ var r uint64
+ onM(func() {
+ lock(addrLock(addr))
+ r = *addr
+ unlock(addrLock(addr))
+ })
+ return r
+}
+
+//go:nosplit
+func atomicstore64(addr *uint64, v uint64) {
+ onM(func() {
+ lock(addrLock(addr))
+ *addr = v
+ unlock(addrLock(addr))
+ })
+}
+
+//go:nosplit
+func atomicor8(addr *uint8, v uint8) {
+ // Align down to 4 bytes and use 32-bit CAS.
+ uaddr := uintptr(unsafe.Pointer(addr))
+ addr32 := (*uint32)(unsafe.Pointer(uaddr &^ 3))
+ word := uint32(v) << ((uaddr & 3) * 8) // little endian
+ for {
+ old := *addr32
+ if cas(addr32, old, old|word) {
+ return
+ }
+ }
+}
diff --git a/src/pkg/runtime/callback_windows.c b/src/pkg/runtime/callback_windows.c
deleted file mode 100644
index 285678fbac..0000000000
--- a/src/pkg/runtime/callback_windows.c
+++ /dev/null
@@ -1,77 +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.
-
-#include "runtime.h"
-#include "type.h"
-#include "typekind.h"
-#include "defs_GOOS_GOARCH.h"
-#include "os_GOOS.h"
-#include "zasm_GOOS_GOARCH.h"
-
-typedef struct Callbacks Callbacks;
-struct Callbacks {
- Lock;
- WinCallbackContext* ctxt[cb_max];
- int32 n;
-};
-
-static Callbacks cbs;
-
-WinCallbackContext** runtime·cbctxts; // to simplify access to cbs.ctxt in sys_windows_*.s
-
-// Call back from windows dll into go.
-byte *
-runtime·compilecallback(Eface fn, bool cleanstack)
-{
- FuncType *ft;
- Type *t;
- int32 argsize, i, n;
- WinCallbackContext *c;
-
- if(fn.type == nil || fn.type->kind != KindFunc)
- runtime·panicstring("compilecallback: not a function");
- ft = (FuncType*)fn.type;
- if(ft->out.len != 1)
- runtime·panicstring("compilecallback: function must have one output parameter");
- if(((Type**)ft->out.array)[0]->size != sizeof(uintptr))
- runtime·panicstring("compilecallback: output parameter size is wrong");
- argsize = 0;
- for(i=0; i<ft->in.len; i++) {
- t = ((Type**)ft->in.array)[i];
- if(t->size > sizeof(uintptr))
- runtime·panicstring("compilecallback: input parameter size is wrong");
- argsize += sizeof(uintptr);
- }
-
- runtime·lock(&cbs);
- if(runtime·cbctxts == nil)
- runtime·cbctxts = &(cbs.ctxt[0]);
- n = cbs.n;
- for(i=0; i<n; i++) {
- if(cbs.ctxt[i]->gobody == fn.data && cbs.ctxt[i]->cleanstack == cleanstack) {
- runtime·unlock(&cbs);
- // runtime·callbackasm is just a series of CALL instructions
- // (each is 5 bytes long), and we want callback to arrive at
- // correspondent call instruction instead of start of
- // runtime·callbackasm.
- return (byte*)runtime·callbackasm + i * 5;
- }
- }
- if(n >= cb_max)
- runtime·throw("too many callback functions");
- c = runtime·mal(sizeof *c);
- c->gobody = fn.data;
- c->argsize = argsize;
- c->cleanstack = cleanstack;
- if(cleanstack && argsize!=0)
- c->restorestack = argsize;
- else
- c->restorestack = 0;
- cbs.ctxt[n] = c;
- cbs.n++;
- runtime·unlock(&cbs);
-
- // as before
- return (byte*)runtime·callbackasm + n * 5;
-}
diff --git a/src/pkg/runtime/cgo/asm_386.s b/src/pkg/runtime/cgo/asm_386.s
index ab2f1d17a8..a895083f1b 100644
--- a/src/pkg/runtime/cgo/asm_386.s
+++ b/src/pkg/runtime/cgo/asm_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
/*
* void crosscall2(void (*fn)(void*, int32), void*, int32)
diff --git a/src/pkg/runtime/cgo/asm_amd64.s b/src/pkg/runtime/cgo/asm_amd64.s
index 64f719ab1c..6095bd1337 100644
--- a/src/pkg/runtime/cgo/asm_amd64.s
+++ b/src/pkg/runtime/cgo/asm_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
/*
* void crosscall2(void (*fn)(void*, int32), void*, int32)
diff --git a/src/pkg/runtime/cgo/asm_arm.s b/src/pkg/runtime/cgo/asm_arm.s
index b989ab9330..6e57432e35 100644
--- a/src/pkg/runtime/cgo/asm_arm.s
+++ b/src/pkg/runtime/cgo/asm_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
/*
* void crosscall2(void (*fn)(void*, int32), void*, int32)
diff --git a/src/pkg/runtime/cgo/asm_nacl_amd64p32.s b/src/pkg/runtime/cgo/asm_nacl_amd64p32.s
index 377cf72a3a..eb92014ed8 100644
--- a/src/pkg/runtime/cgo/asm_nacl_amd64p32.s
+++ b/src/pkg/runtime/cgo/asm_nacl_amd64p32.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
/*
* void crosscall2(void (*fn)(void*, int32), void*, int32)
diff --git a/src/pkg/runtime/cgo/callbacks.c b/src/pkg/runtime/cgo/callbacks.c
index 5a4889c9b3..f074237d5f 100644
--- a/src/pkg/runtime/cgo/callbacks.c
+++ b/src/pkg/runtime/cgo/callbacks.c
@@ -4,7 +4,7 @@
#include "../runtime.h"
#include "../cgocall.h"
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// These utility functions are available to be called from code
// compiled with gcc via crosscall2.
@@ -38,8 +38,8 @@ _cgo_allocate_internal(uintptr len, byte *ret)
{
CgoMal *c;
- ret = runtime·mal(len);
- c = runtime·mal(sizeof(*c));
+ ret = runtime·mallocgc(len, nil, 0);
+ c = runtime·mallocgc(sizeof(*c), nil, 0);
c->next = g->m->cgomal;
c->alloc = ret;
g->m->cgomal = c;
@@ -73,7 +73,7 @@ _cgo_panic_internal(byte *p)
s = runtime·gostring(p);
·cgoStringToEface(s, &err);
- runtime·panic(err);
+ runtime·gopanic(err);
}
#pragma cgo_export_static _cgo_panic
diff --git a/src/pkg/runtime/cgo/setenv.c b/src/pkg/runtime/cgo/setenv.c
index 2d03db09f1..ee529904f7 100644
--- a/src/pkg/runtime/cgo/setenv.c
+++ b/src/pkg/runtime/cgo/setenv.c
@@ -7,4 +7,4 @@
#pragma cgo_import_static x_cgo_setenv
void x_cgo_setenv(char**);
-void (*_cgo_setenv)(char**) = x_cgo_setenv;
+void (*runtime·_cgo_setenv)(char**) = x_cgo_setenv;
diff --git a/src/pkg/runtime/cgocall.c b/src/pkg/runtime/cgocall.go
index 2149d7c723..76a533e933 100644
--- a/src/pkg/runtime/cgocall.c
+++ b/src/pkg/runtime/cgocall.go
@@ -2,13 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "runtime.h"
-#include "arch_GOARCH.h"
-#include "stack.h"
-#include "cgocall.h"
-#include "race.h"
-#include "../../cmd/ld/textflag.h"
-
// Cgo call and callback support.
//
// To call into the C function f from Go, the cgo-generated code calls
@@ -17,7 +10,7 @@
//
// runtime.cgocall (below) locks g to m, calls entersyscall
// so as not to block other goroutines or the garbage collector,
-// and then calls runtime.asmcgocall(_cgo_Cfunc_f, frame).
+// and then calls runtime.asmcgocall(_cgo_Cfunc_f, frame).
//
// runtime.asmcgocall (in asm_$GOARCH.s) switches to the m->g0 stack
// (assumed to be an operating system-allocated stack, so safe to run
@@ -84,49 +77,44 @@
// _cgoexp_GoF immediately returns to crosscall2, which restores the
// callee-save registers for gcc and returns to GoF, which returns to f.
-void *_cgo_init; /* filled in by dynamic linker when Cgo is available */
-static int64 cgosync; /* represents possible synchronization in C code */
+package runtime
-static void unwindm(void);
+import "unsafe"
// Call from Go to C.
+//go:nosplit
+func cgocall(fn, arg unsafe.Pointer) {
+ cgocall_errno(fn, arg)
+}
-static void endcgo(void);
-static FuncVal endcgoV = { endcgo };
-
-void
-runtime·cgocall(void (*fn)(void*), void *arg)
-{
- Defer d;
-
- if(!runtime·iscgo && !Solaris && !Windows)
- runtime·throw("cgocall unavailable");
+//go:nosplit
+func cgocall_errno(fn, arg unsafe.Pointer) int32 {
+ if !iscgo && GOOS != "solaris" && GOOS != "windows" {
+ gothrow("cgocall unavailable")
+ }
- if(fn == 0)
- runtime·throw("cgocall nil");
+ if fn == nil {
+ gothrow("cgocall nil")
+ }
- if(raceenabled)
- runtime·racereleasemerge(&cgosync);
+ if raceenabled {
+ racereleasemerge(unsafe.Pointer(&racecgosync))
+ }
// Create an extra M for callbacks on threads not created by Go on first cgo call.
- if(runtime·needextram && runtime·cas(&runtime·needextram, 1, 0))
- runtime·newextram();
-
- g->m->ncgocall++;
+ if needextram == 1 && cas(&needextram, 1, 0) {
+ onM(newextram)
+ }
/*
* Lock g to m to ensure we stay on the same stack if we do a
* cgo callback. Add entry to defer stack in case of panic.
*/
- runtime·lockOSThread();
- d.fn = &endcgoV;
- d.siz = 0;
- d.link = g->defer;
- d.argp = NoArgs;
- d.special = true;
- g->defer = &d;
-
- g->m->ncgo++;
+ lockOSThread()
+ mp := getg().m
+ mp.ncgocall++
+ mp.ncgo++
+ defer endcgo(mp)
/*
* Announce we are entering a system call
@@ -139,188 +127,144 @@ runtime·cgocall(void (*fn)(void*), void *arg)
* so it is safe to call while "in a system call", outside
* the $GOMAXPROCS accounting.
*/
- runtime·entersyscall();
- runtime·asmcgocall(fn, arg);
- runtime·exitsyscall();
-
- if(g->defer != &d || d.fn != &endcgoV)
- runtime·throw("runtime: bad defer entry in cgocallback");
- g->defer = d.link;
- endcgo();
+ entersyscall()
+ errno := asmcgocall_errno(fn, arg)
+ exitsyscall()
+
+ return errno
}
-static void
-endcgo(void)
-{
- runtime·unlockOSThread();
- g->m->ncgo--;
- if(g->m->ncgo == 0) {
+func endcgo(mp *m) {
+ mp.ncgo--
+ if mp.ncgo == 0 {
// We are going back to Go and are not in a recursive
// call. Let the GC collect any memory allocated via
// _cgo_allocate that is no longer referenced.
- g->m->cgomal = nil;
+ mp.cgomal = nil
}
- if(raceenabled)
- runtime·raceacquire(&cgosync);
+ if raceenabled {
+ raceacquire(unsafe.Pointer(&racecgosync))
+ }
+
+ unlockOSThread() // invalidates mp
}
// Helper functions for cgo code.
-void (*_cgo_malloc)(void*);
-void (*_cgo_free)(void*);
-
-void*
-runtime·cmalloc(uintptr n)
-{
- struct {
- uint64 n;
- void *ret;
- } a;
-
- a.n = n;
- a.ret = nil;
- runtime·cgocall(_cgo_malloc, &a);
- if(a.ret == nil)
- runtime·throw("runtime: C malloc failed");
- return a.ret;
+// Filled by schedinit from corresponding C variables,
+// which are in turn filled in by dynamic linker when Cgo is available.
+var cgoMalloc, cgoFree unsafe.Pointer
+
+func cmalloc(n uintptr) unsafe.Pointer {
+ var args struct {
+ n uint64
+ ret unsafe.Pointer
+ }
+ args.n = uint64(n)
+ cgocall(cgoMalloc, unsafe.Pointer(&args))
+ if args.ret == nil {
+ gothrow("C malloc failed")
+ }
+ return args.ret
}
-void
-runtime·cfree(void *p)
-{
- runtime·cgocall(_cgo_free, p);
+func cfree(p unsafe.Pointer) {
+ cgocall(cgoFree, p)
}
// Call from C back to Go.
-
-static FuncVal unwindmf = {unwindm};
-
-typedef struct CallbackArgs CallbackArgs;
-struct CallbackArgs
-{
- FuncVal *fn;
- void *arg;
- uintptr argsize;
-};
-
-// Location of callback arguments depends on stack frame layout
-// and size of stack frame of cgocallback_gofunc.
-
-// On arm, stack frame is two words and there's a saved LR between
-// SP and the stack frame and between the stack frame and the arguments.
-#ifdef GOARCH_arm
-#define CBARGS (CallbackArgs*)((byte*)g->m->g0->sched.sp+4*sizeof(void*))
-#endif
-
-// On amd64, stack frame is one word, plus caller PC.
-#ifdef GOARCH_amd64
-#define CBARGS (CallbackArgs*)((byte*)g->m->g0->sched.sp+2*sizeof(void*))
-#endif
-
-// Unimplemented on amd64p32
-#ifdef GOARCH_amd64p32
-#define CBARGS (CallbackArgs*)(nil)
-#endif
-
-// On 386, stack frame is three words, plus caller PC.
-#ifdef GOARCH_386
-#define CBARGS (CallbackArgs*)((byte*)g->m->g0->sched.sp+4*sizeof(void*))
-#endif
-
-// Unimplemented on power64 or power64le
-#ifdef GOARCH_power64
-#define CBARGS (CallbackArgs*)(nil)
-#endif
-#ifdef GOARCH_power64le
-#define CBARGS (CallbackArgs*)(nil)
-#endif
-
-void runtime·cgocallbackg1(void);
-
-#pragma textflag NOSPLIT
-void
-runtime·cgocallbackg(void)
-{
- if(g != g->m->curg) {
- runtime·prints("runtime: bad g in cgocallback");
- runtime·exit(2);
+//go:nosplit
+func cgocallbackg() {
+ if gp := getg(); gp != gp.m.curg {
+ println("runtime: bad g in cgocallback")
+ exit(2)
}
- runtime·exitsyscall(); // coming out of cgo call
- runtime·cgocallbackg1();
- runtime·entersyscall(); // going back to cgo call
+ exitsyscall() // coming out of cgo call
+ cgocallbackg1()
+ entersyscall() // going back to cgo call
}
-void
-runtime·cgocallbackg1(void)
-{
- CallbackArgs *cb;
- Defer d;
-
- if(g->m->needextram) {
- g->m->needextram = 0;
- runtime·newextram();
+func cgocallbackg1() {
+ gp := getg()
+ if gp.m.needextram {
+ gp.m.needextram = false
+ onM(newextram)
}
// Add entry to defer stack in case of panic.
- d.fn = &unwindmf;
- d.siz = 0;
- d.link = g->defer;
- d.argp = NoArgs;
- d.special = true;
- g->defer = &d;
+ restore := true
+ defer unwindm(&restore)
+
+ if raceenabled {
+ raceacquire(unsafe.Pointer(&racecgosync))
+ }
- if(raceenabled)
- runtime·raceacquire(&cgosync);
+ type args struct {
+ fn *funcval
+ arg unsafe.Pointer
+ argsize uintptr
+ }
+ var cb *args
+
+ // Location of callback arguments depends on stack frame layout
+ // and size of stack frame of cgocallback_gofunc.
+ sp := gp.m.g0.sched.sp
+ switch GOARCH {
+ default:
+ gothrow("cgocallbackg is unimplemented on arch")
+ case "arm":
+ // On arm, stack frame is two words and there's a saved LR between
+ // SP and the stack frame and between the stack frame and the arguments.
+ cb = (*args)(unsafe.Pointer(sp + 4*ptrSize))
+ case "amd64":
+ // On amd64, stack frame is one word, plus caller PC.
+ cb = (*args)(unsafe.Pointer(sp + 2*ptrSize))
+ case "386":
+ // On 386, stack frame is three words, plus caller PC.
+ cb = (*args)(unsafe.Pointer(sp + 4*ptrSize))
+ }
// Invoke callback.
- cb = CBARGS;
- runtime·newstackcall(cb->fn, cb->arg, cb->argsize);
+ reflectcall(unsafe.Pointer(cb.fn), unsafe.Pointer(cb.arg), uint32(cb.argsize), 0)
- if(raceenabled)
- runtime·racereleasemerge(&cgosync);
+ if raceenabled {
+ racereleasemerge(unsafe.Pointer(&racecgosync))
+ }
- // Pop defer.
// Do not unwind m->g0->sched.sp.
// Our caller, cgocallback, will do that.
- if(g->defer != &d || d.fn != &unwindmf)
- runtime·throw("runtime: bad defer entry in cgocallback");
- g->defer = d.link;
+ restore = false
}
-static void
-unwindm(void)
-{
+func unwindm(restore *bool) {
+ if !*restore {
+ return
+ }
// Restore sp saved by cgocallback during
// unwind of g's stack (see comment at top of file).
- switch(thechar){
+ mp := acquirem()
+ sched := &mp.g0.sched
+ switch GOARCH {
default:
- runtime·throw("runtime: unwindm not implemented");
- case '8':
- case '6':
- g->m->g0->sched.sp = *(uintptr*)g->m->g0->sched.sp;
- break;
- case '5':
- g->m->g0->sched.sp = *(uintptr*)((byte*)g->m->g0->sched.sp + 4);
- break;
+ gothrow("unwindm not implemented")
+ case "386", "amd64":
+ sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp))
+ case "arm":
+ sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + 4))
}
+ releasem(mp)
}
-void
-runtime·badcgocallback(void) // called from assembly
-{
- runtime·throw("runtime: misaligned stack in cgocallback");
+// called from assembly
+func badcgocallback() {
+ gothrow("misaligned stack in cgocallback")
}
-void
-runtime·cgounimpl(void) // called from (incomplete) assembly
-{
- runtime·throw("runtime: cgo not implemented");
+// called from (incomplete) assembly
+func cgounimpl() {
+ gothrow("cgo not implemented")
}
-// For cgo-using programs with external linking,
-// export "main" (defined in assembly) so that libc can handle basic
-// C runtime startup and call the Go program as if it were
-// the C main function.
-#pragma cgo_export_static main
+var racecgosync uint64 // represents possible synchronization in C code
diff --git a/src/pkg/runtime/cgocall.h b/src/pkg/runtime/cgocall.h
index 253661a7e7..c87a9cdc5d 100644
--- a/src/pkg/runtime/cgocall.h
+++ b/src/pkg/runtime/cgocall.h
@@ -7,6 +7,7 @@
*/
void runtime·cgocall(void (*fn)(void*), void*);
+int32 runtime·cgocall_errno(void (*fn)(void*), void*);
void runtime·cgocallback(void (*fn)(void), void*, uintptr);
void *runtime·cmalloc(uintptr);
void runtime·cfree(void*);
diff --git a/src/pkg/runtime/chan.go b/src/pkg/runtime/chan.go
new file mode 100644
index 0000000000..91ade4d37e
--- /dev/null
+++ b/src/pkg/runtime/chan.go
@@ -0,0 +1,644 @@
+// 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
+
+// This file contains the implementation of Go channels.
+
+import "unsafe"
+
+const (
+ maxAlign = 8
+ hchanSize = unsafe.Sizeof(hchan{}) + uintptr(-int(unsafe.Sizeof(hchan{}))&(maxAlign-1))
+ debugChan = false
+)
+
+// TODO(khr): make hchan.buf an unsafe.Pointer, not a *uint8
+
+func makechan(t *chantype, size int64) *hchan {
+ elem := t.elem
+
+ // compiler checks this but be safe.
+ if elem.size >= 1<<16 {
+ gothrow("makechan: invalid channel element type")
+ }
+ if hchanSize%maxAlign != 0 || elem.align > maxAlign {
+ gothrow("makechan: bad alignment")
+ }
+ if size < 0 || int64(uintptr(size)) != size || (elem.size > 0 && uintptr(size) > (maxMem-hchanSize)/uintptr(elem.size)) {
+ panic("makechan: size out of range")
+ }
+
+ var c *hchan
+ if elem.kind&kindNoPointers != 0 || size == 0 {
+ // Allocate memory in one call.
+ // Hchan does not contain pointers interesting for GC in this case:
+ // 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)(gomallocgc(hchanSize+uintptr(size)*uintptr(elem.size), nil, flagNoScan))
+ if size > 0 && elem.size != 0 {
+ c.buf = (*uint8)(add(unsafe.Pointer(c), hchanSize))
+ } else {
+ c.buf = (*uint8)(unsafe.Pointer(c)) // race detector uses this location for synchronization
+ }
+ } else {
+ c = new(hchan)
+ c.buf = (*uint8)(newarray(elem, uintptr(size)))
+ }
+ c.elemsize = uint16(elem.size)
+ c.elemtype = elem
+ c.dataqsiz = uint(size)
+
+ if debugChan {
+ print("makechan: chan=", c, "; elemsize=", elem.size, "; elemalg=", elem.alg, "; dataqsiz=", size, "\n")
+ }
+ return c
+}
+
+// chanbuf(c, i) is pointer to the i'th slot in the buffer.
+func chanbuf(c *hchan, i uint) unsafe.Pointer {
+ return add(unsafe.Pointer(c.buf), uintptr(i)*uintptr(c.elemsize))
+}
+
+// entry point for c <- x from compiled code
+//go:nosplit
+func chansend1(t *chantype, c *hchan, elem unsafe.Pointer) {
+ chansend(t, c, elem, true, getcallerpc(unsafe.Pointer(&t)))
+}
+
+/*
+ * generic single channel send/recv
+ * If block is not nil,
+ * then the protocol will not
+ * sleep but return if it could
+ * not complete.
+ *
+ * sleep can wake up with g.param == nil
+ * when a channel involved in the sleep has
+ * been closed. it is easiest to loop and re-run
+ * the operation; we'll see that it's now closed.
+ */
+func chansend(t *chantype, c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool {
+ if raceenabled {
+ raceReadObjectPC(t.elem, ep, callerpc, funcPC(chansend))
+ }
+
+ if c == nil {
+ if !block {
+ return false
+ }
+ gopark(nil, nil, "chan send (nil chan)")
+ gothrow("unreachable")
+ }
+
+ if debugChan {
+ print("chansend: chan=", c, "\n")
+ }
+
+ if raceenabled {
+ racereadpc(unsafe.Pointer(c), callerpc, funcPC(chansend))
+ }
+
+ // Fast path: check for failed non-blocking operation without acquiring the lock.
+ //
+ // After observing that the channel is not closed, we observe that the channel is
+ // not ready for sending. Each of these observations is a single word-sized read
+ // (first c.closed and second c.recvq.first or c.qcount depending on kind of channel).
+ // Because a closed channel cannot transition from 'ready for sending' to
+ // 'not ready for sending', even if the channel is closed between the two observations,
+ // they imply a moment between the two when the channel was both not yet closed
+ // and not ready for sending. We behave as if we observed the channel at that moment,
+ // and report that the send cannot proceed.
+ //
+ // It is okay if the reads are reordered here: if we observe that the channel is not
+ // ready for sending and then observe that it is not closed, that implies that the
+ // channel wasn't closed during the first observation.
+ if !block && c.closed == 0 && ((c.dataqsiz == 0 && c.recvq.first == nil) ||
+ (c.dataqsiz > 0 && c.qcount == c.dataqsiz)) {
+ return false
+ }
+
+ var t0 int64
+ if blockprofilerate > 0 {
+ t0 = cputicks()
+ }
+
+ lock(&c.lock)
+ if c.closed != 0 {
+ unlock(&c.lock)
+ panic("send on closed channel")
+ }
+
+ if c.dataqsiz == 0 { // synchronous channel
+ sg := c.recvq.dequeue()
+ if sg != nil { // found a waiting receiver
+ if raceenabled {
+ racesync(c, sg)
+ }
+ unlock(&c.lock)
+
+ recvg := sg.g
+ recvg.param = unsafe.Pointer(sg)
+ if sg.elem != nil {
+ memmove(unsafe.Pointer(sg.elem), ep, uintptr(c.elemsize))
+ }
+ if sg.releasetime != 0 {
+ sg.releasetime = cputicks()
+ }
+ goready(recvg)
+ return true
+ }
+
+ if !block {
+ unlock(&c.lock)
+ return false
+ }
+
+ // no receiver available: block on this channel.
+ gp := getg()
+ mysg := acquireSudog()
+ mysg.releasetime = 0
+ if t0 != 0 {
+ mysg.releasetime = -1
+ }
+ mysg.elem = ep
+ mysg.waitlink = nil
+ gp.waiting = mysg
+ mysg.g = gp
+ mysg.selectdone = nil
+ gp.param = nil
+ c.sendq.enqueue(mysg)
+ goparkunlock(&c.lock, "chan send")
+
+ // someone woke us up.
+ if gp.param == nil {
+ if c.closed == 0 {
+ gothrow("chansend: spurious wakeup")
+ }
+ panic("send on closed channel")
+ }
+ if mysg.releasetime > 0 {
+ blockevent(int64(mysg.releasetime)-t0, 2)
+ }
+ if mysg != gp.waiting {
+ gothrow("G waiting list is corrupted!")
+ }
+ gp.waiting = nil
+ releaseSudog(mysg)
+ return true
+ }
+
+ // asynchronous channel
+ // wait for some space to write our data
+ var t1 int64
+ for c.qcount >= c.dataqsiz {
+ if !block {
+ unlock(&c.lock)
+ return false
+ }
+ gp := getg()
+ mysg := acquireSudog()
+ mysg.releasetime = 0
+ if t0 != 0 {
+ mysg.releasetime = -1
+ }
+ mysg.g = gp
+ mysg.elem = nil
+ mysg.selectdone = nil
+ c.sendq.enqueue(mysg)
+ goparkunlock(&c.lock, "chan send")
+
+ // someone woke us up - try again
+ if mysg.releasetime > 0 {
+ t1 = mysg.releasetime
+ }
+ releaseSudog(mysg)
+ lock(&c.lock)
+ if c.closed != 0 {
+ unlock(&c.lock)
+ panic("send on closed channel")
+ }
+ }
+
+ // write our data into the channel buffer
+ if raceenabled {
+ raceacquire(chanbuf(c, c.sendx))
+ racerelease(chanbuf(c, c.sendx))
+ }
+ memmove(chanbuf(c, c.sendx), ep, uintptr(c.elemsize))
+ c.sendx++
+ if c.sendx == c.dataqsiz {
+ c.sendx = 0
+ }
+ c.qcount++
+
+ // wake up a waiting receiver
+ sg := c.recvq.dequeue()
+ if sg != nil {
+ recvg := sg.g
+ unlock(&c.lock)
+ if sg.releasetime != 0 {
+ sg.releasetime = cputicks()
+ }
+ goready(recvg)
+ } else {
+ unlock(&c.lock)
+ }
+ if t1 > 0 {
+ blockevent(t1-t0, 2)
+ }
+ return true
+}
+
+func closechan(c *hchan) {
+ if c == nil {
+ panic("close of nil channel")
+ }
+
+ lock(&c.lock)
+ if c.closed != 0 {
+ unlock(&c.lock)
+ panic("close of closed channel")
+ }
+
+ if raceenabled {
+ callerpc := getcallerpc(unsafe.Pointer(&c))
+ racewritepc(unsafe.Pointer(c), callerpc, funcPC(closechan))
+ racerelease(unsafe.Pointer(c))
+ }
+
+ c.closed = 1
+
+ // release all readers
+ for {
+ sg := c.recvq.dequeue()
+ if sg == nil {
+ break
+ }
+ gp := sg.g
+ gp.param = nil
+ if sg.releasetime != 0 {
+ sg.releasetime = cputicks()
+ }
+ goready(gp)
+ }
+
+ // release all writers
+ for {
+ sg := c.sendq.dequeue()
+ if sg == nil {
+ break
+ }
+ gp := sg.g
+ gp.param = nil
+ if sg.releasetime != 0 {
+ sg.releasetime = cputicks()
+ }
+ goready(gp)
+ }
+ unlock(&c.lock)
+}
+
+// entry points for <- c from compiled code
+//go:nosplit
+func chanrecv1(t *chantype, c *hchan, elem unsafe.Pointer) {
+ chanrecv(t, c, elem, true)
+}
+
+//go:nosplit
+func chanrecv2(t *chantype, c *hchan, elem unsafe.Pointer) (received bool) {
+ _, received = chanrecv(t, c, elem, true)
+ return
+}
+
+// chanrecv receives on channel c and writes the received data to ep.
+// ep may be nil, in which case received data is ignored.
+// If block == false and no elements are available, returns (false, false).
+// Otherwise, if c is closed, zeros *ep and returns (true, false).
+// Otherwise, fills in *ep with an element and returns (true, true).
+func chanrecv(t *chantype, c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) {
+ // raceenabled: don't need to check ep, as it is always on the stack.
+
+ if debugChan {
+ print("chanrecv: chan=", c, "\n")
+ }
+
+ if c == nil {
+ if !block {
+ return
+ }
+ gopark(nil, nil, "chan receive (nil chan)")
+ gothrow("unreachable")
+ }
+
+ // Fast path: check for failed non-blocking operation without acquiring the lock.
+ //
+ // After observing that the channel is not ready for receiving, we observe that the
+ // channel is not closed. Each of these observations is a single word-sized read
+ // (first c.sendq.first or c.qcount, and second c.closed).
+ // Because a channel cannot be reopened, the later observation of the channel
+ // being not closed implies that it was also not closed at the moment of the
+ // first observation. We behave as if we observed the channel at that moment
+ // and report that the receive cannot proceed.
+ //
+ // The order of operations is important here: reversing the operations can lead to
+ // incorrect behavior when racing with a close.
+ if !block && (c.dataqsiz == 0 && c.sendq.first == nil ||
+ c.dataqsiz > 0 && atomicloaduint(&c.qcount) == 0) &&
+ atomicload(&c.closed) == 0 {
+ return
+ }
+
+ var t0 int64
+ if blockprofilerate > 0 {
+ t0 = cputicks()
+ }
+
+ lock(&c.lock)
+ if c.dataqsiz == 0 { // synchronous channel
+ if c.closed != 0 {
+ return recvclosed(c, ep)
+ }
+
+ sg := c.sendq.dequeue()
+ if sg != nil {
+ if raceenabled {
+ racesync(c, sg)
+ }
+ unlock(&c.lock)
+
+ if ep != nil {
+ memmove(ep, sg.elem, uintptr(c.elemsize))
+ }
+ gp := sg.g
+ gp.param = unsafe.Pointer(sg)
+ if sg.releasetime != 0 {
+ sg.releasetime = cputicks()
+ }
+ goready(gp)
+ selected = true
+ received = true
+ return
+ }
+
+ if !block {
+ unlock(&c.lock)
+ return
+ }
+
+ // no sender available: block on this channel.
+ gp := getg()
+ mysg := acquireSudog()
+ mysg.releasetime = 0
+ if t0 != 0 {
+ mysg.releasetime = -1
+ }
+ mysg.elem = ep
+ mysg.waitlink = nil
+ gp.waiting = mysg
+ mysg.g = gp
+ mysg.selectdone = nil
+ gp.param = nil
+ c.recvq.enqueue(mysg)
+ goparkunlock(&c.lock, "chan receive")
+
+ // someone woke us up
+ gp.waiting = nil
+ if mysg.releasetime > 0 {
+ blockevent(mysg.releasetime-t0, 2)
+ }
+ releaseSudog(mysg)
+
+ if gp.param != nil {
+ // a sender sent us some data. It already wrote to ep.
+ selected = true
+ received = true
+ return
+ }
+
+ lock(&c.lock)
+ if c.closed == 0 {
+ gothrow("chanrecv: spurious wakeup")
+ }
+ return recvclosed(c, ep)
+ }
+
+ // asynchronous channel
+ // wait for some data to appear
+ var t1 int64
+ for c.qcount <= 0 {
+ if c.closed != 0 {
+ selected, received = recvclosed(c, ep)
+ if t1 > 0 {
+ blockevent(t1-t0, 2)
+ }
+ return
+ }
+
+ if !block {
+ unlock(&c.lock)
+ return
+ }
+
+ // wait for someone to send an element
+ gp := getg()
+ mysg := acquireSudog()
+ mysg.releasetime = 0
+ if t0 != 0 {
+ mysg.releasetime = -1
+ }
+ mysg.elem = nil
+ mysg.g = gp
+ mysg.selectdone = nil
+
+ c.recvq.enqueue(mysg)
+ goparkunlock(&c.lock, "chan receive")
+
+ // someone woke us up - try again
+ if mysg.releasetime > 0 {
+ t1 = mysg.releasetime
+ }
+ releaseSudog(mysg)
+ lock(&c.lock)
+ }
+
+ if raceenabled {
+ raceacquire(chanbuf(c, c.recvx))
+ racerelease(chanbuf(c, c.recvx))
+ }
+ if ep != nil {
+ memmove(ep, chanbuf(c, c.recvx), uintptr(c.elemsize))
+ }
+ memclr(chanbuf(c, c.recvx), uintptr(c.elemsize))
+
+ c.recvx++
+ if c.recvx == c.dataqsiz {
+ c.recvx = 0
+ }
+ c.qcount--
+
+ // ping a sender now that there is space
+ sg := c.sendq.dequeue()
+ if sg != nil {
+ gp := sg.g
+ unlock(&c.lock)
+ if sg.releasetime != 0 {
+ sg.releasetime = cputicks()
+ }
+ goready(gp)
+ } else {
+ unlock(&c.lock)
+ }
+
+ if t1 > 0 {
+ blockevent(t1-t0, 2)
+ }
+ selected = true
+ received = true
+ return
+}
+
+// recvclosed is a helper function for chanrecv. Handles cleanup
+// when the receiver encounters a closed channel.
+// Caller must hold c.lock, recvclosed will release the lock.
+func recvclosed(c *hchan, ep unsafe.Pointer) (selected, recevied bool) {
+ if raceenabled {
+ raceacquire(unsafe.Pointer(c))
+ }
+ unlock(&c.lock)
+ if ep != nil {
+ memclr(ep, uintptr(c.elemsize))
+ }
+ return true, false
+}
+
+// compiler implements
+//
+// select {
+// case c <- v:
+// ... foo
+// default:
+// ... bar
+// }
+//
+// as
+//
+// if selectnbsend(c, v) {
+// ... foo
+// } else {
+// ... bar
+// }
+//
+func selectnbsend(t *chantype, c *hchan, elem unsafe.Pointer) (selected bool) {
+ return chansend(t, c, elem, false, getcallerpc(unsafe.Pointer(&t)))
+}
+
+// compiler implements
+//
+// select {
+// case v = <-c:
+// ... foo
+// default:
+// ... bar
+// }
+//
+// as
+//
+// if selectnbrecv(&v, c) {
+// ... foo
+// } else {
+// ... bar
+// }
+//
+func selectnbrecv(t *chantype, elem unsafe.Pointer, c *hchan) (selected bool) {
+ selected, _ = chanrecv(t, c, elem, false)
+ return
+}
+
+// compiler implements
+//
+// select {
+// case v, ok = <-c:
+// ... foo
+// default:
+// ... bar
+// }
+//
+// as
+//
+// if c != nil && selectnbrecv2(&v, &ok, c) {
+// ... foo
+// } else {
+// ... bar
+// }
+//
+func selectnbrecv2(t *chantype, elem unsafe.Pointer, received *bool, c *hchan) (selected bool) {
+ // TODO(khr): just return 2 values from this function, now that it is in Go.
+ selected, *received = chanrecv(t, c, elem, false)
+ return
+}
+
+func reflect_chansend(t *chantype, c *hchan, elem unsafe.Pointer, nb bool) (selected bool) {
+ return chansend(t, c, elem, !nb, getcallerpc(unsafe.Pointer(&t)))
+}
+
+func reflect_chanrecv(t *chantype, c *hchan, nb bool, elem unsafe.Pointer) (selected bool, received bool) {
+ return chanrecv(t, c, elem, !nb)
+}
+
+func reflect_chanlen(c *hchan) int {
+ if c == nil {
+ return 0
+ }
+ return int(c.qcount)
+}
+
+func reflect_chancap(c *hchan) int {
+ if c == nil {
+ return 0
+ }
+ return int(c.dataqsiz)
+}
+
+func (q *waitq) enqueue(sgp *sudog) {
+ sgp.next = nil
+ if q.first == nil {
+ q.first = sgp
+ q.last = sgp
+ return
+ }
+ q.last.next = sgp
+ q.last = sgp
+}
+
+func (q *waitq) dequeue() *sudog {
+ for {
+ sgp := q.first
+ if sgp == nil {
+ return nil
+ }
+ q.first = sgp.next
+ if q.last == sgp {
+ q.last = nil
+ }
+
+ // if sgp participates in a select and is already signaled, ignore it
+ if sgp.selectdone != nil {
+ // claim the right to signal
+ if *sgp.selectdone != 0 || !cas(sgp.selectdone, 0, 1) {
+ continue
+ }
+ }
+
+ return sgp
+ }
+}
+
+func racesync(c *hchan, sg *sudog) {
+ racerelease(chanbuf(c, 0))
+ raceacquireg(sg.g, chanbuf(c, 0))
+ racereleaseg(sg.g, chanbuf(c, 0))
+ raceacquire(chanbuf(c, 0))
+}
diff --git a/src/pkg/runtime/chan.goc b/src/pkg/runtime/chan.goc
deleted file mode 100644
index b3520b60fc..0000000000
--- a/src/pkg/runtime/chan.goc
+++ /dev/null
@@ -1,1183 +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
-#include "runtime.h"
-#include "arch_GOARCH.h"
-#include "type.h"
-#include "race.h"
-#include "malloc.h"
-#include "chan.h"
-#include "mgc0.h"
-#include "typekind.h"
-#include "../../cmd/ld/textflag.h"
-
-static void dequeueg(WaitQ*);
-static SudoG* dequeue(WaitQ*);
-static void enqueue(WaitQ*, SudoG*);
-static void racesync(Hchan*, SudoG*);
-
-static Type hchanType;
-static String hchanStr;
-
-void
-runtime·chaninit(void)
-{
- int32 i, off;
- byte *mask;
-
- // Generate (bare minimum) type descriptor for Hchan.
- hchanType.size = sizeof(Hchan);
- hchanStr = runtime·gostringnocopy((byte*)"chan");
- hchanType.string = &hchanStr;
- // Hchan has only one interesting pointer -- buf.
- off = offsetof(Hchan, buf)/PtrSize*gcBits;
- if(off%8)
- runtime·throw("makechan: unaligned buffer");
- if(off+8 >= sizeof(hchanType.gc)*8)
- runtime·throw("makechan: gc mask does not fit");
- mask = (byte*)hchanType.gc;
- for(i = 0; i < off/8; i++)
- mask[i] = (BitsScalar<<2) | (BitsScalar<<6);
- mask[off/8] = (BitsPointer<<2) | (BitsDead<<6);
-}
-
-static Hchan*
-makechan(ChanType *t, int64 hint)
-{
- Hchan *c;
- Type *elem;
-
- elem = t->elem;
-
- // compiler checks this but be safe.
- if(elem->size >= (1<<16))
- runtime·throw("makechan: invalid channel element type");
- if((sizeof(*c)%MAXALIGN) != 0 || elem->align > MAXALIGN)
- runtime·throw("makechan: bad alignment");
-
- if(hint < 0 || (intgo)hint != hint || (elem->size > 0 && hint > (MaxMem - sizeof(*c)) / elem->size))
- runtime·panicstring("makechan: size out of range");
-
- if((elem->kind&KindNoPointers) || hint == 0) {
- // allocate memory in one call
- c = (Hchan*)runtime·mallocgc(sizeof(*c) + hint*elem->size, nil, FlagNoScan);
- if(hint > 0 && elem->size != 0)
- c->buf = (byte*)(c+1);
- else
- c->buf = (byte*)c; // race detector uses this location for synchronization
- } else {
- c = (Hchan*)runtime·cnew(&hchanType);
- c->buf = runtime·cnewarray(elem, hint);
- }
- c->elemsize = elem->size;
- c->elemtype = elem;
- c->dataqsiz = hint;
-
- if(debug)
- runtime·printf("makechan: chan=%p; elemsize=%D; elemalg=%p; dataqsiz=%D\n",
- c, (int64)elem->size, elem->alg, (int64)c->dataqsiz);
-
- return c;
-}
-
-func reflect·makechan(t *ChanType, size uint64) (c *Hchan) {
- c = makechan(t, size);
-}
-
-func makechan(t *ChanType, size int64) (c *Hchan) {
- c = makechan(t, size);
-}
-
-/*
- * generic single channel send/recv
- * if the bool pointer is nil,
- * then the full exchange will
- * occur. if pres is not nil,
- * then the protocol will not
- * sleep but return if it could
- * not complete.
- *
- * sleep can wake up with g->param == nil
- * when a channel involved in the sleep has
- * been closed. it is easiest to loop and re-run
- * the operation; we'll see that it's now closed.
- */
-static bool
-chansend(ChanType *t, Hchan *c, byte *ep, bool block, void *pc)
-{
- SudoG *sg;
- SudoG mysg;
- G* gp;
- int64 t0;
-
- if(raceenabled)
- runtime·racereadobjectpc(ep, t->elem, runtime·getcallerpc(&t), chansend);
-
- if(c == nil) {
- USED(t);
- if(!block)
- return false;
- runtime·park(nil, nil, "chan send (nil chan)");
- return false; // not reached
- }
-
- if(debug) {
- runtime·printf("chansend: chan=%p; elem=", c);
- c->elemtype->alg->print(c->elemsize, ep);
- runtime·prints("\n");
- }
-
- t0 = 0;
- mysg.releasetime = 0;
- if(runtime·blockprofilerate > 0) {
- t0 = runtime·cputicks();
- mysg.releasetime = -1;
- }
-
- runtime·lock(c);
- if(raceenabled)
- runtime·racereadpc(c, pc, chansend);
- if(c->closed)
- goto closed;
-
- if(c->dataqsiz > 0)
- goto asynch;
-
- sg = dequeue(&c->recvq);
- if(sg != nil) {
- if(raceenabled)
- racesync(c, sg);
- runtime·unlock(c);
-
- gp = sg->g;
- gp->param = sg;
- if(sg->elem != nil)
- c->elemtype->alg->copy(c->elemsize, sg->elem, ep);
- if(sg->releasetime)
- sg->releasetime = runtime·cputicks();
- runtime·ready(gp);
- return true;
- }
-
- if(!block) {
- runtime·unlock(c);
- return false;
- }
-
- mysg.elem = ep;
- mysg.g = g;
- mysg.selectdone = nil;
- g->param = nil;
- enqueue(&c->sendq, &mysg);
- runtime·parkunlock(c, "chan send");
-
- if(g->param == nil) {
- runtime·lock(c);
- if(!c->closed)
- runtime·throw("chansend: spurious wakeup");
- goto closed;
- }
-
- if(mysg.releasetime > 0)
- runtime·blockevent(mysg.releasetime - t0, 2);
-
- return true;
-
-asynch:
- if(c->closed)
- goto closed;
-
- if(c->qcount >= c->dataqsiz) {
- if(!block) {
- runtime·unlock(c);
- return false;
- }
- mysg.g = g;
- mysg.elem = nil;
- mysg.selectdone = nil;
- enqueue(&c->sendq, &mysg);
- runtime·parkunlock(c, "chan send");
-
- runtime·lock(c);
- goto asynch;
- }
-
- if(raceenabled) {
- runtime·raceacquire(chanbuf(c, c->sendx));
- runtime·racerelease(chanbuf(c, c->sendx));
- }
-
- c->elemtype->alg->copy(c->elemsize, chanbuf(c, c->sendx), ep);
- if(++c->sendx == c->dataqsiz)
- c->sendx = 0;
- c->qcount++;
-
- sg = dequeue(&c->recvq);
- if(sg != nil) {
- gp = sg->g;
- runtime·unlock(c);
- if(sg->releasetime)
- sg->releasetime = runtime·cputicks();
- runtime·ready(gp);
- } else
- runtime·unlock(c);
- if(mysg.releasetime > 0)
- runtime·blockevent(mysg.releasetime - t0, 2);
- return true;
-
-closed:
- runtime·unlock(c);
- runtime·panicstring("send on closed channel");
- return false; // not reached
-}
-
-
-static bool
-chanrecv(ChanType *t, Hchan* c, byte *ep, bool block, bool *received)
-{
- SudoG *sg;
- SudoG mysg;
- G *gp;
- int64 t0;
-
- // raceenabled: don't need to check ep, as it is always on the stack.
-
- if(debug)
- runtime·printf("chanrecv: chan=%p\n", c);
-
- if(c == nil) {
- USED(t);
- if(!block)
- return false;
- runtime·park(nil, nil, "chan receive (nil chan)");
- return false; // not reached
- }
-
- t0 = 0;
- mysg.releasetime = 0;
- if(runtime·blockprofilerate > 0) {
- t0 = runtime·cputicks();
- mysg.releasetime = -1;
- }
-
- runtime·lock(c);
- if(c->dataqsiz > 0)
- goto asynch;
-
- if(c->closed)
- goto closed;
-
- sg = dequeue(&c->sendq);
- if(sg != nil) {
- if(raceenabled)
- racesync(c, sg);
- runtime·unlock(c);
-
- if(ep != nil)
- c->elemtype->alg->copy(c->elemsize, ep, sg->elem);
- gp = sg->g;
- gp->param = sg;
- if(sg->releasetime)
- sg->releasetime = runtime·cputicks();
- runtime·ready(gp);
-
- if(received != nil)
- *received = true;
- return true;
- }
-
- if(!block) {
- runtime·unlock(c);
- return false;
- }
-
- mysg.elem = ep;
- mysg.g = g;
- mysg.selectdone = nil;
- g->param = nil;
- enqueue(&c->recvq, &mysg);
- runtime·parkunlock(c, "chan receive");
-
- if(g->param == nil) {
- runtime·lock(c);
- if(!c->closed)
- runtime·throw("chanrecv: spurious wakeup");
- goto closed;
- }
-
- if(received != nil)
- *received = true;
- if(mysg.releasetime > 0)
- runtime·blockevent(mysg.releasetime - t0, 2);
- return true;
-
-asynch:
- if(c->qcount <= 0) {
- if(c->closed)
- goto closed;
-
- if(!block) {
- runtime·unlock(c);
- if(received != nil)
- *received = false;
- return false;
- }
- mysg.g = g;
- mysg.elem = nil;
- mysg.selectdone = nil;
- enqueue(&c->recvq, &mysg);
- runtime·parkunlock(c, "chan receive");
-
- runtime·lock(c);
- goto asynch;
- }
-
- if(raceenabled) {
- runtime·raceacquire(chanbuf(c, c->recvx));
- runtime·racerelease(chanbuf(c, c->recvx));
- }
-
- if(ep != nil)
- c->elemtype->alg->copy(c->elemsize, ep, chanbuf(c, c->recvx));
- c->elemtype->alg->copy(c->elemsize, chanbuf(c, c->recvx), nil);
- if(++c->recvx == c->dataqsiz)
- c->recvx = 0;
- c->qcount--;
-
- sg = dequeue(&c->sendq);
- if(sg != nil) {
- gp = sg->g;
- runtime·unlock(c);
- if(sg->releasetime)
- sg->releasetime = runtime·cputicks();
- runtime·ready(gp);
- } else
- runtime·unlock(c);
-
- if(received != nil)
- *received = true;
- if(mysg.releasetime > 0)
- runtime·blockevent(mysg.releasetime - t0, 2);
- return true;
-
-closed:
- if(ep != nil)
- c->elemtype->alg->copy(c->elemsize, ep, nil);
- if(received != nil)
- *received = false;
- if(raceenabled)
- runtime·raceacquire(c);
- runtime·unlock(c);
- if(mysg.releasetime > 0)
- runtime·blockevent(mysg.releasetime - t0, 2);
- return true;
-}
-
-#pragma textflag NOSPLIT
-func chansend1(t *ChanType, c *Hchan, elem *byte) {
- chansend(t, c, elem, true, runtime·getcallerpc(&t));
-}
-
-#pragma textflag NOSPLIT
-func chanrecv1(t *ChanType, c *Hchan, elem *byte) {
- chanrecv(t, c, elem, true, nil);
-}
-
-// chanrecv2(hchan *chan any, elem *any) (received bool);
-#pragma textflag NOSPLIT
-func chanrecv2(t *ChanType, c *Hchan, elem *byte) (received bool) {
- chanrecv(t, c, elem, true, &received);
-}
-
-// compiler implements
-//
-// select {
-// case c <- v:
-// ... foo
-// default:
-// ... bar
-// }
-//
-// as
-//
-// if selectnbsend(c, v) {
-// ... foo
-// } else {
-// ... bar
-// }
-//
-#pragma textflag NOSPLIT
-func selectnbsend(t *ChanType, c *Hchan, elem *byte) (selected bool) {
- selected = chansend(t, c, elem, false, runtime·getcallerpc(&t));
-}
-
-// compiler implements
-//
-// select {
-// case v = <-c:
-// ... foo
-// default:
-// ... bar
-// }
-//
-// as
-//
-// if selectnbrecv(&v, c) {
-// ... foo
-// } else {
-// ... bar
-// }
-//
-#pragma textflag NOSPLIT
-func selectnbrecv(t *ChanType, elem *byte, c *Hchan) (selected bool) {
- selected = chanrecv(t, c, elem, false, nil);
-}
-
-// compiler implements
-//
-// select {
-// case v, ok = <-c:
-// ... foo
-// default:
-// ... bar
-// }
-//
-// as
-//
-// if c != nil && selectnbrecv2(&v, &ok, c) {
-// ... foo
-// } else {
-// ... bar
-// }
-//
-#pragma textflag NOSPLIT
-func selectnbrecv2(t *ChanType, elem *byte, received *bool, c *Hchan) (selected bool) {
- selected = chanrecv(t, c, elem, false, received);
-}
-
-#pragma textflag NOSPLIT
-func reflect·chansend(t *ChanType, c *Hchan, elem *byte, nb bool) (selected bool) {
- selected = chansend(t, c, elem, !nb, runtime·getcallerpc(&t));
-}
-
-func reflect·chanrecv(t *ChanType, c *Hchan, nb bool, elem *byte) (selected bool, received bool) {
- received = false;
- selected = chanrecv(t, c, elem, !nb, &received);
-}
-
-static int64
-selectsize(int32 size)
-{
- Select *sel;
- int64 selsize;
-
- selsize = sizeof(*sel) +
- (size-1)*sizeof(sel->scase[0]) +
- size*sizeof(sel->lockorder[0]) +
- size*sizeof(sel->pollorder[0]);
- return ROUND(selsize, Int64Align);
-}
-
-#pragma textflag NOSPLIT
-func newselect(sel *Select, selsize int64, size int32) {
- if(selsize != selectsize(size)) {
- runtime·printf("runtime: bad select size %D, want %D\n", selsize, selectsize(size));
- runtime·throw("bad select size");
- }
- sel->tcase = size;
- sel->ncase = 0;
- sel->lockorder = (void*)(sel->scase + size);
- sel->pollorder = (void*)(sel->lockorder + size);
-
- if(debug)
- runtime·printf("newselect s=%p size=%d\n", sel, size);
-}
-
-// cut in half to give stack a chance to split
-static void selectsend(Select *sel, Hchan *c, void *pc, void *elem, int32 so);
-
-#pragma textflag NOSPLIT
-func selectsend(sel *Select, c *Hchan, elem *byte) (selected bool) {
- selected = false;
-
- // nil cases do not compete
- if(c != nil)
- selectsend(sel, c, runtime·getcallerpc(&sel), elem, (byte*)&selected - (byte*)&sel);
-}
-
-static void
-selectsend(Select *sel, Hchan *c, void *pc, void *elem, int32 so)
-{
- int32 i;
- Scase *cas;
-
- i = sel->ncase;
- if(i >= sel->tcase)
- runtime·throw("selectsend: too many cases");
- sel->ncase = i+1;
- cas = &sel->scase[i];
-
- cas->pc = pc;
- cas->chan = c;
- cas->so = so;
- cas->kind = CaseSend;
- cas->sg.elem = elem;
-
- if(debug)
- runtime·printf("selectsend s=%p pc=%p chan=%p so=%d\n",
- sel, cas->pc, cas->chan, cas->so);
-}
-
-// cut in half to give stack a chance to split
-static void selectrecv(Select *sel, Hchan *c, void *pc, void *elem, bool*, int32 so);
-
-#pragma textflag NOSPLIT
-func selectrecv(sel *Select, c *Hchan, elem *byte) (selected bool) {
- selected = false;
-
- // nil cases do not compete
- if(c != nil)
- selectrecv(sel, c, runtime·getcallerpc(&sel), elem, nil, (byte*)&selected - (byte*)&sel);
-}
-
-#pragma textflag NOSPLIT
-func selectrecv2(sel *Select, c *Hchan, elem *byte, received *bool) (selected bool) {
- selected = false;
-
- // nil cases do not compete
- if(c != nil)
- selectrecv(sel, c, runtime·getcallerpc(&sel), elem, received, (byte*)&selected - (byte*)&sel);
-}
-
-static void
-selectrecv(Select *sel, Hchan *c, void *pc, void *elem, bool *received, int32 so)
-{
- int32 i;
- Scase *cas;
-
- i = sel->ncase;
- if(i >= sel->tcase)
- runtime·throw("selectrecv: too many cases");
- sel->ncase = i+1;
- cas = &sel->scase[i];
- cas->pc = pc;
- cas->chan = c;
-
- cas->so = so;
- cas->kind = CaseRecv;
- cas->sg.elem = elem;
- cas->receivedp = received;
-
- if(debug)
- runtime·printf("selectrecv s=%p pc=%p chan=%p so=%d\n",
- sel, cas->pc, cas->chan, cas->so);
-}
-
-// cut in half to give stack a chance to split
-static void selectdefault(Select*, void*, int32);
-
-#pragma textflag NOSPLIT
-func selectdefault(sel *Select) (selected bool) {
- selected = false;
- selectdefault(sel, runtime·getcallerpc(&sel), (byte*)&selected - (byte*)&sel);
-}
-
-static void
-selectdefault(Select *sel, void *callerpc, int32 so)
-{
- int32 i;
- Scase *cas;
-
- i = sel->ncase;
- if(i >= sel->tcase)
- runtime·throw("selectdefault: too many cases");
- sel->ncase = i+1;
- cas = &sel->scase[i];
- cas->pc = callerpc;
- cas->chan = nil;
-
- cas->so = so;
- cas->kind = CaseDefault;
-
- if(debug)
- runtime·printf("selectdefault s=%p pc=%p so=%d\n",
- sel, cas->pc, cas->so);
-}
-
-static void
-sellock(Select *sel)
-{
- uint32 i;
- Hchan *c, *c0;
-
- c = nil;
- for(i=0; i<sel->ncase; i++) {
- c0 = sel->lockorder[i];
- if(c0 && c0 != c) {
- c = sel->lockorder[i];
- runtime·lock(c);
- }
- }
-}
-
-static void
-selunlock(Select *sel)
-{
- int32 i, n, r;
- Hchan *c;
-
- // We must be very careful here to not touch sel after we have unlocked
- // the last lock, because sel can be freed right after the last unlock.
- // Consider the following situation.
- // First M calls runtime·park() in runtime·selectgo() passing the sel.
- // Once runtime·park() has unlocked the last lock, another M makes
- // the G that calls select runnable again and schedules it for execution.
- // When the G runs on another M, it locks all the locks and frees sel.
- // Now if the first M touches sel, it will access freed memory.
- n = (int32)sel->ncase;
- r = 0;
- // skip the default case
- if(n>0 && sel->lockorder[0] == nil)
- r = 1;
- for(i = n-1; i >= r; i--) {
- c = sel->lockorder[i];
- if(i>0 && sel->lockorder[i-1] == c)
- continue; // will unlock it on the next iteration
- runtime·unlock(c);
- }
-}
-
-static bool
-selparkcommit(G *gp, void *sel)
-{
- USED(gp);
- selunlock(sel);
- return true;
-}
-
-func block() {
- runtime·park(nil, nil, "select (no cases)"); // forever
-}
-
-static void* selectgo(Select**);
-
-// selectgo(sel *byte);
-//
-// overwrites return pc on stack to signal which case of the select
-// to run, so cannot appear at the top of a split stack.
-#pragma textflag NOSPLIT
-func selectgo(sel *Select) {
- runtime·setcallerpc(&sel, selectgo(&sel));
-}
-
-static void*
-selectgo(Select **selp)
-{
- Select *sel;
- uint32 o, i, j, k, done;
- int64 t0;
- Scase *cas, *dfl;
- Hchan *c;
- SudoG *sg;
- G *gp;
- byte *as;
- void *pc;
-
- sel = *selp;
-
- if(debug)
- runtime·printf("select: sel=%p\n", sel);
-
- t0 = 0;
- if(runtime·blockprofilerate > 0) {
- t0 = runtime·cputicks();
- for(i=0; i<sel->ncase; i++)
- sel->scase[i].sg.releasetime = -1;
- }
-
- // The compiler rewrites selects that statically have
- // only 0 or 1 cases plus default into simpler constructs.
- // The only way we can end up with such small sel->ncase
- // values here is for a larger select in which most channels
- // have been nilled out. The general code handles those
- // cases correctly, and they are rare enough not to bother
- // optimizing (and needing to test).
-
- // generate permuted order
- for(i=0; i<sel->ncase; i++)
- sel->pollorder[i] = i;
- for(i=1; i<sel->ncase; i++) {
- o = sel->pollorder[i];
- j = runtime·fastrand1()%(i+1);
- sel->pollorder[i] = sel->pollorder[j];
- sel->pollorder[j] = o;
- }
-
- // sort the cases by Hchan address to get the locking order.
- // simple heap sort, to guarantee n log n time and constant stack footprint.
- for(i=0; i<sel->ncase; i++) {
- j = i;
- c = sel->scase[j].chan;
- while(j > 0 && sel->lockorder[k=(j-1)/2] < c) {
- sel->lockorder[j] = sel->lockorder[k];
- j = k;
- }
- sel->lockorder[j] = c;
- }
- for(i=sel->ncase; i-->0; ) {
- c = sel->lockorder[i];
- sel->lockorder[i] = sel->lockorder[0];
- j = 0;
- for(;;) {
- k = j*2+1;
- if(k >= i)
- break;
- if(k+1 < i && sel->lockorder[k] < sel->lockorder[k+1])
- k++;
- if(c < sel->lockorder[k]) {
- sel->lockorder[j] = sel->lockorder[k];
- j = k;
- continue;
- }
- break;
- }
- sel->lockorder[j] = c;
- }
- /*
- for(i=0; i+1<sel->ncase; i++)
- if(sel->lockorder[i] > sel->lockorder[i+1]) {
- runtime·printf("i=%d %p %p\n", i, sel->lockorder[i], sel->lockorder[i+1]);
- runtime·throw("select: broken sort");
- }
- */
- sellock(sel);
-
-loop:
- // pass 1 - look for something already waiting
- dfl = nil;
- for(i=0; i<sel->ncase; i++) {
- o = sel->pollorder[i];
- cas = &sel->scase[o];
- c = cas->chan;
-
- switch(cas->kind) {
- case CaseRecv:
- if(c->dataqsiz > 0) {
- if(c->qcount > 0)
- goto asyncrecv;
- } else {
- sg = dequeue(&c->sendq);
- if(sg != nil)
- goto syncrecv;
- }
- if(c->closed)
- goto rclose;
- break;
-
- case CaseSend:
- if(raceenabled)
- runtime·racereadpc(c, cas->pc, chansend);
- if(c->closed)
- goto sclose;
- if(c->dataqsiz > 0) {
- if(c->qcount < c->dataqsiz)
- goto asyncsend;
- } else {
- sg = dequeue(&c->recvq);
- if(sg != nil)
- goto syncsend;
- }
- break;
-
- case CaseDefault:
- dfl = cas;
- break;
- }
- }
-
- if(dfl != nil) {
- selunlock(sel);
- cas = dfl;
- goto retc;
- }
-
-
- // pass 2 - enqueue on all chans
- done = 0;
- for(i=0; i<sel->ncase; i++) {
- o = sel->pollorder[i];
- cas = &sel->scase[o];
- c = cas->chan;
- sg = &cas->sg;
- sg->g = g;
- sg->selectdone = &done;
-
- switch(cas->kind) {
- case CaseRecv:
- enqueue(&c->recvq, sg);
- break;
-
- case CaseSend:
- enqueue(&c->sendq, sg);
- break;
- }
- }
-
- g->param = nil;
- runtime·park(selparkcommit, sel, "select");
-
- sellock(sel);
- sg = g->param;
-
- // pass 3 - dequeue from unsuccessful chans
- // otherwise they stack up on quiet channels
- for(i=0; i<sel->ncase; i++) {
- cas = &sel->scase[i];
- if(cas != (Scase*)sg) {
- c = cas->chan;
- if(cas->kind == CaseSend)
- dequeueg(&c->sendq);
- else
- dequeueg(&c->recvq);
- }
- }
-
- if(sg == nil)
- goto loop;
-
- cas = (Scase*)sg;
- c = cas->chan;
-
- if(c->dataqsiz > 0)
- runtime·throw("selectgo: shouldn't happen");
-
- if(debug)
- runtime·printf("wait-return: sel=%p c=%p cas=%p kind=%d\n",
- sel, c, cas, cas->kind);
-
- if(cas->kind == CaseRecv) {
- if(cas->receivedp != nil)
- *cas->receivedp = true;
- }
-
- if(raceenabled) {
- if(cas->kind == CaseRecv && cas->sg.elem != nil)
- runtime·racewriteobjectpc(cas->sg.elem, c->elemtype, cas->pc, chanrecv);
- else if(cas->kind == CaseSend)
- runtime·racereadobjectpc(cas->sg.elem, c->elemtype, cas->pc, chansend);
- }
-
- selunlock(sel);
- goto retc;
-
-asyncrecv:
- // can receive from buffer
- if(raceenabled) {
- if(cas->sg.elem != nil)
- runtime·racewriteobjectpc(cas->sg.elem, c->elemtype, cas->pc, chanrecv);
- runtime·raceacquire(chanbuf(c, c->recvx));
- runtime·racerelease(chanbuf(c, c->recvx));
- }
- if(cas->receivedp != nil)
- *cas->receivedp = true;
- if(cas->sg.elem != nil)
- c->elemtype->alg->copy(c->elemsize, cas->sg.elem, chanbuf(c, c->recvx));
- c->elemtype->alg->copy(c->elemsize, chanbuf(c, c->recvx), nil);
- if(++c->recvx == c->dataqsiz)
- c->recvx = 0;
- c->qcount--;
- sg = dequeue(&c->sendq);
- if(sg != nil) {
- gp = sg->g;
- selunlock(sel);
- if(sg->releasetime)
- sg->releasetime = runtime·cputicks();
- runtime·ready(gp);
- } else {
- selunlock(sel);
- }
- goto retc;
-
-asyncsend:
- // can send to buffer
- if(raceenabled) {
- runtime·raceacquire(chanbuf(c, c->sendx));
- runtime·racerelease(chanbuf(c, c->sendx));
- runtime·racereadobjectpc(cas->sg.elem, c->elemtype, cas->pc, chansend);
- }
- c->elemtype->alg->copy(c->elemsize, chanbuf(c, c->sendx), cas->sg.elem);
- if(++c->sendx == c->dataqsiz)
- c->sendx = 0;
- c->qcount++;
- sg = dequeue(&c->recvq);
- if(sg != nil) {
- gp = sg->g;
- selunlock(sel);
- if(sg->releasetime)
- sg->releasetime = runtime·cputicks();
- runtime·ready(gp);
- } else {
- selunlock(sel);
- }
- goto retc;
-
-syncrecv:
- // can receive from sleeping sender (sg)
- if(raceenabled) {
- if(cas->sg.elem != nil)
- runtime·racewriteobjectpc(cas->sg.elem, c->elemtype, cas->pc, chanrecv);
- racesync(c, sg);
- }
- selunlock(sel);
- if(debug)
- runtime·printf("syncrecv: sel=%p c=%p o=%d\n", sel, c, o);
- if(cas->receivedp != nil)
- *cas->receivedp = true;
- if(cas->sg.elem != nil)
- c->elemtype->alg->copy(c->elemsize, cas->sg.elem, sg->elem);
- gp = sg->g;
- gp->param = sg;
- if(sg->releasetime)
- sg->releasetime = runtime·cputicks();
- runtime·ready(gp);
- goto retc;
-
-rclose:
- // read at end of closed channel
- selunlock(sel);
- if(cas->receivedp != nil)
- *cas->receivedp = false;
- if(cas->sg.elem != nil)
- c->elemtype->alg->copy(c->elemsize, cas->sg.elem, nil);
- if(raceenabled)
- runtime·raceacquire(c);
- goto retc;
-
-syncsend:
- // can send to sleeping receiver (sg)
- if(raceenabled) {
- runtime·racereadobjectpc(cas->sg.elem, c->elemtype, cas->pc, chansend);
- racesync(c, sg);
- }
- selunlock(sel);
- if(debug)
- runtime·printf("syncsend: sel=%p c=%p o=%d\n", sel, c, o);
- if(sg->elem != nil)
- c->elemtype->alg->copy(c->elemsize, sg->elem, cas->sg.elem);
- gp = sg->g;
- gp->param = sg;
- if(sg->releasetime)
- sg->releasetime = runtime·cputicks();
- runtime·ready(gp);
-
-retc:
- // return pc corresponding to chosen case.
- // Set boolean passed during select creation
- // (at offset selp + cas->so) to true.
- // If cas->so == 0, this is a reflect-driven select and we
- // don't need to update the boolean.
- pc = cas->pc;
- if(cas->so > 0) {
- as = (byte*)selp + cas->so;
- *as = true;
- }
- if(cas->sg.releasetime > 0)
- runtime·blockevent(cas->sg.releasetime - t0, 2);
- return pc;
-
-sclose:
- // send on closed channel
- selunlock(sel);
- runtime·panicstring("send on closed channel");
- return nil; // not reached
-}
-
-// This struct must match ../reflect/value.go:/runtimeSelect.
-typedef struct runtimeSelect runtimeSelect;
-struct runtimeSelect
-{
- uintptr dir;
- ChanType *typ;
- Hchan *ch;
- byte *val;
-};
-
-// This enum must match ../reflect/value.go:/SelectDir.
-enum SelectDir {
- SelectSend = 1,
- SelectRecv,
- SelectDefault,
-};
-
-func reflect·rselect(cases Slice) (chosen int, recvOK bool) {
- int32 i;
- Select *sel;
- runtimeSelect* rcase, *rc;
-
- chosen = -1;
- recvOK = false;
-
- rcase = (runtimeSelect*)cases.array;
-
- // FlagNoScan is safe here, because all objects are also referenced from cases.
- sel = runtime·mallocgc(selectsize(cases.len), 0, FlagNoScan);
- runtime·newselect(sel, selectsize(cases.len), cases.len);
- for(i=0; i<cases.len; i++) {
- rc = &rcase[i];
- switch(rc->dir) {
- case SelectDefault:
- selectdefault(sel, (void*)i, 0);
- break;
- case SelectSend:
- if(rc->ch == nil)
- break;
- selectsend(sel, rc->ch, (void*)i, rc->val, 0);
- break;
- case SelectRecv:
- if(rc->ch == nil)
- break;
- selectrecv(sel, rc->ch, (void*)i, rc->val, &recvOK, 0);
- break;
- }
- }
-
- chosen = (intgo)(uintptr)selectgo(&sel);
-}
-
-static void closechan(Hchan *c, void *pc);
-
-#pragma textflag NOSPLIT
-func closechan(c *Hchan) {
- closechan(c, runtime·getcallerpc(&c));
-}
-
-#pragma textflag NOSPLIT
-func reflect·chanclose(c *Hchan) {
- closechan(c, runtime·getcallerpc(&c));
-}
-
-static void
-closechan(Hchan *c, void *pc)
-{
- SudoG *sg;
- G* gp;
-
- if(c == nil)
- runtime·panicstring("close of nil channel");
-
- runtime·lock(c);
- if(c->closed) {
- runtime·unlock(c);
- runtime·panicstring("close of closed channel");
- }
-
- if(raceenabled) {
- runtime·racewritepc(c, pc, runtime·closechan);
- runtime·racerelease(c);
- }
-
- c->closed = true;
-
- // release all readers
- for(;;) {
- sg = dequeue(&c->recvq);
- if(sg == nil)
- break;
- gp = sg->g;
- gp->param = nil;
- if(sg->releasetime)
- sg->releasetime = runtime·cputicks();
- runtime·ready(gp);
- }
-
- // release all writers
- for(;;) {
- sg = dequeue(&c->sendq);
- if(sg == nil)
- break;
- gp = sg->g;
- gp->param = nil;
- if(sg->releasetime)
- sg->releasetime = runtime·cputicks();
- runtime·ready(gp);
- }
-
- runtime·unlock(c);
-}
-
-func reflect·chanlen(c *Hchan) (len int) {
- if(c == nil)
- len = 0;
- else
- len = c->qcount;
-}
-
-func reflect·chancap(c *Hchan) (cap int) {
- if(c == nil)
- cap = 0;
- else
- cap = c->dataqsiz;
-}
-
-static SudoG*
-dequeue(WaitQ *q)
-{
- SudoG *sgp;
-
-loop:
- sgp = q->first;
- if(sgp == nil)
- return nil;
- q->first = sgp->link;
- if(q->last == sgp)
- q->last = nil;
-
- // if sgp participates in a select and is already signaled, ignore it
- if(sgp->selectdone != nil) {
- // claim the right to signal
- if(*sgp->selectdone != 0 || !runtime·cas(sgp->selectdone, 0, 1))
- goto loop;
- }
-
- return sgp;
-}
-
-static void
-dequeueg(WaitQ *q)
-{
- SudoG **l, *sgp, *prevsgp;
-
- prevsgp = nil;
- for(l=&q->first; (sgp=*l) != nil; l=&sgp->link, prevsgp=sgp) {
- if(sgp->g == g) {
- *l = sgp->link;
- if(q->last == sgp)
- q->last = prevsgp;
- break;
- }
- }
-}
-
-static void
-enqueue(WaitQ *q, SudoG *sgp)
-{
- sgp->link = nil;
- if(q->first == nil) {
- q->first = sgp;
- q->last = sgp;
- return;
- }
- q->last->link = sgp;
- q->last = sgp;
-}
-
-static void
-racesync(Hchan *c, SudoG *sg)
-{
- runtime·racerelease(chanbuf(c, 0));
- runtime·raceacquireg(sg->g, chanbuf(c, 0));
- runtime·racereleaseg(sg->g, chanbuf(c, 0));
- runtime·raceacquire(chanbuf(c, 0));
-}
diff --git a/src/pkg/runtime/chan.h b/src/pkg/runtime/chan.h
index 043ef7d21c..c34ff1533d 100644
--- a/src/pkg/runtime/chan.h
+++ b/src/pkg/runtime/chan.h
@@ -5,21 +5,9 @@
#define MAXALIGN 8
typedef struct WaitQ WaitQ;
-typedef struct SudoG SudoG;
typedef struct Select Select;
typedef struct Scase Scase;
-// Known to compiler.
-// Changes here must also be made in src/cmd/gc/select.c's selecttype.
-struct SudoG
-{
- G* g;
- uint32* selectdone;
- SudoG* link;
- byte* elem; // data element
- int64 releasetime;
-};
-
struct WaitQ
{
SudoG* first;
@@ -32,13 +20,13 @@ struct Hchan
uintgo dataqsiz; // size of the circular q
byte* buf;
uint16 elemsize;
- bool closed;
+ uint32 closed;
Type* elemtype; // element type
uintgo sendx; // send index
uintgo recvx; // receive index
WaitQ recvq; // list of recv waiters
WaitQ sendq; // list of send waiters
- Lock;
+ Mutex lock;
};
// Buffer follows Hchan immediately in memory.
@@ -59,12 +47,13 @@ enum
// Changes here must also be made in src/cmd/gc/select.c's selecttype.
struct Scase
{
- SudoG sg; // must be first member (cast to Scase)
+ void* elem; // data element
Hchan* chan; // chan
- byte* pc; // return pc
+ uintptr pc; // return pc
uint16 kind;
uint16 so; // vararg of selected bool
bool* receivedp; // pointer to received bool (recv2)
+ int64 releasetime;
};
// Known to compiler.
diff --git a/src/pkg/runtime/chan_test.go b/src/pkg/runtime/chan_test.go
index 9ffdc07dc7..01632892ed 100644
--- a/src/pkg/runtime/chan_test.go
+++ b/src/pkg/runtime/chan_test.go
@@ -198,6 +198,26 @@ func TestChan(t *testing.T) {
}
}
+func TestNonblockRecvRace(t *testing.T) {
+ n := 10000
+ if testing.Short() {
+ n = 100
+ }
+ for i := 0; i < n; i++ {
+ c := make(chan int, 1)
+ c <- 1
+ go func() {
+ select {
+ case <-c:
+ default:
+ t.Fatal("chan is not ready")
+ }
+ }()
+ close(c)
+ <-c
+ }
+}
+
func TestSelfSelect(t *testing.T) {
// Ensure that send/recv on the same chan in select
// does not crash nor deadlock.
@@ -430,6 +450,38 @@ func TestMultiConsumer(t *testing.T) {
}
}
+func TestShrinkStackDuringBlockedSend(t *testing.T) {
+ // make sure that channel operations still work when we are
+ // blocked on a channel send and we shrink the stack.
+ // NOTE: this test probably won't fail unless stack.c:StackDebug
+ // is set to >= 1.
+ const n = 10
+ c := make(chan int)
+ done := make(chan struct{})
+
+ go func() {
+ for i := 0; i < n; i++ {
+ c <- i
+ // use lots of stack, briefly.
+ stackGrowthRecursive(20)
+ }
+ done <- struct{}{}
+ }()
+
+ for i := 0; i < n; i++ {
+ x := <-c
+ if x != i {
+ t.Errorf("bad channel read: want %d, got %d", i, x)
+ }
+ // Waste some time so sender can finish using lots of stack
+ // and block in channel send.
+ time.Sleep(1 * time.Millisecond)
+ // trigger GC which will shrink the stack of the sender.
+ runtime.GC()
+ }
+ <-done
+}
+
func BenchmarkChanNonblocking(b *testing.B) {
myc := make(chan int)
b.RunParallel(func(pb *testing.PB) {
diff --git a/src/pkg/runtime/cpuprof.go b/src/pkg/runtime/cpuprof.go
new file mode 100644
index 0000000000..8b1c1c6327
--- /dev/null
+++ b/src/pkg/runtime/cpuprof.go
@@ -0,0 +1,425 @@
+// 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.
+
+// CPU profiling.
+// Based on algorithms and data structures used in
+// http://code.google.com/p/google-perftools/.
+//
+// The main difference between this code and the google-perftools
+// code is that this code is written to allow copying the profile data
+// to an arbitrary io.Writer, while the google-perftools code always
+// writes to an operating system file.
+//
+// The signal handler for the profiling clock tick adds a new stack trace
+// to a hash table tracking counts for recent traces. Most clock ticks
+// hit in the cache. In the event of a cache miss, an entry must be
+// evicted from the hash table, copied to a log that will eventually be
+// written as profile data. The google-perftools code flushed the
+// log itself during the signal handler. This code cannot do that, because
+// the io.Writer might block or need system calls or locks that are not
+// safe to use from within the signal handler. Instead, we split the log
+// into two halves and let the signal handler fill one half while a goroutine
+// is writing out the other half. When the signal handler fills its half, it
+// offers to swap with the goroutine. If the writer is not done with its half,
+// we lose the stack trace for this clock tick (and record that loss).
+// The goroutine interacts with the signal handler by calling getprofile() to
+// get the next log piece to write, implicitly handing back the last log
+// piece it obtained.
+//
+// The state of this dance between the signal handler and the goroutine
+// is encoded in the Profile.handoff field. If handoff == 0, then the goroutine
+// is not using either log half and is waiting (or will soon be waiting) for
+// a new piece by calling notesleep(&p->wait). If the signal handler
+// changes handoff from 0 to non-zero, it must call notewakeup(&p->wait)
+// to wake the goroutine. The value indicates the number of entries in the
+// log half being handed off. The goroutine leaves the non-zero value in
+// place until it has finished processing the log half and then flips the number
+// back to zero. Setting the high bit in handoff means that the profiling is over,
+// and the goroutine is now in charge of flushing the data left in the hash table
+// to the log and returning that data.
+//
+// The handoff field is manipulated using atomic operations.
+// For the most part, the manipulation of handoff is orderly: if handoff == 0
+// then the signal handler owns it and can change it to non-zero.
+// If handoff != 0 then the goroutine owns it and can change it to zero.
+// If that were the end of the story then we would not need to manipulate
+// handoff using atomic operations. The operations are needed, however,
+// in order to let the log closer set the high bit to indicate "EOF" safely
+// in the situation when normally the goroutine "owns" handoff.
+
+package runtime
+
+import "unsafe"
+
+const (
+ numBuckets = 1 << 10
+ logSize = 1 << 17
+ assoc = 4
+ maxCPUProfStack = 64
+)
+
+type cpuprofEntry struct {
+ count uintptr
+ depth uintptr
+ stack [maxCPUProfStack]uintptr
+}
+
+type cpuProfile struct {
+ on bool // profiling is on
+ wait note // goroutine waits here
+ count uintptr // tick count
+ evicts uintptr // eviction count
+ lost uintptr // lost ticks that need to be logged
+
+ // Active recent stack traces.
+ hash [numBuckets]struct {
+ entry [assoc]cpuprofEntry
+ }
+
+ // Log of traces evicted from hash.
+ // Signal handler has filled log[toggle][:nlog].
+ // Goroutine is writing log[1-toggle][:handoff].
+ log [2][logSize / 2]uintptr
+ nlog uintptr
+ toggle int32
+ handoff uint32
+
+ // Writer state.
+ // Writer maintains its own toggle to avoid races
+ // looking at signal handler's toggle.
+ wtoggle uint32
+ wholding bool // holding & need to release a log half
+ flushing bool // flushing hash table - profile is over
+ eodSent bool // special end-of-data record sent; => flushing
+}
+
+var (
+ cpuprofLock mutex
+ cpuprof *cpuProfile
+
+ eod = [3]uintptr{0, 1, 0}
+)
+
+func setcpuprofilerate_m() // proc.c
+
+func setcpuprofilerate(hz int32) {
+ g := getg()
+ g.m.scalararg[0] = uintptr(hz)
+ onM(setcpuprofilerate_m)
+}
+
+// lostProfileData is a no-op function used in profiles
+// to mark the number of profiling stack traces that were
+// discarded due to slow data writers.
+func lostProfileData() {}
+
+// SetCPUProfileRate sets the CPU profiling rate to hz samples per second.
+// If hz <= 0, SetCPUProfileRate turns off profiling.
+// If the profiler is on, the rate cannot be changed without first turning it off.
+//
+// Most clients should use the runtime/pprof package or
+// the testing package's -test.cpuprofile flag instead of calling
+// SetCPUProfileRate directly.
+func SetCPUProfileRate(hz int) {
+ // Clamp hz to something reasonable.
+ if hz < 0 {
+ hz = 0
+ }
+ if hz > 1000000 {
+ hz = 1000000
+ }
+
+ lock(&cpuprofLock)
+ if hz > 0 {
+ if cpuprof == nil {
+ cpuprof = (*cpuProfile)(sysAlloc(unsafe.Sizeof(cpuProfile{}), &memstats.other_sys))
+ if cpuprof == nil {
+ print("runtime: cpu profiling cannot allocate memory\n")
+ unlock(&cpuprofLock)
+ return
+ }
+ }
+ if cpuprof.on || cpuprof.handoff != 0 {
+ print("runtime: cannot set cpu profile rate until previous profile has finished.\n")
+ unlock(&cpuprofLock)
+ return
+ }
+
+ cpuprof.on = true
+ // pprof binary header format.
+ // http://code.google.com/p/google-perftools/source/browse/trunk/src/profiledata.cc#117
+ p := &cpuprof.log[0]
+ p[0] = 0 // count for header
+ p[1] = 3 // depth for header
+ p[2] = 0 // version number
+ p[3] = uintptr(1e6 / hz) // period (microseconds)
+ p[4] = 0
+ cpuprof.nlog = 5
+ cpuprof.toggle = 0
+ cpuprof.wholding = false
+ cpuprof.wtoggle = 0
+ cpuprof.flushing = false
+ cpuprof.eodSent = false
+ noteclear(&cpuprof.wait)
+
+ setcpuprofilerate(int32(hz))
+ } else if cpuprof != nil && cpuprof.on {
+ setcpuprofilerate(0)
+ cpuprof.on = false
+
+ // Now add is not running anymore, and getprofile owns the entire log.
+ // Set the high bit in prof->handoff to tell getprofile.
+ for {
+ n := cpuprof.handoff
+ if n&0x80000000 != 0 {
+ print("runtime: setcpuprofile(off) twice\n")
+ }
+ if cas(&cpuprof.handoff, n, n|0x80000000) {
+ if n == 0 {
+ // we did the transition from 0 -> nonzero so we wake getprofile
+ notewakeup(&cpuprof.wait)
+ }
+ break
+ }
+ }
+ }
+ unlock(&cpuprofLock)
+}
+
+func cpuproftick(pc *uintptr, n int32) {
+ if n > maxCPUProfStack {
+ n = maxCPUProfStack
+ }
+ s := (*[maxCPUProfStack]uintptr)(unsafe.Pointer(pc))[:n]
+ cpuprof.add(s)
+}
+
+// add adds the stack trace to the profile.
+// It is called from signal handlers and other limited environments
+// and cannot allocate memory or acquire locks that might be
+// held at the time of the signal, nor can it use substantial amounts
+// of stack. It is allowed to call evict.
+func (p *cpuProfile) add(pc []uintptr) {
+ // Compute hash.
+ h := uintptr(0)
+ for _, x := range pc {
+ h = h<<8 | (h >> (8 * (unsafe.Sizeof(h) - 1)))
+ h += x*31 + x*7 + x*3
+ }
+ p.count++
+
+ // Add to entry count if already present in table.
+ b := &p.hash[h%numBuckets]
+Assoc:
+ for i := range b.entry {
+ e := &b.entry[i]
+ if e.depth != uintptr(len(pc)) {
+ continue
+ }
+ for j := range pc {
+ if e.stack[j] != pc[j] {
+ continue Assoc
+ }
+ }
+ e.count++
+ return
+ }
+
+ // Evict entry with smallest count.
+ var e *cpuprofEntry
+ for i := range b.entry {
+ if e == nil || b.entry[i].count < e.count {
+ e = &b.entry[i]
+ }
+ }
+ if e.count > 0 {
+ if !p.evict(e) {
+ // Could not evict entry. Record lost stack.
+ p.lost++
+ return
+ }
+ p.evicts++
+ }
+
+ // Reuse the newly evicted entry.
+ e.depth = uintptr(len(pc))
+ e.count = 1
+ copy(e.stack[:], pc)
+}
+
+// evict copies the given entry's data into the log, so that
+// the entry can be reused. evict is called from add, which
+// is called from the profiling signal handler, so it must not
+// allocate memory or block. It is safe to call flushlog.
+// evict returns true if the entry was copied to the log,
+// false if there was no room available.
+func (p *cpuProfile) evict(e *cpuprofEntry) bool {
+ d := e.depth
+ nslot := d + 2
+ log := &p.log[p.toggle]
+ if p.nlog+nslot > uintptr(len(p.log[0])) {
+ if !p.flushlog() {
+ return false
+ }
+ log = &p.log[p.toggle]
+ }
+
+ q := p.nlog
+ log[q] = e.count
+ q++
+ log[q] = d
+ q++
+ copy(log[q:], e.stack[:d])
+ q += d
+ p.nlog = q
+ e.count = 0
+ return true
+}
+
+// flushlog tries to flush the current log and switch to the other one.
+// flushlog is called from evict, called from add, called from the signal handler,
+// so it cannot allocate memory or block. It can try to swap logs with
+// the writing goroutine, as explained in the comment at the top of this file.
+func (p *cpuProfile) flushlog() bool {
+ if !cas(&p.handoff, 0, uint32(p.nlog)) {
+ return false
+ }
+ notewakeup(&p.wait)
+
+ p.toggle = 1 - p.toggle
+ log := &p.log[p.toggle]
+ q := uintptr(0)
+ if p.lost > 0 {
+ lostPC := funcPC(lostProfileData)
+ log[0] = p.lost
+ log[1] = 1
+ log[2] = lostPC
+ q = 3
+ p.lost = 0
+ }
+ p.nlog = q
+ return true
+}
+
+// getprofile blocks until the next block of profiling data is available
+// and returns it as a []byte. It is called from the writing goroutine.
+func (p *cpuProfile) getprofile() []byte {
+ if p == nil {
+ return nil
+ }
+
+ if p.wholding {
+ // Release previous log to signal handling side.
+ // Loop because we are racing against SetCPUProfileRate(0).
+ for {
+ n := p.handoff
+ if n == 0 {
+ print("runtime: phase error during cpu profile handoff\n")
+ return nil
+ }
+ if n&0x80000000 != 0 {
+ p.wtoggle = 1 - p.wtoggle
+ p.wholding = false
+ p.flushing = true
+ goto Flush
+ }
+ if cas(&p.handoff, n, 0) {
+ break
+ }
+ }
+ p.wtoggle = 1 - p.wtoggle
+ p.wholding = false
+ }
+
+ if p.flushing {
+ goto Flush
+ }
+
+ if !p.on && p.handoff == 0 {
+ return nil
+ }
+
+ // Wait for new log.
+ notetsleepg(&p.wait, -1)
+ noteclear(&p.wait)
+
+ switch n := p.handoff; {
+ case n == 0:
+ print("runtime: phase error during cpu profile wait\n")
+ return nil
+ case n == 0x80000000:
+ p.flushing = true
+ goto Flush
+ default:
+ n &^= 0x80000000
+
+ // Return new log to caller.
+ p.wholding = true
+
+ return uintptrBytes(p.log[p.wtoggle][:n])
+ }
+
+ // In flush mode.
+ // Add is no longer being called. We own the log.
+ // Also, p->handoff is non-zero, so flushlog will return false.
+ // Evict the hash table into the log and return it.
+Flush:
+ for i := range p.hash {
+ b := &p.hash[i]
+ for j := range b.entry {
+ e := &b.entry[j]
+ if e.count > 0 && !p.evict(e) {
+ // Filled the log. Stop the loop and return what we've got.
+ break Flush
+ }
+ }
+ }
+
+ // Return pending log data.
+ if p.nlog > 0 {
+ // Note that we're using toggle now, not wtoggle,
+ // because we're working on the log directly.
+ n := p.nlog
+ p.nlog = 0
+ return uintptrBytes(p.log[p.toggle][:n])
+ }
+
+ // Made it through the table without finding anything to log.
+ if !p.eodSent {
+ // We may not have space to append this to the partial log buf,
+ // so we always return a new slice for the end-of-data marker.
+ p.eodSent = true
+ return uintptrBytes(eod[:])
+ }
+
+ // Finally done. Clean up and return nil.
+ p.flushing = false
+ if !cas(&p.handoff, p.handoff, 0) {
+ print("runtime: profile flush racing with something\n")
+ }
+ return nil
+}
+
+func uintptrBytes(p []uintptr) (ret []byte) {
+ pp := (*sliceStruct)(unsafe.Pointer(&p))
+ rp := (*sliceStruct)(unsafe.Pointer(&ret))
+
+ rp.array = pp.array
+ rp.len = pp.len * int(unsafe.Sizeof(p[0]))
+ rp.cap = rp.len
+
+ return
+}
+
+// CPUProfile returns the next chunk of binary CPU profiling stack trace data,
+// blocking until data is available. If profiling is turned off and all the profile
+// data accumulated while it was on has been returned, CPUProfile returns nil.
+// The caller must save the returned data before calling CPUProfile again.
+//
+// Most clients should use the runtime/pprof package or
+// the testing package's -test.cpuprofile flag instead of calling
+// CPUProfile directly.
+func CPUProfile() []byte {
+ return cpuprof.getprofile()
+}
diff --git a/src/pkg/runtime/cpuprof.goc b/src/pkg/runtime/cpuprof.goc
deleted file mode 100644
index faaea29435..0000000000
--- a/src/pkg/runtime/cpuprof.goc
+++ /dev/null
@@ -1,433 +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.
-
-// CPU profiling.
-// Based on algorithms and data structures used in
-// http://code.google.com/p/google-perftools/.
-//
-// The main difference between this code and the google-perftools
-// code is that this code is written to allow copying the profile data
-// to an arbitrary io.Writer, while the google-perftools code always
-// writes to an operating system file.
-//
-// The signal handler for the profiling clock tick adds a new stack trace
-// to a hash table tracking counts for recent traces. Most clock ticks
-// hit in the cache. In the event of a cache miss, an entry must be
-// evicted from the hash table, copied to a log that will eventually be
-// written as profile data. The google-perftools code flushed the
-// log itself during the signal handler. This code cannot do that, because
-// the io.Writer might block or need system calls or locks that are not
-// safe to use from within the signal handler. Instead, we split the log
-// into two halves and let the signal handler fill one half while a goroutine
-// is writing out the other half. When the signal handler fills its half, it
-// offers to swap with the goroutine. If the writer is not done with its half,
-// we lose the stack trace for this clock tick (and record that loss).
-// The goroutine interacts with the signal handler by calling getprofile() to
-// get the next log piece to write, implicitly handing back the last log
-// piece it obtained.
-//
-// The state of this dance between the signal handler and the goroutine
-// is encoded in the Profile.handoff field. If handoff == 0, then the goroutine
-// is not using either log half and is waiting (or will soon be waiting) for
-// a new piece by calling notesleep(&p->wait). If the signal handler
-// changes handoff from 0 to non-zero, it must call notewakeup(&p->wait)
-// to wake the goroutine. The value indicates the number of entries in the
-// log half being handed off. The goroutine leaves the non-zero value in
-// place until it has finished processing the log half and then flips the number
-// back to zero. Setting the high bit in handoff means that the profiling is over,
-// and the goroutine is now in charge of flushing the data left in the hash table
-// to the log and returning that data.
-//
-// The handoff field is manipulated using atomic operations.
-// For the most part, the manipulation of handoff is orderly: if handoff == 0
-// then the signal handler owns it and can change it to non-zero.
-// If handoff != 0 then the goroutine owns it and can change it to zero.
-// If that were the end of the story then we would not need to manipulate
-// handoff using atomic operations. The operations are needed, however,
-// in order to let the log closer set the high bit to indicate "EOF" safely
-// in the situation when normally the goroutine "owns" handoff.
-
-package runtime
-#include "runtime.h"
-#include "arch_GOARCH.h"
-#include "malloc.h"
-
-enum
-{
- HashSize = 1<<10,
- LogSize = 1<<17,
- Assoc = 4,
- MaxStack = 64,
-};
-
-typedef struct Profile Profile;
-typedef struct Bucket Bucket;
-typedef struct Entry Entry;
-
-struct Entry {
- uintptr count;
- uintptr depth;
- uintptr stack[MaxStack];
-};
-
-struct Bucket {
- Entry entry[Assoc];
-};
-
-struct Profile {
- bool on; // profiling is on
- Note wait; // goroutine waits here
- uintptr count; // tick count
- uintptr evicts; // eviction count
- uintptr lost; // lost ticks that need to be logged
-
- // Active recent stack traces.
- Bucket hash[HashSize];
-
- // Log of traces evicted from hash.
- // Signal handler has filled log[toggle][:nlog].
- // Goroutine is writing log[1-toggle][:handoff].
- uintptr log[2][LogSize/2];
- uintptr nlog;
- int32 toggle;
- uint32 handoff;
-
- // Writer state.
- // Writer maintains its own toggle to avoid races
- // looking at signal handler's toggle.
- uint32 wtoggle;
- bool wholding; // holding & need to release a log half
- bool flushing; // flushing hash table - profile is over
- bool eod_sent; // special end-of-data record sent; => flushing
-};
-
-static Lock lk;
-static Profile *prof;
-
-static void tick(uintptr*, int32);
-static void add(Profile*, uintptr*, int32);
-static bool evict(Profile*, Entry*);
-static bool flushlog(Profile*);
-
-static uintptr eod[3] = {0, 1, 0};
-
-// LostProfileData is a no-op function used in profiles
-// to mark the number of profiling stack traces that were
-// discarded due to slow data writers.
-static void
-LostProfileData(void)
-{
-}
-
-// SetCPUProfileRate sets the CPU profiling rate.
-// The user documentation is in debug.go.
-void
-runtime·SetCPUProfileRate(intgo hz)
-{
- uintptr *p;
- uintptr n;
-
- // Clamp hz to something reasonable.
- if(hz < 0)
- hz = 0;
- if(hz > 1000000)
- hz = 1000000;
-
- runtime·lock(&lk);
- if(hz > 0) {
- if(prof == nil) {
- prof = runtime·SysAlloc(sizeof *prof, &mstats.other_sys);
- if(prof == nil) {
- runtime·printf("runtime: cpu profiling cannot allocate memory\n");
- runtime·unlock(&lk);
- return;
- }
- }
- if(prof->on || prof->handoff != 0) {
- runtime·printf("runtime: cannot set cpu profile rate until previous profile has finished.\n");
- runtime·unlock(&lk);
- return;
- }
-
- prof->on = true;
- p = prof->log[0];
- // pprof binary header format.
- // http://code.google.com/p/google-perftools/source/browse/trunk/src/profiledata.cc#117
- *p++ = 0; // count for header
- *p++ = 3; // depth for header
- *p++ = 0; // version number
- *p++ = 1000000 / hz; // period (microseconds)
- *p++ = 0;
- prof->nlog = p - prof->log[0];
- prof->toggle = 0;
- prof->wholding = false;
- prof->wtoggle = 0;
- prof->flushing = false;
- prof->eod_sent = false;
- runtime·noteclear(&prof->wait);
-
- runtime·setcpuprofilerate(tick, hz);
- } else if(prof != nil && prof->on) {
- runtime·setcpuprofilerate(nil, 0);
- prof->on = false;
-
- // Now add is not running anymore, and getprofile owns the entire log.
- // Set the high bit in prof->handoff to tell getprofile.
- for(;;) {
- n = prof->handoff;
- if(n&0x80000000)
- runtime·printf("runtime: setcpuprofile(off) twice");
- if(runtime·cas(&prof->handoff, n, n|0x80000000))
- break;
- }
- if(n == 0) {
- // we did the transition from 0 -> nonzero so we wake getprofile
- runtime·notewakeup(&prof->wait);
- }
- }
- runtime·unlock(&lk);
-}
-
-static void
-tick(uintptr *pc, int32 n)
-{
- add(prof, pc, n);
-}
-
-// add adds the stack trace to the profile.
-// It is called from signal handlers and other limited environments
-// and cannot allocate memory or acquire locks that might be
-// held at the time of the signal, nor can it use substantial amounts
-// of stack. It is allowed to call evict.
-static void
-add(Profile *p, uintptr *pc, int32 n)
-{
- int32 i, j;
- uintptr h, x;
- Bucket *b;
- Entry *e;
-
- if(n > MaxStack)
- n = MaxStack;
-
- // Compute hash.
- h = 0;
- for(i=0; i<n; i++) {
- h = h<<8 | (h>>(8*(sizeof(h)-1)));
- x = pc[i];
- h += x*31 + x*7 + x*3;
- }
- p->count++;
-
- // Add to entry count if already present in table.
- b = &p->hash[h%HashSize];
- for(i=0; i<Assoc; i++) {
- e = &b->entry[i];
- if(e->depth != n)
- continue;
- for(j=0; j<n; j++)
- if(e->stack[j] != pc[j])
- goto ContinueAssoc;
- e->count++;
- return;
- ContinueAssoc:;
- }
-
- // Evict entry with smallest count.
- e = &b->entry[0];
- for(i=1; i<Assoc; i++)
- if(b->entry[i].count < e->count)
- e = &b->entry[i];
- if(e->count > 0) {
- if(!evict(p, e)) {
- // Could not evict entry. Record lost stack.
- p->lost++;
- return;
- }
- p->evicts++;
- }
-
- // Reuse the newly evicted entry.
- e->depth = n;
- e->count = 1;
- for(i=0; i<n; i++)
- e->stack[i] = pc[i];
-}
-
-// evict copies the given entry's data into the log, so that
-// the entry can be reused. evict is called from add, which
-// is called from the profiling signal handler, so it must not
-// allocate memory or block. It is safe to call flushLog.
-// evict returns true if the entry was copied to the log,
-// false if there was no room available.
-static bool
-evict(Profile *p, Entry *e)
-{
- int32 i, d, nslot;
- uintptr *log, *q;
-
- d = e->depth;
- nslot = d+2;
- log = p->log[p->toggle];
- if(p->nlog+nslot > nelem(p->log[0])) {
- if(!flushlog(p))
- return false;
- log = p->log[p->toggle];
- }
-
- q = log+p->nlog;
- *q++ = e->count;
- *q++ = d;
- for(i=0; i<d; i++)
- *q++ = e->stack[i];
- p->nlog = q - log;
- e->count = 0;
- return true;
-}
-
-// flushlog tries to flush the current log and switch to the other one.
-// flushlog is called from evict, called from add, called from the signal handler,
-// so it cannot allocate memory or block. It can try to swap logs with
-// the writing goroutine, as explained in the comment at the top of this file.
-static bool
-flushlog(Profile *p)
-{
- uintptr *log, *q;
-
- if(!runtime·cas(&p->handoff, 0, p->nlog))
- return false;
- runtime·notewakeup(&p->wait);
-
- p->toggle = 1 - p->toggle;
- log = p->log[p->toggle];
- q = log;
- if(p->lost > 0) {
- *q++ = p->lost;
- *q++ = 1;
- *q++ = (uintptr)LostProfileData;
- p->lost = 0;
- }
- p->nlog = q - log;
- return true;
-}
-
-// getprofile blocks until the next block of profiling data is available
-// and returns it as a []byte. It is called from the writing goroutine.
-Slice
-getprofile(Profile *p)
-{
- uint32 i, j, n;
- Slice ret;
- Bucket *b;
- Entry *e;
-
- ret.array = nil;
- ret.len = 0;
- ret.cap = 0;
-
- if(p == nil)
- return ret;
-
- if(p->wholding) {
- // Release previous log to signal handling side.
- // Loop because we are racing against SetCPUProfileRate(0).
- for(;;) {
- n = p->handoff;
- if(n == 0) {
- runtime·printf("runtime: phase error during cpu profile handoff\n");
- return ret;
- }
- if(n & 0x80000000) {
- p->wtoggle = 1 - p->wtoggle;
- p->wholding = false;
- p->flushing = true;
- goto flush;
- }
- if(runtime·cas(&p->handoff, n, 0))
- break;
- }
- p->wtoggle = 1 - p->wtoggle;
- p->wholding = false;
- }
-
- if(p->flushing)
- goto flush;
-
- if(!p->on && p->handoff == 0)
- return ret;
-
- // Wait for new log.
- runtime·notetsleepg(&p->wait, -1);
- runtime·noteclear(&p->wait);
-
- n = p->handoff;
- if(n == 0) {
- runtime·printf("runtime: phase error during cpu profile wait\n");
- return ret;
- }
- if(n == 0x80000000) {
- p->flushing = true;
- goto flush;
- }
- n &= ~0x80000000;
-
- // Return new log to caller.
- p->wholding = true;
-
- ret.array = (byte*)p->log[p->wtoggle];
- ret.len = n*sizeof(uintptr);
- ret.cap = ret.len;
- return ret;
-
-flush:
- // In flush mode.
- // Add is no longer being called. We own the log.
- // Also, p->handoff is non-zero, so flushlog will return false.
- // Evict the hash table into the log and return it.
- for(i=0; i<HashSize; i++) {
- b = &p->hash[i];
- for(j=0; j<Assoc; j++) {
- e = &b->entry[j];
- if(e->count > 0 && !evict(p, e)) {
- // Filled the log. Stop the loop and return what we've got.
- goto breakflush;
- }
- }
- }
-breakflush:
-
- // Return pending log data.
- if(p->nlog > 0) {
- // Note that we're using toggle now, not wtoggle,
- // because we're working on the log directly.
- ret.array = (byte*)p->log[p->toggle];
- ret.len = p->nlog*sizeof(uintptr);
- ret.cap = ret.len;
- p->nlog = 0;
- return ret;
- }
-
- // Made it through the table without finding anything to log.
- if(!p->eod_sent) {
- // We may not have space to append this to the partial log buf,
- // so we always return a new slice for the end-of-data marker.
- p->eod_sent = true;
- ret.array = (byte*)eod;
- ret.len = sizeof eod;
- ret.cap = ret.len;
- return ret;
- }
-
- // Finally done. Clean up and return nil.
- p->flushing = false;
- if(!runtime·cas(&p->handoff, p->handoff, 0))
- runtime·printf("runtime: profile flush racing with something\n");
- return ret; // set to nil at top of function
-}
-
-// CPUProfile returns the next cpu profile block as a []byte.
-// The user documentation is in debug.go.
-func CPUProfile() (ret Slice) {
- ret = getprofile(prof);
-}
diff --git a/src/pkg/runtime/debug.go b/src/pkg/runtime/debug.go
index d82afb08ec..bb4bd60ed4 100644
--- a/src/pkg/runtime/debug.go
+++ b/src/pkg/runtime/debug.go
@@ -4,6 +4,8 @@
package runtime
+import "unsafe"
+
// Breakpoint executes a breakpoint trap.
func Breakpoint()
@@ -21,152 +23,34 @@ func UnlockOSThread()
// change the current setting.
// The number of logical CPUs on the local machine can be queried with NumCPU.
// This call will go away when the scheduler improves.
-func GOMAXPROCS(n int) int
-
-// NumCPU returns the number of logical CPUs on the local machine.
-func NumCPU() int
-
-// NumCgoCall returns the number of cgo calls made by the current process.
-func NumCgoCall() int64
-
-// NumGoroutine returns the number of goroutines that currently exist.
-func NumGoroutine() int
-
-// MemProfileRate controls the fraction of memory allocations
-// that are recorded and reported in the memory profile.
-// The profiler aims to sample an average of
-// one allocation per MemProfileRate bytes allocated.
-//
-// To include every allocated block in the profile, set MemProfileRate to 1.
-// To turn off profiling entirely, set MemProfileRate to 0.
-//
-// The tools that process the memory profiles assume that the
-// profile rate is constant across the lifetime of the program
-// and equal to the current value. Programs that change the
-// memory profiling rate should do so just once, as early as
-// possible in the execution of the program (for example,
-// at the beginning of main).
-var MemProfileRate int = 512 * 1024
-
-// A MemProfileRecord describes the live objects allocated
-// by a particular call sequence (stack trace).
-type MemProfileRecord struct {
- AllocBytes, FreeBytes int64 // number of bytes allocated, freed
- AllocObjects, FreeObjects int64 // number of objects allocated, freed
- Stack0 [32]uintptr // stack trace for this record; ends at first 0 entry
-}
-
-// InUseBytes returns the number of bytes in use (AllocBytes - FreeBytes).
-func (r *MemProfileRecord) InUseBytes() int64 { return r.AllocBytes - r.FreeBytes }
-
-// InUseObjects returns the number of objects in use (AllocObjects - FreeObjects).
-func (r *MemProfileRecord) InUseObjects() int64 {
- return r.AllocObjects - r.FreeObjects
+func GOMAXPROCS(n int) int {
+ g := getg()
+ g.m.scalararg[0] = uintptr(n)
+ onM(gomaxprocs_m)
+ n = int(g.m.scalararg[0])
+ g.m.scalararg[0] = 0
+ return n
}
-// Stack returns the stack trace associated with the record,
-// a prefix of r.Stack0.
-func (r *MemProfileRecord) Stack() []uintptr {
- for i, v := range r.Stack0 {
- if v == 0 {
- return r.Stack0[0:i]
- }
- }
- return r.Stack0[0:]
-}
-
-// MemProfile returns n, the number of records in the current memory profile.
-// If len(p) >= n, MemProfile copies the profile into p and returns n, true.
-// If len(p) < n, MemProfile does not change p and returns n, false.
-//
-// If inuseZero is true, the profile includes allocation records
-// where r.AllocBytes > 0 but r.AllocBytes == r.FreeBytes.
-// These are sites where memory was allocated, but it has all
-// been released back to the runtime.
-//
-// Most clients should use the runtime/pprof package or
-// the testing package's -test.memprofile flag instead
-// of calling MemProfile directly.
-func MemProfile(p []MemProfileRecord, inuseZero bool) (n int, ok bool)
+func gomaxprocs_m() // proc.c
-// A StackRecord describes a single execution stack.
-type StackRecord struct {
- Stack0 [32]uintptr // stack trace for this record; ends at first 0 entry
+// NumCPU returns the number of logical CPUs on the local machine.
+func NumCPU() int {
+ return int(ncpu)
}
-// Stack returns the stack trace associated with the record,
-// a prefix of r.Stack0.
-func (r *StackRecord) Stack() []uintptr {
- for i, v := range r.Stack0 {
- if v == 0 {
- return r.Stack0[0:i]
- }
+// NumCgoCall returns the number of cgo calls made by the current process.
+func NumCgoCall() int64 {
+ var n int64
+ for mp := (*m)(atomicloadp(unsafe.Pointer(&allm))); mp != nil; mp = mp.alllink {
+ n += int64(mp.ncgocall)
}
- return r.Stack0[0:]
+ return n
}
-// ThreadCreateProfile returns n, the number of records in the thread creation profile.
-// If len(p) >= n, ThreadCreateProfile copies the profile into p and returns n, true.
-// If len(p) < n, ThreadCreateProfile does not change p and returns n, false.
-//
-// Most clients should use the runtime/pprof package instead
-// of calling ThreadCreateProfile directly.
-func ThreadCreateProfile(p []StackRecord) (n int, ok bool)
-
-// GoroutineProfile returns n, the number of records in the active goroutine stack profile.
-// If len(p) >= n, GoroutineProfile copies the profile into p and returns n, true.
-// If len(p) < n, GoroutineProfile does not change p and returns n, false.
-//
-// Most clients should use the runtime/pprof package instead
-// of calling GoroutineProfile directly.
-func GoroutineProfile(p []StackRecord) (n int, ok bool)
-
-// CPUProfile returns the next chunk of binary CPU profiling stack trace data,
-// blocking until data is available. If profiling is turned off and all the profile
-// data accumulated while it was on has been returned, CPUProfile returns nil.
-// The caller must save the returned data before calling CPUProfile again.
-//
-// Most clients should use the runtime/pprof package or
-// the testing package's -test.cpuprofile flag instead of calling
-// CPUProfile directly.
-func CPUProfile() []byte
-
-// SetCPUProfileRate sets the CPU profiling rate to hz samples per second.
-// If hz <= 0, SetCPUProfileRate turns off profiling.
-// If the profiler is on, the rate cannot be changed without first turning it off.
-//
-// Most clients should use the runtime/pprof package or
-// the testing package's -test.cpuprofile flag instead of calling
-// SetCPUProfileRate directly.
-func SetCPUProfileRate(hz int)
-
-// SetBlockProfileRate controls the fraction of goroutine blocking events
-// that are reported in the blocking profile. The profiler aims to sample
-// an average of one blocking event per rate nanoseconds spent blocked.
-//
-// To include every blocking event in the profile, pass rate = 1.
-// To turn off profiling entirely, pass rate <= 0.
-func SetBlockProfileRate(rate int)
-
-// BlockProfileRecord describes blocking events originated
-// at a particular call sequence (stack trace).
-type BlockProfileRecord struct {
- Count int64
- Cycles int64
- StackRecord
+// NumGoroutine returns the number of goroutines that currently exist.
+func NumGoroutine() int {
+ return int(gcount())
}
-// BlockProfile returns n, the number of records in the current blocking profile.
-// If len(p) >= n, BlockProfile copies the profile into p and returns n, true.
-// If len(p) < n, BlockProfile does not change p and returns n, false.
-//
-// Most clients should use the runtime/pprof package or
-// the testing package's -test.blockprofile flag instead
-// of calling BlockProfile directly.
-func BlockProfile(p []BlockProfileRecord) (n int, ok bool)
-
-// Stack formats a stack trace of the calling goroutine into buf
-// and returns the number of bytes written to buf.
-// If all is true, Stack formats stack traces of all other goroutines
-// into buf after the trace for the current goroutine.
-func Stack(buf []byte, all bool) int
+func gcount() int32
diff --git a/src/pkg/runtime/debug/garbage.go b/src/pkg/runtime/debug/garbage.go
index edb3643871..30994f2196 100644
--- a/src/pkg/runtime/debug/garbage.go
+++ b/src/pkg/runtime/debug/garbage.go
@@ -19,14 +19,6 @@ type GCStats struct {
PauseQuantiles []time.Duration
}
-// Implemented in package runtime.
-func readGCStats(*[]time.Duration)
-func enableGC(bool) bool
-func setGCPercent(int) int
-func freeOSMemory()
-func setMaxStack(int) int
-func setMaxThreads(int) int
-
// ReadGCStats reads statistics about garbage collection into stats.
// The number of entries in the pause history is system-dependent;
// stats.Pause slice will be reused if large enough, reallocated otherwise.
@@ -91,9 +83,9 @@ func (x byDuration) Less(i, j int) bool { return x[i] < x[j] }
// at startup, or 100 if the variable is not set.
// A negative percentage disables garbage collection.
func SetGCPercent(percent int) int {
- old := setGCPercent(percent)
+ old := setGCPercent(int32(percent))
runtime.GC()
- return old
+ return int(old)
}
// FreeOSMemory forces a garbage collection followed by an
@@ -145,7 +137,9 @@ func SetMaxThreads(threads int) int {
// that the runtime trigger only a panic, not a crash.
// SetPanicOnFault applies only to the current goroutine.
// It returns the previous setting.
-func SetPanicOnFault(enabled bool) bool
+func SetPanicOnFault(enabled bool) bool {
+ return setPanicOnFault(enabled)
+}
// WriteHeapDump writes a description of the heap and the objects in
// it to the given file descriptor.
diff --git a/src/pkg/runtime/debug/stubs.go b/src/pkg/runtime/debug/stubs.go
new file mode 100644
index 0000000000..8fba6cf34b
--- /dev/null
+++ b/src/pkg/runtime/debug/stubs.go
@@ -0,0 +1,20 @@
+// 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 debug
+
+import (
+ "time"
+)
+
+// Uses assembly to call corresponding runtime-internal functions.
+func setMaxStack(int) int
+func setGCPercent(int32) int32
+func setPanicOnFault(bool) bool
+func setMaxThreads(int) int
+
+// Implemented in package runtime.
+func readGCStats(*[]time.Duration)
+func enableGC(bool) bool
+func freeOSMemory()
diff --git a/src/pkg/runtime/debug/stubs.s b/src/pkg/runtime/debug/stubs.s
new file mode 100644
index 0000000000..d56274f2dc
--- /dev/null
+++ b/src/pkg/runtime/debug/stubs.s
@@ -0,0 +1,21 @@
+// 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.
+
+#include "textflag.h"
+
+#ifdef GOARCH_arm
+#define JMP B
+#endif
+
+TEXT ·setMaxStack(SB),NOSPLIT,$0-0
+ JMP runtime·setMaxStack(SB)
+
+TEXT ·setGCPercent(SB),NOSPLIT,$0-0
+ JMP runtime·setGCPercent(SB)
+
+TEXT ·setPanicOnFault(SB),NOSPLIT,$0-0
+ JMP runtime·setPanicOnFault(SB)
+
+TEXT ·setMaxThreads(SB),NOSPLIT,$0-0
+ JMP runtime·setMaxThreads(SB)
diff --git a/src/pkg/runtime/defs.c b/src/pkg/runtime/defs.c
index 7563344578..b0a9b20d7f 100644
--- a/src/pkg/runtime/defs.c
+++ b/src/pkg/runtime/defs.c
@@ -11,4 +11,5 @@
#include "type.h"
#include "race.h"
#include "chan.h"
-#include "mprof.h"
+#include "defs_GOOS_GOARCH.h"
+#include "os_GOOS.h"
diff --git a/src/pkg/runtime/defs1_linux.go b/src/pkg/runtime/defs1_linux.go
index c0b510ac41..87c6e02a41 100644
--- a/src/pkg/runtime/defs1_linux.go
+++ b/src/pkg/runtime/defs1_linux.go
@@ -33,7 +33,7 @@ type Fpxreg1 C.struct__fpxreg
type Xmmreg1 C.struct__xmmreg
type Fpstate1 C.struct__fpstate
type Fpreg1 C.struct__fpreg
-type Sigaltstack C.struct_sigaltstack
+type SigaltstackT C.struct_sigaltstack
type Mcontext C.mcontext_t
type Ucontext C.ucontext_t
type Sigcontext C.struct_sigcontext
diff --git a/src/pkg/runtime/defs2_linux.go b/src/pkg/runtime/defs2_linux.go
index 60ecc69bb9..980df9ec31 100644
--- a/src/pkg/runtime/defs2_linux.go
+++ b/src/pkg/runtime/defs2_linux.go
@@ -139,7 +139,7 @@ type Timespec C.struct_timespec
type Timeval C.struct_timeval
type Sigaction C.struct_kernel_sigaction
type Siginfo C.siginfo_t
-type Sigaltstack C.struct_sigaltstack
+type SigaltstackT C.struct_sigaltstack
type Sigcontext C.struct_sigcontext
type Ucontext C.struct_ucontext
type Itimerval C.struct_itimerval
diff --git a/src/pkg/runtime/defs_arm_linux.go b/src/pkg/runtime/defs_arm_linux.go
index db0a191542..afd6897e30 100644
--- a/src/pkg/runtime/defs_arm_linux.go
+++ b/src/pkg/runtime/defs_arm_linux.go
@@ -115,7 +115,7 @@ const (
)
type Timespec C.struct_timespec
-type Sigaltstack C.struct_sigaltstack
+type SigaltstackT C.struct_sigaltstack
type Sigcontext C.struct_sigcontext
type Ucontext C.struct_ucontext
type Timeval C.struct_timeval
diff --git a/src/pkg/runtime/defs_darwin_386.h b/src/pkg/runtime/defs_darwin_386.h
index 7b210eebf8..0e0b4fbf79 100644
--- a/src/pkg/runtime/defs_darwin_386.h
+++ b/src/pkg/runtime/defs_darwin_386.h
@@ -124,7 +124,7 @@ typedef struct MachHeader MachHeader;
typedef struct MachNDR MachNDR;
typedef struct MachPort MachPort;
typedef struct StackT StackT;
-typedef struct Sigaction Sigaction;
+typedef struct SigactionT SigactionT;
typedef struct Siginfo Siginfo;
typedef struct Timeval Timeval;
typedef struct Itimerval Itimerval;
@@ -142,7 +142,7 @@ typedef struct FloatState32 FloatState32;
typedef struct ExceptionState32 ExceptionState32;
typedef struct Mcontext32 Mcontext32;
typedef struct Ucontext Ucontext;
-typedef struct Kevent Kevent;
+typedef struct KeventT KeventT;
#pragma pack on
@@ -182,7 +182,7 @@ struct StackT {
};
typedef byte Sighandler[4];
-struct Sigaction {
+struct SigactionT {
byte __sigaction_u[4];
void *sa_tramp;
uint32 sa_mask;
@@ -379,7 +379,7 @@ struct Ucontext {
Mcontext32 *uc_mcontext;
};
-struct Kevent {
+struct KeventT {
uint32 ident;
int16 filter;
uint16 flags;
diff --git a/src/pkg/runtime/defs_darwin_amd64.h b/src/pkg/runtime/defs_darwin_amd64.h
index 2d464a9e50..4bf83c1cb9 100644
--- a/src/pkg/runtime/defs_darwin_amd64.h
+++ b/src/pkg/runtime/defs_darwin_amd64.h
@@ -124,7 +124,7 @@ typedef struct MachHeader MachHeader;
typedef struct MachNDR MachNDR;
typedef struct MachPort MachPort;
typedef struct StackT StackT;
-typedef struct Sigaction Sigaction;
+typedef struct SigactionT SigactionT;
typedef struct Siginfo Siginfo;
typedef struct Timeval Timeval;
typedef struct Itimerval Itimerval;
@@ -142,7 +142,7 @@ typedef struct FloatState32 FloatState32;
typedef struct ExceptionState32 ExceptionState32;
typedef struct Mcontext32 Mcontext32;
typedef struct Ucontext Ucontext;
-typedef struct Kevent Kevent;
+typedef struct KeventT KeventT;
#pragma pack on
@@ -183,7 +183,7 @@ struct StackT {
};
typedef byte Sighandler[8];
-struct Sigaction {
+struct SigactionT {
byte __sigaction_u[8];
void *sa_tramp;
uint32 sa_mask;
@@ -382,7 +382,7 @@ struct Ucontext {
Mcontext64 *uc_mcontext;
};
-struct Kevent {
+struct KeventT {
uint64 ident;
int16 filter;
uint16 flags;
diff --git a/src/pkg/runtime/defs_dragonfly.go b/src/pkg/runtime/defs_dragonfly.go
index 8ebc3a9e1a..555b8f5951 100644
--- a/src/pkg/runtime/defs_dragonfly.go
+++ b/src/pkg/runtime/defs_dragonfly.go
@@ -110,7 +110,7 @@ const (
type Rtprio C.struct_rtprio
type Lwpparams C.struct_lwp_params
-type Sigaltstack C.struct_sigaltstack
+type SigaltstackT C.struct_sigaltstack
type Sigset C.struct___sigset
type StackT C.stack_t
diff --git a/src/pkg/runtime/defs_dragonfly_386.h b/src/pkg/runtime/defs_dragonfly_386.h
index 696dcd887d..f86b9c6b94 100644
--- a/src/pkg/runtime/defs_dragonfly_386.h
+++ b/src/pkg/runtime/defs_dragonfly_386.h
@@ -85,7 +85,7 @@ enum {
typedef struct Rtprio Rtprio;
typedef struct Lwpparams Lwpparams;
-typedef struct Sigaltstack Sigaltstack;
+typedef struct SigaltstackT SigaltstackT;
typedef struct Sigset Sigset;
typedef struct StackT StackT;
typedef struct Siginfo Siginfo;
@@ -94,7 +94,7 @@ typedef struct Ucontext Ucontext;
typedef struct Timespec Timespec;
typedef struct Timeval Timeval;
typedef struct Itimerval Itimerval;
-typedef struct Kevent Kevent;
+typedef struct KeventT KeventT;
#pragma pack on
@@ -109,7 +109,7 @@ struct Lwpparams {
int32 *tid1;
int32 *tid2;
};
-struct Sigaltstack {
+struct SigaltstackT {
int8 *ss_sp;
uint32 ss_size;
int32 ss_flags;
@@ -185,7 +185,7 @@ struct Itimerval {
Timeval it_value;
};
-struct Kevent {
+struct KeventT {
uint32 ident;
int16 filter;
uint16 flags;
diff --git a/src/pkg/runtime/defs_dragonfly_amd64.h b/src/pkg/runtime/defs_dragonfly_amd64.h
index 74581cc94c..671555241d 100644
--- a/src/pkg/runtime/defs_dragonfly_amd64.h
+++ b/src/pkg/runtime/defs_dragonfly_amd64.h
@@ -85,7 +85,7 @@ enum {
typedef struct Rtprio Rtprio;
typedef struct Lwpparams Lwpparams;
-typedef struct Sigaltstack Sigaltstack;
+typedef struct SigaltstackT SigaltstackT;
typedef struct Sigset Sigset;
typedef struct StackT StackT;
typedef struct Siginfo Siginfo;
@@ -94,7 +94,7 @@ typedef struct Ucontext Ucontext;
typedef struct Timespec Timespec;
typedef struct Timeval Timeval;
typedef struct Itimerval Itimerval;
-typedef struct Kevent Kevent;
+typedef struct KeventT KeventT;
#pragma pack on
@@ -109,7 +109,7 @@ struct Lwpparams {
int32 *tid1;
int32 *tid2;
};
-struct Sigaltstack {
+struct SigaltstackT {
int8 *ss_sp;
uint64 ss_size;
int32 ss_flags;
@@ -195,7 +195,7 @@ struct Itimerval {
Timeval it_value;
};
-struct Kevent {
+struct KeventT {
uint64 ident;
int16 filter;
uint16 flags;
diff --git a/src/pkg/runtime/defs_freebsd.go b/src/pkg/runtime/defs_freebsd.go
index 2832583e06..0253685aac 100644
--- a/src/pkg/runtime/defs_freebsd.go
+++ b/src/pkg/runtime/defs_freebsd.go
@@ -117,7 +117,7 @@ const (
type Rtprio C.struct_rtprio
type ThrParam C.struct_thr_param
-type Sigaltstack C.struct_sigaltstack
+type SigaltstackT C.struct_sigaltstack
type Sigset C.struct___sigset
type StackT C.stack_t
diff --git a/src/pkg/runtime/defs_freebsd_386.h b/src/pkg/runtime/defs_freebsd_386.h
index fab9385269..156dccba48 100644
--- a/src/pkg/runtime/defs_freebsd_386.h
+++ b/src/pkg/runtime/defs_freebsd_386.h
@@ -89,7 +89,7 @@ enum {
typedef struct Rtprio Rtprio;
typedef struct ThrParam ThrParam;
-typedef struct Sigaltstack Sigaltstack;
+typedef struct SigaltstackT SigaltstackT;
typedef struct Sigset Sigset;
typedef struct StackT StackT;
typedef struct Siginfo Siginfo;
@@ -98,7 +98,7 @@ typedef struct Ucontext Ucontext;
typedef struct Timespec Timespec;
typedef struct Timeval Timeval;
typedef struct Itimerval Itimerval;
-typedef struct Kevent Kevent;
+typedef struct KeventT KeventT;
#pragma pack on
@@ -119,7 +119,7 @@ struct ThrParam {
Rtprio *rtp;
void *spare[3];
};
-struct Sigaltstack {
+struct SigaltstackT {
int8 *ss_sp;
uint32 ss_size;
int32 ss_flags;
@@ -200,7 +200,7 @@ struct Itimerval {
Timeval it_value;
};
-struct Kevent {
+struct KeventT {
uint32 ident;
int16 filter;
uint16 flags;
diff --git a/src/pkg/runtime/defs_freebsd_amd64.h b/src/pkg/runtime/defs_freebsd_amd64.h
index c1db91803e..4ba8956a2c 100644
--- a/src/pkg/runtime/defs_freebsd_amd64.h
+++ b/src/pkg/runtime/defs_freebsd_amd64.h
@@ -89,7 +89,7 @@ enum {
typedef struct Rtprio Rtprio;
typedef struct ThrParam ThrParam;
-typedef struct Sigaltstack Sigaltstack;
+typedef struct SigaltstackT SigaltstackT;
typedef struct Sigset Sigset;
typedef struct StackT StackT;
typedef struct Siginfo Siginfo;
@@ -98,7 +98,7 @@ typedef struct Ucontext Ucontext;
typedef struct Timespec Timespec;
typedef struct Timeval Timeval;
typedef struct Itimerval Itimerval;
-typedef struct Kevent Kevent;
+typedef struct KeventT KeventT;
#pragma pack on
@@ -120,7 +120,7 @@ struct ThrParam {
Rtprio *rtp;
void *spare[3];
};
-struct Sigaltstack {
+struct SigaltstackT {
int8 *ss_sp;
uint64 ss_size;
int32 ss_flags;
@@ -211,7 +211,7 @@ struct Itimerval {
Timeval it_value;
};
-struct Kevent {
+struct KeventT {
uint64 ident;
int16 filter;
uint16 flags;
diff --git a/src/pkg/runtime/defs_freebsd_arm.h b/src/pkg/runtime/defs_freebsd_arm.h
index 4fc452e457..17deba68d1 100644
--- a/src/pkg/runtime/defs_freebsd_arm.h
+++ b/src/pkg/runtime/defs_freebsd_arm.h
@@ -89,7 +89,7 @@ enum {
typedef struct Rtprio Rtprio;
typedef struct ThrParam ThrParam;
-typedef struct Sigaltstack Sigaltstack;
+typedef struct SigaltstackT SigaltstackT;
typedef struct Sigset Sigset;
typedef struct StackT StackT;
typedef struct Siginfo Siginfo;
@@ -98,7 +98,7 @@ typedef struct Ucontext Ucontext;
typedef struct Timespec Timespec;
typedef struct Timeval Timeval;
typedef struct Itimerval Itimerval;
-typedef struct Kevent Kevent;
+typedef struct KeventT KeventT;
#pragma pack on
@@ -119,7 +119,7 @@ struct ThrParam {
Rtprio *rtp;
void *spare[3];
};
-struct Sigaltstack {
+struct SigaltstackT {
uint8 *ss_sp;
uint32 ss_size;
int32 ss_flags;
@@ -173,7 +173,7 @@ struct Itimerval {
Timeval it_value;
};
-struct Kevent {
+struct KeventT {
uint32 ident;
int16 filter;
uint16 flags;
diff --git a/src/pkg/runtime/defs_linux_386.h b/src/pkg/runtime/defs_linux_386.h
index 27dae9e82b..24a05d862a 100644
--- a/src/pkg/runtime/defs_linux_386.h
+++ b/src/pkg/runtime/defs_linux_386.h
@@ -95,9 +95,9 @@ typedef struct Xmmreg Xmmreg;
typedef struct Fpstate Fpstate;
typedef struct Timespec Timespec;
typedef struct Timeval Timeval;
-typedef struct Sigaction Sigaction;
+typedef struct SigactionT SigactionT;
typedef struct Siginfo Siginfo;
-typedef struct Sigaltstack Sigaltstack;
+typedef struct SigaltstackT SigaltstackT;
typedef struct Sigcontext Sigcontext;
typedef struct Ucontext Ucontext;
typedef struct Itimerval Itimerval;
@@ -144,7 +144,7 @@ struct Timeval {
int32 tv_sec;
int32 tv_usec;
};
-struct Sigaction {
+struct SigactionT {
void *k_sa_handler;
uint32 sa_flags;
void *sa_restorer;
@@ -156,7 +156,7 @@ struct Siginfo {
int32 si_code;
byte _sifields[116];
};
-struct Sigaltstack {
+struct SigaltstackT {
byte *ss_sp;
int32 ss_flags;
uint32 ss_size;
@@ -194,7 +194,7 @@ struct Sigcontext {
struct Ucontext {
uint32 uc_flags;
Ucontext *uc_link;
- Sigaltstack uc_stack;
+ SigaltstackT uc_stack;
Sigcontext uc_mcontext;
uint32 uc_sigmask;
};
@@ -204,7 +204,7 @@ struct Itimerval {
};
struct EpollEvent {
uint32 events;
- uint64 data;
+ byte data[8]; // to match amd64
};
diff --git a/src/pkg/runtime/defs_linux_amd64.h b/src/pkg/runtime/defs_linux_amd64.h
index 3e87df68a1..14616dffed 100644
--- a/src/pkg/runtime/defs_linux_amd64.h
+++ b/src/pkg/runtime/defs_linux_amd64.h
@@ -88,7 +88,7 @@ enum {
typedef struct Timespec Timespec;
typedef struct Timeval Timeval;
-typedef struct Sigaction Sigaction;
+typedef struct SigactionT SigactionT;
typedef struct Siginfo Siginfo;
typedef struct Itimerval Itimerval;
typedef struct EpollEvent EpollEvent;
@@ -103,7 +103,7 @@ struct Timeval {
int64 tv_sec;
int64 tv_usec;
};
-struct Sigaction {
+struct SigactionT {
void *sa_handler;
uint64 sa_flags;
void *sa_restorer;
@@ -122,7 +122,7 @@ struct Itimerval {
};
struct EpollEvent {
uint32 events;
- uint64 data;
+ byte data[8]; // unaligned uintptr
};
@@ -144,7 +144,7 @@ typedef struct Fpxreg1 Fpxreg1;
typedef struct Xmmreg1 Xmmreg1;
typedef struct Fpstate1 Fpstate1;
typedef struct Fpreg1 Fpreg1;
-typedef struct Sigaltstack Sigaltstack;
+typedef struct SigaltstackT SigaltstackT;
typedef struct Mcontext Mcontext;
typedef struct Ucontext Ucontext;
typedef struct Sigcontext Sigcontext;
@@ -200,7 +200,7 @@ struct Fpreg1 {
uint16 significand[4];
uint16 exponent;
};
-struct Sigaltstack {
+struct SigaltstackT {
byte *ss_sp;
int32 ss_flags;
byte Pad_cgo_0[4];
@@ -214,7 +214,7 @@ struct Mcontext {
struct Ucontext {
uint64 uc_flags;
Ucontext *uc_link;
- Sigaltstack uc_stack;
+ SigaltstackT uc_stack;
Mcontext uc_mcontext;
Usigset uc_sigmask;
Fpstate __fpregs_mem;
diff --git a/src/pkg/runtime/defs_linux_arm.h b/src/pkg/runtime/defs_linux_arm.h
index 05a17af64b..50b3c919ed 100644
--- a/src/pkg/runtime/defs_linux_arm.h
+++ b/src/pkg/runtime/defs_linux_arm.h
@@ -88,8 +88,8 @@ struct Timespec {
int32 tv_nsec;
};
-typedef struct Sigaltstack Sigaltstack;
-struct Sigaltstack {
+typedef struct SigaltstackT SigaltstackT;
+struct SigaltstackT {
void *ss_sp;
int32 ss_flags;
uint32 ss_size;
@@ -124,7 +124,7 @@ typedef struct Ucontext Ucontext;
struct Ucontext {
uint32 uc_flags;
Ucontext *uc_link;
- Sigaltstack uc_stack;
+ SigaltstackT uc_stack;
Sigcontext uc_mcontext;
uint32 uc_sigmask;
int32 __unused[31];
@@ -151,8 +151,8 @@ struct Siginfo {
uint8 _sifields[4];
};
-typedef struct Sigaction Sigaction;
-struct Sigaction {
+typedef struct SigactionT SigactionT;
+struct SigactionT {
void *sa_handler;
uint32 sa_flags;
void *sa_restorer;
@@ -163,6 +163,6 @@ typedef struct EpollEvent EpollEvent;
struct EpollEvent {
uint32 events;
uint32 _pad;
- uint64 data;
+ byte data[8]; // to match amd64
};
#pragma pack off
diff --git a/src/pkg/runtime/defs_linux_power64.h b/src/pkg/runtime/defs_linux_power64.h
index 41db45ca14..64f145672c 100644
--- a/src/pkg/runtime/defs_linux_power64.h
+++ b/src/pkg/runtime/defs_linux_power64.h
@@ -147,7 +147,7 @@ enum {
//typedef struct Usigset Usigset;
typedef struct Ptregs Ptregs;
typedef struct Vreg Vreg;
-typedef struct Sigaltstack Sigaltstack;
+typedef struct SigaltstackT SigaltstackT;
typedef struct Sigcontext Sigcontext;
typedef struct Ucontext Ucontext;
@@ -179,7 +179,7 @@ struct Vreg {
uint32 u[4];
};
-struct Sigaltstack {
+struct SigaltstackT {
byte *ss_sp;
int32 ss_flags;
byte Pad_cgo_0[4];
@@ -201,7 +201,7 @@ struct Sigcontext {
struct Ucontext {
uint64 uc_flags;
Ucontext *uc_link;
- Sigaltstack uc_stack;
+ SigaltstackT uc_stack;
Usigset uc_sigmask;
Usigset __unused[15];
Sigcontext uc_mcontext;
diff --git a/src/pkg/runtime/defs_nacl_amd64p32.h b/src/pkg/runtime/defs_nacl_amd64p32.h
index 8d3068bf87..45663d40af 100644
--- a/src/pkg/runtime/defs_nacl_amd64p32.h
+++ b/src/pkg/runtime/defs_nacl_amd64p32.h
@@ -79,7 +79,7 @@ struct ExcContext
union {
ExcRegs386 regs;
ExcRegsAmd64 regs64;
- };
+ } regs;
};
struct ExcPortableContext
diff --git a/src/pkg/runtime/defs_netbsd.go b/src/pkg/runtime/defs_netbsd.go
index d1c4cbe96c..b27949e423 100644
--- a/src/pkg/runtime/defs_netbsd.go
+++ b/src/pkg/runtime/defs_netbsd.go
@@ -109,7 +109,7 @@ const (
EVFILT_WRITE = C.EVFILT_WRITE
)
-type Sigaltstack C.struct_sigaltstack
+type SigaltstackT C.struct_sigaltstack
type Sigset C.sigset_t
type Siginfo C.struct__ksiginfo
diff --git a/src/pkg/runtime/defs_netbsd_386.h b/src/pkg/runtime/defs_netbsd_386.h
index 7fd66959f3..fd87804f90 100644
--- a/src/pkg/runtime/defs_netbsd_386.h
+++ b/src/pkg/runtime/defs_netbsd_386.h
@@ -82,7 +82,7 @@ enum {
EVFILT_WRITE = 0x1,
};
-typedef struct Sigaltstack Sigaltstack;
+typedef struct SigaltstackT SigaltstackT;
typedef struct Sigset Sigset;
typedef struct Siginfo Siginfo;
typedef struct StackT StackT;
@@ -91,11 +91,11 @@ typedef struct Timeval Timeval;
typedef struct Itimerval Itimerval;
typedef struct McontextT McontextT;
typedef struct UcontextT UcontextT;
-typedef struct Kevent Kevent;
+typedef struct KeventT KeventT;
#pragma pack on
-struct Sigaltstack {
+struct SigaltstackT {
byte *ss_sp;
uint32 ss_size;
int32 ss_flags;
@@ -143,13 +143,13 @@ struct UcontextT {
int32 __uc_pad[4];
};
-struct Kevent {
+struct KeventT {
uint32 ident;
uint32 filter;
uint32 flags;
uint32 fflags;
int64 data;
- int32 udata;
+ byte *udata;
};
diff --git a/src/pkg/runtime/defs_netbsd_amd64.h b/src/pkg/runtime/defs_netbsd_amd64.h
index 972af165b7..dac94b113b 100644
--- a/src/pkg/runtime/defs_netbsd_amd64.h
+++ b/src/pkg/runtime/defs_netbsd_amd64.h
@@ -82,7 +82,7 @@ enum {
EVFILT_WRITE = 0x1,
};
-typedef struct Sigaltstack Sigaltstack;
+typedef struct SigaltstackT SigaltstackT;
typedef struct Sigset Sigset;
typedef struct Siginfo Siginfo;
typedef struct StackT StackT;
@@ -91,11 +91,11 @@ typedef struct Timeval Timeval;
typedef struct Itimerval Itimerval;
typedef struct McontextT McontextT;
typedef struct UcontextT UcontextT;
-typedef struct Kevent Kevent;
+typedef struct KeventT KeventT;
#pragma pack on
-struct Sigaltstack {
+struct SigaltstackT {
byte *ss_sp;
uint64 ss_size;
int32 ss_flags;
@@ -147,14 +147,14 @@ struct UcontextT {
McontextT uc_mcontext;
};
-struct Kevent {
+struct KeventT {
uint64 ident;
uint32 filter;
uint32 flags;
uint32 fflags;
byte Pad_cgo_0[4];
int64 data;
- int64 udata;
+ byte *udata;
};
diff --git a/src/pkg/runtime/defs_netbsd_arm.h b/src/pkg/runtime/defs_netbsd_arm.h
index c6f5b1c47c..70f34af47b 100644
--- a/src/pkg/runtime/defs_netbsd_arm.h
+++ b/src/pkg/runtime/defs_netbsd_arm.h
@@ -82,7 +82,7 @@ enum {
EVFILT_WRITE = 0x1,
};
-typedef struct Sigaltstack Sigaltstack;
+typedef struct SigaltstackT SigaltstackT;
typedef struct Sigset Sigset;
typedef struct Siginfo Siginfo;
typedef struct StackT StackT;
@@ -91,11 +91,11 @@ typedef struct Timeval Timeval;
typedef struct Itimerval Itimerval;
typedef struct McontextT McontextT;
typedef struct UcontextT UcontextT;
-typedef struct Kevent Kevent;
+typedef struct KeventT KeventT;
#pragma pack on
-struct Sigaltstack {
+struct SigaltstackT {
byte *ss_sp;
uint32 ss_size;
int32 ss_flags;
@@ -147,13 +147,13 @@ struct UcontextT {
int32 __uc_pad[2];
};
-struct Kevent {
+struct KeventT {
uint32 ident;
uint32 filter;
uint32 flags;
uint32 fflags;
int64 data;
- int32 udata;
+ byte *udata;
};
diff --git a/src/pkg/runtime/defs_openbsd.go b/src/pkg/runtime/defs_openbsd.go
index 4a705796f0..39224c988c 100644
--- a/src/pkg/runtime/defs_openbsd.go
+++ b/src/pkg/runtime/defs_openbsd.go
@@ -104,9 +104,9 @@ const (
EVFILT_WRITE = C.EVFILT_WRITE
)
-type Tfork C.struct___tfork
+type TforkT C.struct___tfork
-type Sigaltstack C.struct_sigaltstack
+type SigaltstackT C.struct_sigaltstack
type Sigcontext C.struct_sigcontext
type Siginfo C.siginfo_t
type Sigset C.sigset_t
@@ -118,4 +118,4 @@ type Timespec C.struct_timespec
type Timeval C.struct_timeval
type Itimerval C.struct_itimerval
-type Kevent C.struct_kevent
+type KeventT C.struct_kevent
diff --git a/src/pkg/runtime/defs_openbsd_386.h b/src/pkg/runtime/defs_openbsd_386.h
index b8f993e2b8..6b77e0084a 100644
--- a/src/pkg/runtime/defs_openbsd_386.h
+++ b/src/pkg/runtime/defs_openbsd_386.h
@@ -81,25 +81,25 @@ enum {
EVFILT_WRITE = -0x2,
};
-typedef struct Tfork Tfork;
-typedef struct Sigaltstack Sigaltstack;
+typedef struct TforkT TforkT;
+typedef struct SigaltstackT SigaltstackT;
typedef struct Sigcontext Sigcontext;
typedef struct Siginfo Siginfo;
typedef struct StackT StackT;
typedef struct Timespec Timespec;
typedef struct Timeval Timeval;
typedef struct Itimerval Itimerval;
-typedef struct Kevent Kevent;
+typedef struct KeventT KeventT;
#pragma pack on
-struct Tfork {
+struct TforkT {
byte *tf_tcb;
int32 *tf_tid;
byte *tf_stack;
};
-struct Sigaltstack {
+struct SigaltstackT {
byte *ss_sp;
uint32 ss_size;
int32 ss_flags;
@@ -155,7 +155,7 @@ struct Itimerval {
Timeval it_value;
};
-struct Kevent {
+struct KeventT {
uint32 ident;
int16 filter;
uint16 flags;
diff --git a/src/pkg/runtime/defs_openbsd_amd64.h b/src/pkg/runtime/defs_openbsd_amd64.h
index a1ae2ef65c..761e8e47df 100644
--- a/src/pkg/runtime/defs_openbsd_amd64.h
+++ b/src/pkg/runtime/defs_openbsd_amd64.h
@@ -81,25 +81,25 @@ enum {
EVFILT_WRITE = -0x2,
};
-typedef struct Tfork Tfork;
-typedef struct Sigaltstack Sigaltstack;
+typedef struct TforkT TforkT;
+typedef struct SigaltstackT SigaltstackT;
typedef struct Sigcontext Sigcontext;
typedef struct Siginfo Siginfo;
typedef struct StackT StackT;
typedef struct Timespec Timespec;
typedef struct Timeval Timeval;
typedef struct Itimerval Itimerval;
-typedef struct Kevent Kevent;
+typedef struct KeventT KeventT;
#pragma pack on
-struct Tfork {
+struct TforkT {
byte *tf_tcb;
int32 *tf_tid;
byte *tf_stack;
};
-struct Sigaltstack {
+struct SigaltstackT {
byte *ss_sp;
uint64 ss_size;
int32 ss_flags;
@@ -166,7 +166,7 @@ struct Itimerval {
Timeval it_value;
};
-struct Kevent {
+struct KeventT {
uint64 ident;
int16 filter;
uint16 flags;
diff --git a/src/pkg/runtime/defs_plan9_386.h b/src/pkg/runtime/defs_plan9_386.h
index bde299dee1..a762b85899 100644
--- a/src/pkg/runtime/defs_plan9_386.h
+++ b/src/pkg/runtime/defs_plan9_386.h
@@ -21,9 +21,6 @@ struct Ureg
uint32 pc; /* pc */
uint32 cs; /* old context */
uint32 flags; /* old flags */
- union {
- uint32 usp;
- uint32 sp;
- };
+ uint32 sp;
uint32 ss; /* old stack segment */
};
diff --git a/src/pkg/runtime/defs_solaris.go b/src/pkg/runtime/defs_solaris.go
index 8dcbb08b7d..ba44e5fd4d 100644
--- a/src/pkg/runtime/defs_solaris.go
+++ b/src/pkg/runtime/defs_solaris.go
@@ -133,7 +133,7 @@ const (
type SemT C.sem_t
-type Sigaltstack C.struct_sigaltstack
+type SigaltstackT C.struct_sigaltstack
type Sigset C.sigset_t
type StackT C.stack_t
diff --git a/src/pkg/runtime/defs_solaris_amd64.h b/src/pkg/runtime/defs_solaris_amd64.h
index 799724fadc..cb1cfeadcc 100644
--- a/src/pkg/runtime/defs_solaris_amd64.h
+++ b/src/pkg/runtime/defs_solaris_amd64.h
@@ -101,11 +101,11 @@ enum {
};
typedef struct SemT SemT;
-typedef struct Sigaltstack Sigaltstack;
+typedef struct SigaltstackT SigaltstackT;
typedef struct Sigset Sigset;
typedef struct StackT StackT;
typedef struct Siginfo Siginfo;
-typedef struct Sigaction Sigaction;
+typedef struct SigactionT SigactionT;
typedef struct Fpregset Fpregset;
typedef struct Mcontext Mcontext;
typedef struct Ucontext Ucontext;
@@ -126,7 +126,7 @@ struct SemT {
uint64 sem_pad2[2];
};
-struct Sigaltstack {
+struct SigaltstackT {
byte *ss_sp;
uint64 ss_size;
int32 ss_flags;
@@ -149,7 +149,7 @@ struct Siginfo {
int32 si_pad;
byte __data[240];
};
-struct Sigaction {
+struct SigactionT {
int32 sa_flags;
byte Pad_cgo_0[4];
byte _funcptr[8];
diff --git a/src/pkg/runtime/env_plan9.c b/src/pkg/runtime/env_plan9.c
deleted file mode 100644
index f732c9f294..0000000000
--- a/src/pkg/runtime/env_plan9.c
+++ /dev/null
@@ -1,42 +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.
-
-#include "runtime.h"
-#include "os_GOOS.h"
-
-byte*
-runtime·getenv(int8 *s)
-{
- int32 fd, n, r;
- intgo len;
- byte file[128];
- byte *p;
- static byte b[128];
-
- len = runtime·findnull((byte*)s);
- if(len > sizeof file-6)
- return nil;
-
- runtime·memclr(file, sizeof file);
- runtime·memmove((void*)file, (void*)"/env/", 5);
- runtime·memmove((void*)(file+5), (void*)s, len);
-
- fd = runtime·open((int8*)file, OREAD, 0);
- if(fd < 0)
- return nil;
- n = runtime·seek(fd, 0, 2);
- if(runtime·strcmp((byte*)s, (byte*)"GOTRACEBACK") == 0){
- // should not call malloc
- if(n >= sizeof b)
- return nil;
- runtime·memclr(b, sizeof b);
- p = b;
- }else
- p = runtime·malloc(n+1);
- r = runtime·pread(fd, p, n, 0);
- runtime·close(fd);
- if(r < 0)
- return nil;
- return p;
-}
diff --git a/src/pkg/runtime/env_plan9.go b/src/pkg/runtime/env_plan9.go
new file mode 100644
index 0000000000..76e9867e03
--- /dev/null
+++ b/src/pkg/runtime/env_plan9.go
@@ -0,0 +1,52 @@
+// 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 runtime
+
+import "unsafe"
+
+func getenv(s *byte) *byte {
+ val := gogetenv(gostringnocopy(s))
+ if val == "" {
+ return nil
+ }
+ // Strings found in environment are NUL-terminated.
+ return &bytes(val)[0]
+}
+
+var tracebackbuf [128]byte
+
+func gogetenv(key string) string {
+ var file [128]byte
+ if len(key) > len(file)-6 {
+ return ""
+ }
+
+ copy(file[:], "/env/")
+ copy(file[5:], key)
+
+ fd := open(&file[0], _OREAD, 0)
+ if fd < 0 {
+ return ""
+ }
+ n := seek(fd, 0, 2) - 1
+ if n <= 0 {
+ close(fd)
+ return ""
+ }
+
+ p := make([]byte, n)
+
+ r := pread(fd, unsafe.Pointer(&p[0]), int32(n), 0)
+ close(fd)
+ if r < 0 {
+ return ""
+ }
+
+ var s string
+ sp := (*_string)(unsafe.Pointer(&s))
+ sp.str = &p[0]
+ sp.len = int(r)
+ return s
+}
diff --git a/src/pkg/runtime/env_posix.c b/src/pkg/runtime/env_posix.c
deleted file mode 100644
index 9b3583ce8b..0000000000
--- a/src/pkg/runtime/env_posix.c
+++ /dev/null
@@ -1,62 +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 darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
-
-#include "runtime.h"
-#include "arch_GOARCH.h"
-#include "malloc.h"
-
-Slice syscall·envs;
-
-byte*
-runtime·getenv(int8 *s)
-{
- int32 i, j;
- intgo len;
- byte *v, *bs;
- String* envv;
- int32 envc;
-
- bs = (byte*)s;
- len = runtime·findnull(bs);
- envv = (String*)syscall·envs.array;
- envc = syscall·envs.len;
- for(i=0; i<envc; i++){
- if(envv[i].len <= len)
- continue;
- v = envv[i].str;
- for(j=0; j<len; j++)
- if(bs[j] != v[j])
- goto nomatch;
- if(v[len] != '=')
- goto nomatch;
- return v+len+1;
- nomatch:;
- }
- return nil;
-}
-
-void (*_cgo_setenv)(byte**);
-
-// Update the C environment if cgo is loaded.
-// Called from syscall.Setenv.
-void
-syscall·setenv_c(String k, String v)
-{
- byte *arg[2];
-
- if(_cgo_setenv == nil)
- return;
-
- arg[0] = runtime·malloc(k.len + 1);
- runtime·memmove(arg[0], k.str, k.len);
- arg[0][k.len] = 0;
-
- arg[1] = runtime·malloc(v.len + 1);
- runtime·memmove(arg[1], v.str, v.len);
- arg[1][v.len] = 0;
-
- runtime·asmcgocall((void*)_cgo_setenv, arg);
-}
diff --git a/src/pkg/runtime/env_posix.go b/src/pkg/runtime/env_posix.go
new file mode 100644
index 0000000000..6c04f6cc70
--- /dev/null
+++ b/src/pkg/runtime/env_posix.go
@@ -0,0 +1,52 @@
+// 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 darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
+
+package runtime
+
+import "unsafe"
+
+func environ() []string
+
+func getenv(s *byte) *byte {
+ val := gogetenv(gostringnocopy(s))
+ if val == "" {
+ return nil
+ }
+ // Strings found in environment are NUL-terminated.
+ return &bytes(val)[0]
+}
+
+func gogetenv(key string) string {
+ env := environ()
+ if env == nil {
+ gothrow("getenv before env init")
+ }
+ for _, s := range environ() {
+ if len(s) > len(key) && s[len(key)] == '=' && s[:len(key)] == key {
+ return s[len(key)+1:]
+ }
+ }
+ return ""
+}
+
+var _cgo_setenv uintptr // pointer to C function
+
+// Update the C environment if cgo is loaded.
+// Called from syscall.Setenv.
+func syscall_setenv_c(k string, v string) {
+ if _cgo_setenv == 0 {
+ return
+ }
+ arg := [2]unsafe.Pointer{cstring(k), cstring(v)}
+ asmcgocall(unsafe.Pointer(_cgo_setenv), unsafe.Pointer(&arg))
+}
+
+func cstring(s string) unsafe.Pointer {
+ p := make([]byte, len(s)+1)
+ sp := (*_string)(unsafe.Pointer(&s))
+ memmove(unsafe.Pointer(&p[0]), unsafe.Pointer(sp.str), uintptr(len(s)))
+ return unsafe.Pointer(&p[0])
+}
diff --git a/src/pkg/runtime/error.go b/src/pkg/runtime/error.go
index 12fd09eaf9..3ea93680ce 100644
--- a/src/pkg/runtime/error.go
+++ b/src/pkg/runtime/error.go
@@ -4,6 +4,8 @@
package runtime
+import "unsafe"
+
// The Error interface identifies a run time error.
type Error interface {
error
@@ -75,17 +77,19 @@ func newErrorString(s string, ret *interface{}) {
}
// An errorCString represents a runtime error described by a single C string.
-// Not "type errorCString uintptr" because of http://golang.org/issue/7084.
-type errorCString struct{ cstr uintptr }
+// Not "type errorCString unsafe.Pointer" because of http://golang.org/issue/7084.
+// Not uintptr because we want to avoid an allocation if interfaces can't hold
+// uintptrs directly (and cstr _is_ a pointer).
+type errorCString struct{ cstr unsafe.Pointer }
func (e errorCString) RuntimeError() {}
func (e errorCString) Error() string {
- return "runtime error: " + cstringToGo(e.cstr)
+ return "runtime error: " + gostringnocopy((*byte)(e.cstr))
}
// For calling from C.
-func newErrorCString(s uintptr, ret *interface{}) {
+func newErrorCString(s unsafe.Pointer, ret *interface{}) {
*ret = errorCString{s}
}
@@ -93,7 +97,10 @@ type stringer interface {
String() string
}
-func typestring(interface{}) string
+func typestring(x interface{}) string {
+ e := (*eface)(unsafe.Pointer(&x))
+ return *e._type._string
+}
// For calling from C.
// Prints an argument passed to panic.
diff --git a/src/pkg/runtime/export_futex_test.go b/src/pkg/runtime/export_futex_test.go
index 1477828a7d..96281f6509 100644
--- a/src/pkg/runtime/export_futex_test.go
+++ b/src/pkg/runtime/export_futex_test.go
@@ -6,8 +6,5 @@
package runtime
-func futexsleep(addr *uint32, val uint32, ns int64)
-func futexwakeup(addr *uint32, val uint32)
-
var Futexsleep = futexsleep
var Futexwakeup = futexwakeup
diff --git a/src/pkg/runtime/export_test.go b/src/pkg/runtime/export_test.go
index 01b47e17af..07ef26f25a 100644
--- a/src/pkg/runtime/export_test.go
+++ b/src/pkg/runtime/export_test.go
@@ -6,6 +6,8 @@
package runtime
+import "unsafe"
+
var Fadd64 = fadd64
var Fsub64 = fsub64
var Fmul64 = fmul64
@@ -16,14 +18,12 @@ var Fcmp64 = fcmp64
var Fintto64 = fintto64
var F64toint = f64toint
-func entersyscall()
-func exitsyscall()
-func golockedOSThread() bool
+// in asm_*.s
func stackguard() (sp, limit uintptr)
var Entersyscall = entersyscall
var Exitsyscall = exitsyscall
-var LockedOSThread = golockedOSThread
+var LockedOSThread = lockedOSThread
var Stackguard = stackguard
type LFNode struct {
@@ -31,11 +31,26 @@ type LFNode struct {
Pushcnt uintptr
}
-func lfstackpush_go(head *uint64, node *LFNode)
-func lfstackpop_go(head *uint64) *LFNode
+func lfstackpush_m()
+func lfstackpop_m()
+
+func LFStackPush(head *uint64, node *LFNode) {
+ mp := acquirem()
+ mp.ptrarg[0] = unsafe.Pointer(head)
+ mp.ptrarg[1] = unsafe.Pointer(node)
+ onM(lfstackpush_m)
+ releasem(mp)
+}
-var LFStackPush = lfstackpush_go
-var LFStackPop = lfstackpop_go
+func LFStackPop(head *uint64) *LFNode {
+ mp := acquirem()
+ mp.ptrarg[0] = unsafe.Pointer(head)
+ onM(lfstackpop_m)
+ node := (*LFNode)(unsafe.Pointer(mp.ptrarg[0]))
+ mp.ptrarg[0] = nil
+ releasem(mp)
+ return node
+}
type ParFor struct {
body *byte
@@ -48,45 +63,101 @@ type ParFor struct {
wait bool
}
-func newParFor(nthrmax uint32) *ParFor
-func parForSetup(desc *ParFor, nthr, n uint32, ctx *byte, wait bool, body func(*ParFor, uint32))
-func parForDo(desc *ParFor)
-func parForIters(desc *ParFor, tid uintptr) (uintptr, uintptr)
+func newparfor_m()
+func parforsetup_m()
+func parfordo_m()
+func parforiters_m()
+
+func NewParFor(nthrmax uint32) *ParFor {
+ mp := acquirem()
+ mp.scalararg[0] = uintptr(nthrmax)
+ onM(newparfor_m)
+ desc := (*ParFor)(mp.ptrarg[0])
+ mp.ptrarg[0] = nil
+ releasem(mp)
+ return desc
+}
-var NewParFor = newParFor
-var ParForSetup = parForSetup
-var ParForDo = parForDo
+func ParForSetup(desc *ParFor, nthr, n uint32, ctx *byte, wait bool, body func(*ParFor, uint32)) {
+ mp := acquirem()
+ mp.ptrarg[0] = unsafe.Pointer(desc)
+ mp.ptrarg[1] = unsafe.Pointer(ctx)
+ mp.ptrarg[2] = unsafe.Pointer(funcPC(body)) // TODO(rsc): Should be a scalar.
+ mp.scalararg[0] = uintptr(nthr)
+ mp.scalararg[1] = uintptr(n)
+ mp.scalararg[2] = 0
+ if wait {
+ mp.scalararg[2] = 1
+ }
+ onM(parforsetup_m)
+ releasem(mp)
+}
+
+func ParForDo(desc *ParFor) {
+ mp := acquirem()
+ mp.ptrarg[0] = unsafe.Pointer(desc)
+ onM(parfordo_m)
+ releasem(mp)
+}
func ParForIters(desc *ParFor, tid uint32) (uint32, uint32) {
- begin, end := parForIters(desc, uintptr(tid))
- return uint32(begin), uint32(end)
+ mp := acquirem()
+ mp.ptrarg[0] = unsafe.Pointer(desc)
+ mp.scalararg[0] = uintptr(tid)
+ onM(parforiters_m)
+ begin := uint32(mp.scalararg[0])
+ end := uint32(mp.scalararg[1])
+ releasem(mp)
+ return begin, end
}
+// in mgc0.c
//go:noescape
-func GCMask(x interface{}) []byte
+func getgcmask(data unsafe.Pointer, typ *_type, array **byte, len *uint)
+
+func GCMask(x interface{}) (ret []byte) {
+ e := (*eface)(unsafe.Pointer(&x))
+ s := (*slice)(unsafe.Pointer(&ret))
+ onM(func() {
+ getgcmask(e.data, e._type, &s.array, &s.len)
+ s.cap = s.len
+ })
+ return
+}
func testSchedLocalQueue()
func testSchedLocalQueueSteal()
-
-var TestSchedLocalQueue1 = testSchedLocalQueue
-var TestSchedLocalQueueSteal1 = testSchedLocalQueueSteal
+func RunSchedLocalQueueTest() {
+ onM(testSchedLocalQueue)
+}
+func RunSchedLocalQueueStealTest() {
+ onM(testSchedLocalQueueSteal)
+}
var HaveGoodHash = haveGoodHash
var StringHash = stringHash
var BytesHash = bytesHash
var Int32Hash = int32Hash
var Int64Hash = int64Hash
-
-var HashLoad = &hashLoad
-
-func memclrBytes(b []byte)
-
+var EfaceHash = efaceHash
+var IfaceHash = ifaceHash
var MemclrBytes = memclrBytes
-func gogoBytes() int32
-
-var GogoBytes = gogoBytes
+var HashLoad = &hashLoad
-func gostringW([]uint16) string
+// For testing.
+func GogoBytes() int32 {
+ return _RuntimeGogoBytes
+}
-var GostringW = gostringW
+// in string.c
+//go:noescape
+func gostringw(w *uint16) string
+
+// entry point for testing
+func GostringW(w []uint16) (s string) {
+ onM(func() {
+ s = gostringw(&w[0])
+ })
+ return
+}
diff --git a/src/pkg/runtime/extern.go b/src/pkg/runtime/extern.go
index 57f09aaf7d..3d06a23fce 100644
--- a/src/pkg/runtime/extern.go
+++ b/src/pkg/runtime/extern.go
@@ -46,6 +46,8 @@ a comma-separated list of name=val pairs. Supported names are:
schedtrace: setting schedtrace=X causes the scheduler to emit a single line to standard
error every X milliseconds, summarizing the scheduler state.
+ scavenge: scavenge=1 enables debugging mode of heap scavenger.
+
The GOMAXPROCS variable limits the number of operating system threads that
can execute user-level Go code simultaneously. There is no limit to the number of threads
that can be blocked in system calls on behalf of Go code; those do not count against
@@ -73,72 +75,64 @@ of the run-time system.
*/
package runtime
-// Gosched yields the processor, allowing other goroutines to run. It does not
-// suspend the current goroutine, so execution resumes automatically.
-func Gosched()
-
-// Goexit terminates the goroutine that calls it. No other goroutine is affected.
-// Goexit runs all deferred calls before terminating the goroutine.
-//
-// Calling Goexit from the main goroutine terminates that goroutine
-// without func main returning. Since func main has not returned,
-// the program continues execution of other goroutines.
-// If all other goroutines exit, the program crashes.
-func Goexit()
-
// Caller reports file and line number information about function invocations on
// the calling goroutine's stack. The argument skip is the number of stack frames
// to ascend, with 0 identifying the caller of Caller. (For historical reasons the
// meaning of skip differs between Caller and Callers.) The return values report the
// program counter, file name, and line number within the file of the corresponding
// call. The boolean ok is false if it was not possible to recover the information.
-func Caller(skip int) (pc uintptr, file string, line int, ok bool)
+func Caller(skip int) (pc uintptr, file string, line int, ok bool) {
+ // Ask for two PCs: the one we were asked for
+ // and what it called, so that we can see if it
+ // "called" sigpanic.
+ var rpc [2]uintptr
+ if callers(1+skip-1, &rpc[0], 2) < 2 {
+ return
+ }
+ f := findfunc(rpc[1])
+ if f == nil {
+ // TODO(rsc): Probably a bug?
+ // The C version said "have retpc at least"
+ // but actually returned pc=0.
+ ok = true
+ return
+ }
+ pc = rpc[1]
+ xpc := pc
+ g := findfunc(rpc[0])
+ // All architectures turn faults into apparent calls to sigpanic.
+ // If we see a call to sigpanic, we do not back up the PC to find
+ // the line number of the call instruction, because there is no call.
+ if xpc > f.entry && (g == nil || g.entry != funcPC(sigpanic)) {
+ xpc--
+ }
+ line = int(funcline(f, xpc, &file))
+ ok = true
+ return
+}
// Callers fills the slice pc with the program counters of function invocations
// on the calling goroutine's stack. The argument skip is the number of stack frames
// to skip before recording in pc, with 0 identifying the frame for Callers itself and
// 1 identifying the caller of Callers.
// It returns the number of entries written to pc.
-func Callers(skip int, pc []uintptr) int
-
-type Func struct {
- opaque struct{} // unexported field to disallow conversions
-}
-
-// FuncForPC returns a *Func describing the function that contains the
-// given program counter address, or else nil.
-func FuncForPC(pc uintptr) *Func
-
-// Name returns the name of the function.
-func (f *Func) Name() string {
- return funcname_go(f)
-}
-
-// Entry returns the entry address of the function.
-func (f *Func) Entry() uintptr {
- return funcentry_go(f)
-}
-
-// FileLine returns the file name and line number of the
-// source code corresponding to the program counter pc.
-// The result will not be accurate if pc is not a program
-// counter within f.
-func (f *Func) FileLine(pc uintptr) (file string, line int) {
- return funcline_go(f, pc)
+func Callers(skip int, pc []uintptr) int {
+ // runtime.callers uses pc.array==nil as a signal
+ // to print a stack trace. Pick off 0-length pc here
+ // so that we don't let a nil pc slice get to it.
+ if len(pc) == 0 {
+ return 0
+ }
+ return callers(skip, &pc[0], len(pc))
}
-// implemented in symtab.c
-func funcline_go(*Func, uintptr) (string, int)
-func funcname_go(*Func) string
-func funcentry_go(*Func) uintptr
-
func getgoroot() string
// GOROOT returns the root of the Go tree.
// It uses the GOROOT environment variable, if set,
// or else the root used during the Go build.
func GOROOT() string {
- s := getgoroot()
+ s := gogetenv("GOROOT")
if s != "" {
return s
}
diff --git a/src/pkg/runtime/funcdata.h b/src/pkg/runtime/funcdata.h
index 1f624b529b..dc9c41363e 100644
--- a/src/pkg/runtime/funcdata.h
+++ b/src/pkg/runtime/funcdata.h
@@ -6,13 +6,15 @@
// in Go binaries. It is included by both C and assembly, so it must
// be written using #defines. It is included by the runtime package
// as well as the compilers.
+//
+// symtab.go also contains a copy of these constants.
#define PCDATA_ArgSize 0 /* argument size at CALL instruction */
#define PCDATA_StackMapIndex 1
-#define FUNCDATA_ArgsPointerMaps 2 /* garbage collector blocks */
-#define FUNCDATA_LocalsPointerMaps 3
-#define FUNCDATA_DeadValueMaps 4
+#define FUNCDATA_ArgsPointerMaps 0 /* garbage collector blocks */
+#define FUNCDATA_LocalsPointerMaps 1
+#define FUNCDATA_DeadValueMaps 2
// To be used in assembly.
#define ARGSIZE(n) PCDATA $PCDATA_ArgSize, $n
@@ -27,9 +29,9 @@
enum {
PCDATA_ArgSize = 0,
PCDATA_StackMapIndex = 1,
- FUNCDATA_ArgsPointerMaps = 2,
- FUNCDATA_LocalsPointerMaps = 3,
- FUNCDATA_DeadValueMaps = 4,
+ FUNCDATA_ArgsPointerMaps = 0,
+ FUNCDATA_LocalsPointerMaps = 1,
+ FUNCDATA_DeadValueMaps = 2,
ArgsSizeUnknown = 0x80000000,
};
*/
diff --git a/src/pkg/runtime/gc_test.go b/src/pkg/runtime/gc_test.go
index 073d9fa758..6abec4cca7 100644
--- a/src/pkg/runtime/gc_test.go
+++ b/src/pkg/runtime/gc_test.go
@@ -164,6 +164,14 @@ func TestGcLastTime(t *testing.T) {
if t0 > last || last > t1 {
t.Fatalf("bad last GC time: got %v, want [%v, %v]", last, t0, t1)
}
+ pause := ms.PauseNs[(ms.NumGC+255)%256]
+ // Due to timer granularity, pause can actually be 0 on windows
+ // or on virtualized environments.
+ if pause == 0 {
+ t.Logf("last GC pause was 0")
+ } else if pause > 10e9 {
+ t.Logf("bad last GC pause: got %v, want [0, 10e9]", pause)
+ }
}
var hugeSink interface{}
diff --git a/src/pkg/runtime/gcinfo_test.go b/src/pkg/runtime/gcinfo_test.go
index 5e5b7ec375..7d432983b1 100644
--- a/src/pkg/runtime/gcinfo_test.go
+++ b/src/pkg/runtime/gcinfo_test.go
@@ -12,24 +12,37 @@ import (
// TestGCInfo tests that various objects in heap, data and bss receive correct GC pointer type info.
func TestGCInfo(t *testing.T) {
- verifyGCInfo(t, "bss ScalarPtr", &bssScalarPtr, infoScalarPtr)
- verifyGCInfo(t, "bss PtrScalar", &bssPtrScalar, infoPtrScalar)
- verifyGCInfo(t, "bss Complex", &bssComplex, infoComplex())
- verifyGCInfo(t, "bss string", &bssString, infoString)
- verifyGCInfo(t, "bss eface", &bssEface, infoEface)
-
- verifyGCInfo(t, "data ScalarPtr", &dataScalarPtr, infoScalarPtr)
- verifyGCInfo(t, "data PtrScalar", &dataPtrScalar, infoPtrScalar)
- verifyGCInfo(t, "data Complex", &dataComplex, infoComplex())
- verifyGCInfo(t, "data string", &dataString, infoString)
- verifyGCInfo(t, "data eface", &dataEface, infoEface)
-
- for i := 0; i < 3; i++ {
- verifyGCInfo(t, "heap ScalarPtr", escape(new(ScalarPtr)), infoScalarPtr)
- verifyGCInfo(t, "heap PtrScalar", escape(new(PtrScalar)), infoPtrScalar)
- verifyGCInfo(t, "heap Complex", escape(new(Complex)), infoComplex())
- verifyGCInfo(t, "heap string", escape(new(string)), infoString)
- verifyGCInfo(t, "heap eface", escape(new(interface{})), infoEface)
+ verifyGCInfo(t, "bss ScalarPtr", &bssScalarPtr, nonStackInfo(infoScalarPtr))
+ verifyGCInfo(t, "bss PtrScalar", &bssPtrScalar, nonStackInfo(infoPtrScalar))
+ verifyGCInfo(t, "bss BigStruct", &bssBigStruct, nonStackInfo(infoBigStruct()))
+ verifyGCInfo(t, "bss string", &bssString, nonStackInfo(infoString))
+ verifyGCInfo(t, "bss slice", &bssSlice, nonStackInfo(infoSlice))
+ verifyGCInfo(t, "bss eface", &bssEface, nonStackInfo(infoEface))
+ verifyGCInfo(t, "bss iface", &bssIface, nonStackInfo(infoIface))
+
+ verifyGCInfo(t, "data ScalarPtr", &dataScalarPtr, nonStackInfo(infoScalarPtr))
+ verifyGCInfo(t, "data PtrScalar", &dataPtrScalar, nonStackInfo(infoPtrScalar))
+ verifyGCInfo(t, "data BigStruct", &dataBigStruct, nonStackInfo(infoBigStruct()))
+ verifyGCInfo(t, "data string", &dataString, nonStackInfo(infoString))
+ verifyGCInfo(t, "data slice", &dataSlice, nonStackInfo(infoSlice))
+ verifyGCInfo(t, "data eface", &dataEface, nonStackInfo(infoEface))
+ verifyGCInfo(t, "data iface", &dataIface, nonStackInfo(infoIface))
+
+ verifyGCInfo(t, "stack ScalarPtr", new(ScalarPtr), infoScalarPtr)
+ verifyGCInfo(t, "stack PtrScalar", new(PtrScalar), infoPtrScalar)
+ verifyGCInfo(t, "stack BigStruct", new(BigStruct), infoBigStruct())
+ verifyGCInfo(t, "stack string", new(string), infoString)
+ verifyGCInfo(t, "stack slice", new([]string), infoSlice)
+ verifyGCInfo(t, "stack eface", new(interface{}), infoEface)
+ verifyGCInfo(t, "stack iface", new(Iface), infoIface)
+
+ for i := 0; i < 10; i++ {
+ verifyGCInfo(t, "heap ScalarPtr", escape(new(ScalarPtr)), nonStackInfo(infoScalarPtr))
+ verifyGCInfo(t, "heap PtrScalar", escape(new(PtrScalar)), nonStackInfo(infoPtrScalar))
+ verifyGCInfo(t, "heap BigStruct", escape(new(BigStruct)), nonStackInfo(infoBigStruct()))
+ verifyGCInfo(t, "heap string", escape(new(string)), nonStackInfo(infoString))
+ verifyGCInfo(t, "heap eface", escape(new(interface{})), nonStackInfo(infoEface))
+ verifyGCInfo(t, "heap iface", escape(new(Iface)), nonStackInfo(infoIface))
}
}
@@ -46,6 +59,20 @@ func verifyGCInfo(t *testing.T, name string, p interface{}, mask0 []byte) {
}
}
+func nonStackInfo(mask []byte) []byte {
+ // BitsDead is replaced with BitsScalar everywhere except stacks.
+ mask1 := make([]byte, len(mask))
+ mw := false
+ for i, v := range mask {
+ if !mw && v == BitsDead {
+ v = BitsScalar
+ }
+ mw = !mw && v == BitsMultiWord
+ mask1[i] = v
+ }
+ return mask1
+}
+
var gcinfoSink interface{}
func escape(p interface{}) interface{} {
@@ -61,8 +88,8 @@ const (
)
const (
- BitsString = iota
- BitsSlice
+ BitsString = iota // unused
+ BitsSlice // unused
BitsIface
BitsEface
)
@@ -89,7 +116,7 @@ type PtrScalar struct {
var infoPtrScalar = []byte{BitsPointer, BitsScalar, BitsPointer, BitsScalar, BitsPointer, BitsScalar}
-type Complex struct {
+type BigStruct struct {
q *int
w byte
e [17]byte
@@ -100,27 +127,31 @@ type Complex struct {
i string
}
-func infoComplex() []byte {
+func infoBigStruct() []byte {
switch runtime.GOARCH {
case "386", "arm":
return []byte{
- BitsPointer, BitsScalar, BitsScalar, BitsScalar,
- BitsScalar, BitsScalar, BitsMultiWord, BitsSlice,
- BitsScalar, BitsScalar, BitsScalar, BitsScalar,
- BitsScalar, BitsMultiWord, BitsString,
+ BitsPointer, // q *int
+ BitsScalar, BitsScalar, BitsScalar, BitsScalar, BitsScalar, // w byte; e [17]byte
+ BitsPointer, BitsDead, BitsDead, // r []byte
+ BitsScalar, BitsScalar, BitsScalar, BitsScalar, // t int; y uint16; u uint64
+ BitsPointer, BitsDead, // i string
}
case "amd64":
return []byte{
- BitsPointer, BitsScalar, BitsScalar, BitsScalar,
- BitsMultiWord, BitsSlice, BitsScalar, BitsScalar,
- BitsScalar, BitsScalar, BitsMultiWord, BitsString,
+ BitsPointer, // q *int
+ BitsScalar, BitsScalar, BitsScalar, // w byte; e [17]byte
+ BitsPointer, BitsDead, BitsDead, // r []byte
+ BitsScalar, BitsScalar, BitsScalar, // t int; y uint16; u uint64
+ BitsPointer, BitsDead, // i string
}
case "amd64p32":
return []byte{
- BitsPointer, BitsScalar, BitsScalar, BitsScalar,
- BitsScalar, BitsScalar, BitsMultiWord, BitsSlice,
- BitsScalar, BitsScalar, BitsScalar, BitsScalar,
- BitsScalar, BitsScalar, BitsMultiWord, BitsString,
+ BitsPointer, // q *int
+ BitsScalar, BitsScalar, BitsScalar, BitsScalar, BitsScalar, // w byte; e [17]byte
+ BitsPointer, BitsDead, BitsDead, // r []byte
+ BitsScalar, BitsScalar, BitsDead, BitsScalar, BitsScalar, // t int; y uint16; u uint64
+ BitsPointer, BitsDead, // i string
}
case "power64", "power64le":
return []byte{
@@ -133,21 +164,36 @@ func infoComplex() []byte {
}
}
+type Iface interface {
+ f()
+}
+
+type IfaceImpl int
+
+func (IfaceImpl) f() {
+}
+
var (
// BSS
bssScalarPtr ScalarPtr
bssPtrScalar PtrScalar
- bssComplex Complex
+ bssBigStruct BigStruct
bssString string
+ bssSlice []string
bssEface interface{}
+ bssIface Iface
// DATA
dataScalarPtr = ScalarPtr{q: 1}
dataPtrScalar = PtrScalar{w: 1}
- dataComplex = Complex{w: 1}
+ dataBigStruct = BigStruct{w: 1}
dataString = "foo"
+ dataSlice = []string{"foo"}
dataEface interface{} = 42
+ dataIface Iface = IfaceImpl(42)
- infoString = []byte{BitsMultiWord, BitsString}
+ infoString = []byte{BitsPointer, BitsDead}
+ infoSlice = []byte{BitsPointer, BitsDead, BitsDead}
infoEface = []byte{BitsMultiWord, BitsEface}
+ infoIface = []byte{BitsMultiWord, BitsIface}
)
diff --git a/src/pkg/runtime/hash_test.go b/src/pkg/runtime/hash_test.go
index 1c11e0538d..41fff98eb0 100644
--- a/src/pkg/runtime/hash_test.go
+++ b/src/pkg/runtime/hash_test.go
@@ -344,6 +344,64 @@ func (k *Int64Key) name() string {
return "int64"
}
+type EfaceKey struct {
+ i interface{}
+}
+
+func (k *EfaceKey) clear() {
+ k.i = nil
+}
+func (k *EfaceKey) random(r *rand.Rand) {
+ k.i = uint64(r.Int63())
+}
+func (k *EfaceKey) bits() int {
+ // use 64 bits. This tests inlined interfaces
+ // on 64-bit targets and indirect interfaces on
+ // 32-bit targets.
+ return 64
+}
+func (k *EfaceKey) flipBit(i int) {
+ k.i = k.i.(uint64) ^ uint64(1)<<uint(i)
+}
+func (k *EfaceKey) hash() uintptr {
+ return EfaceHash(k.i, 0)
+}
+func (k *EfaceKey) name() string {
+ return "Eface"
+}
+
+type IfaceKey struct {
+ i interface {
+ F()
+ }
+}
+type fInter uint64
+
+func (x fInter) F() {
+}
+
+func (k *IfaceKey) clear() {
+ k.i = nil
+}
+func (k *IfaceKey) random(r *rand.Rand) {
+ k.i = fInter(r.Int63())
+}
+func (k *IfaceKey) bits() int {
+ // use 64 bits. This tests inlined interfaces
+ // on 64-bit targets and indirect interfaces on
+ // 32-bit targets.
+ return 64
+}
+func (k *IfaceKey) flipBit(i int) {
+ k.i = k.i.(fInter) ^ fInter(1)<<uint(i)
+}
+func (k *IfaceKey) hash() uintptr {
+ return IfaceHash(k.i, 0)
+}
+func (k *IfaceKey) name() string {
+ return "Iface"
+}
+
// Flipping a single bit of a key should flip each output bit with 50% probability.
func TestSmhasherAvalanche(t *testing.T) {
if !HaveGoodHash() {
@@ -360,6 +418,8 @@ func TestSmhasherAvalanche(t *testing.T) {
avalancheTest1(t, &BytesKey{make([]byte, 200)})
avalancheTest1(t, &Int32Key{})
avalancheTest1(t, &Int64Key{})
+ avalancheTest1(t, &EfaceKey{})
+ avalancheTest1(t, &IfaceKey{})
}
func avalancheTest1(t *testing.T, k Key) {
const REP = 100000
diff --git a/src/pkg/runtime/hashmap.go b/src/pkg/runtime/hashmap.go
index 6706290974..55287f6ff9 100644
--- a/src/pkg/runtime/hashmap.go
+++ b/src/pkg/runtime/hashmap.go
@@ -162,12 +162,12 @@ func makemap(t *maptype, hint int64) *hmap {
}
// check compiler's and reflect's math
- if t.key.size > maxKeySize && (t.indirectkey == 0 || t.keysize != uint8(ptrSize)) ||
- t.key.size <= maxKeySize && (t.indirectkey == 1 || t.keysize != uint8(t.key.size)) {
+ if t.key.size > maxKeySize && (!t.indirectkey || t.keysize != uint8(ptrSize)) ||
+ t.key.size <= maxKeySize && (t.indirectkey || t.keysize != uint8(t.key.size)) {
gothrow("key size wrong")
}
- if t.elem.size > maxValueSize && (t.indirectvalue == 0 || t.valuesize != uint8(ptrSize)) ||
- t.elem.size <= maxValueSize && (t.indirectvalue == 1 || t.valuesize != uint8(t.elem.size)) {
+ if t.elem.size > maxValueSize && (!t.indirectvalue || t.valuesize != uint8(ptrSize)) ||
+ t.elem.size <= maxValueSize && (t.indirectvalue || t.valuesize != uint8(t.elem.size)) {
gothrow("value size wrong")
}
@@ -219,7 +219,7 @@ func makemap(t *maptype, hint int64) *hmap {
h.count = 0
h.B = B
h.flags = 0
- h.hash0 = fastrand2()
+ h.hash0 = fastrand1()
h.buckets = buckets
h.oldbuckets = nil
h.nevacuate = 0
@@ -234,16 +234,16 @@ func makemap(t *maptype, hint int64) *hmap {
// hold onto it for very long.
func mapaccess1(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {
if raceenabled && h != nil {
- callerpc := gogetcallerpc(unsafe.Pointer(&t))
- fn := mapaccess1
- pc := **(**uintptr)(unsafe.Pointer(&fn))
+ callerpc := getcallerpc(unsafe.Pointer(&t))
+ pc := funcPC(mapaccess1)
racereadpc(unsafe.Pointer(h), callerpc, pc)
raceReadObjectPC(t.key, key, callerpc, pc)
}
if h == nil || h.count == 0 {
return unsafe.Pointer(t.elem.zero)
}
- hash := goalg(t.key.alg).hash(key, uintptr(t.key.size), uintptr(h.hash0))
+ alg := goalg(t.key.alg)
+ hash := alg.hash(key, uintptr(t.key.size), uintptr(h.hash0))
m := uintptr(1)<<h.B - 1
b := (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize)))
if c := h.oldbuckets; c != nil {
@@ -262,12 +262,12 @@ func mapaccess1(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {
continue
}
k := add(unsafe.Pointer(b), dataOffset+i*uintptr(t.keysize))
- if t.indirectkey != 0 {
+ if t.indirectkey {
k = *((*unsafe.Pointer)(k))
}
- if goeq(t.key.alg, key, k, uintptr(t.key.size)) {
+ if alg.equal(key, k, uintptr(t.key.size)) {
v := add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+i*uintptr(t.valuesize))
- if t.indirectvalue != 0 {
+ if t.indirectvalue {
v = *((*unsafe.Pointer)(v))
}
return v
@@ -282,16 +282,16 @@ func mapaccess1(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {
func mapaccess2(t *maptype, h *hmap, key unsafe.Pointer) (unsafe.Pointer, bool) {
if raceenabled && h != nil {
- callerpc := gogetcallerpc(unsafe.Pointer(&t))
- fn := mapaccess2
- pc := **(**uintptr)(unsafe.Pointer(&fn))
+ callerpc := getcallerpc(unsafe.Pointer(&t))
+ pc := funcPC(mapaccess2)
racereadpc(unsafe.Pointer(h), callerpc, pc)
raceReadObjectPC(t.key, key, callerpc, pc)
}
if h == nil || h.count == 0 {
return unsafe.Pointer(t.elem.zero), false
}
- hash := goalg(t.key.alg).hash(key, uintptr(t.key.size), uintptr(h.hash0))
+ alg := goalg(t.key.alg)
+ hash := alg.hash(key, uintptr(t.key.size), uintptr(h.hash0))
m := uintptr(1)<<h.B - 1
b := (*bmap)(unsafe.Pointer(uintptr(h.buckets) + (hash&m)*uintptr(t.bucketsize)))
if c := h.oldbuckets; c != nil {
@@ -310,12 +310,12 @@ func mapaccess2(t *maptype, h *hmap, key unsafe.Pointer) (unsafe.Pointer, bool)
continue
}
k := add(unsafe.Pointer(b), dataOffset+i*uintptr(t.keysize))
- if t.indirectkey != 0 {
+ if t.indirectkey {
k = *((*unsafe.Pointer)(k))
}
- if goeq(t.key.alg, key, k, uintptr(t.key.size)) {
+ if alg.equal(key, k, uintptr(t.key.size)) {
v := add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+i*uintptr(t.valuesize))
- if t.indirectvalue != 0 {
+ if t.indirectvalue {
v = *((*unsafe.Pointer)(v))
}
return v, true
@@ -333,7 +333,8 @@ func mapaccessK(t *maptype, h *hmap, key unsafe.Pointer) (unsafe.Pointer, unsafe
if h == nil || h.count == 0 {
return nil, nil
}
- hash := goalg(t.key.alg).hash(key, uintptr(t.key.size), uintptr(h.hash0))
+ alg := goalg(t.key.alg)
+ hash := alg.hash(key, uintptr(t.key.size), uintptr(h.hash0))
m := uintptr(1)<<h.B - 1
b := (*bmap)(unsafe.Pointer(uintptr(h.buckets) + (hash&m)*uintptr(t.bucketsize)))
if c := h.oldbuckets; c != nil {
@@ -352,12 +353,12 @@ func mapaccessK(t *maptype, h *hmap, key unsafe.Pointer) (unsafe.Pointer, unsafe
continue
}
k := add(unsafe.Pointer(b), dataOffset+i*uintptr(t.keysize))
- if t.indirectkey != 0 {
+ if t.indirectkey {
k = *((*unsafe.Pointer)(k))
}
- if goeq(t.key.alg, key, k, uintptr(t.key.size)) {
+ if alg.equal(key, k, uintptr(t.key.size)) {
v := add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+i*uintptr(t.valuesize))
- if t.indirectvalue != 0 {
+ if t.indirectvalue {
v = *((*unsafe.Pointer)(v))
}
return k, v
@@ -375,15 +376,15 @@ func mapassign1(t *maptype, h *hmap, key unsafe.Pointer, val unsafe.Pointer) {
panic("assignment to entry in nil map")
}
if raceenabled {
- callerpc := gogetcallerpc(unsafe.Pointer(&t))
- fn := mapassign1
- pc := **(**uintptr)(unsafe.Pointer(&fn))
+ callerpc := getcallerpc(unsafe.Pointer(&t))
+ pc := funcPC(mapassign1)
racewritepc(unsafe.Pointer(h), callerpc, pc)
raceReadObjectPC(t.key, key, callerpc, pc)
raceReadObjectPC(t.elem, val, callerpc, pc)
}
- hash := goalg(t.key.alg).hash(key, uintptr(t.key.size), uintptr(h.hash0))
+ alg := goalg(t.key.alg)
+ hash := alg.hash(key, uintptr(t.key.size), uintptr(h.hash0))
if h.buckets == nil {
if checkgc {
@@ -418,17 +419,17 @@ again:
}
k := add(unsafe.Pointer(b), dataOffset+i*uintptr(t.keysize))
k2 := k
- if t.indirectkey != 0 {
+ if t.indirectkey {
k2 = *((*unsafe.Pointer)(k2))
}
- if !goeq(t.key.alg, key, k2, uintptr(t.key.size)) {
+ if !alg.equal(key, k2, uintptr(t.key.size)) {
continue
}
// already have a mapping for key. Update it.
memmove(k2, key, uintptr(t.key.size))
v := add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+i*uintptr(t.valuesize))
v2 := v
- if t.indirectvalue != 0 {
+ if t.indirectvalue {
v2 = *((*unsafe.Pointer)(v2))
}
memmove(v2, val, uintptr(t.elem.size))
@@ -459,7 +460,7 @@ again:
}
// store new key/value at insert position
- if t.indirectkey != 0 {
+ if t.indirectkey {
if checkgc {
memstats.next_gc = memstats.heap_alloc
}
@@ -467,7 +468,7 @@ again:
*(*unsafe.Pointer)(insertk) = kmem
insertk = kmem
}
- if t.indirectvalue != 0 {
+ if t.indirectvalue {
if checkgc {
memstats.next_gc = memstats.heap_alloc
}
@@ -483,16 +484,16 @@ again:
func mapdelete(t *maptype, h *hmap, key unsafe.Pointer) {
if raceenabled && h != nil {
- callerpc := gogetcallerpc(unsafe.Pointer(&t))
- fn := mapdelete
- pc := **(**uintptr)(unsafe.Pointer(&fn))
+ callerpc := getcallerpc(unsafe.Pointer(&t))
+ pc := funcPC(mapdelete)
racewritepc(unsafe.Pointer(h), callerpc, pc)
raceReadObjectPC(t.key, key, callerpc, pc)
}
if h == nil || h.count == 0 {
return
}
- hash := goalg(t.key.alg).hash(key, uintptr(t.key.size), uintptr(h.hash0))
+ alg := goalg(t.key.alg)
+ hash := alg.hash(key, uintptr(t.key.size), uintptr(h.hash0))
bucket := hash & (uintptr(1)<<h.B - 1)
if h.oldbuckets != nil {
growWork(t, h, bucket)
@@ -509,10 +510,10 @@ func mapdelete(t *maptype, h *hmap, key unsafe.Pointer) {
}
k := add(unsafe.Pointer(b), dataOffset+i*uintptr(t.keysize))
k2 := k
- if t.indirectkey != 0 {
+ if t.indirectkey {
k2 = *((*unsafe.Pointer)(k2))
}
- if !goeq(t.key.alg, key, k2, uintptr(t.key.size)) {
+ if !alg.equal(key, k2, uintptr(t.key.size)) {
continue
}
memclr(k, uintptr(t.keysize))
@@ -539,10 +540,8 @@ func mapiterinit(t *maptype, h *hmap, it *hiter) {
it.bptr = nil
if raceenabled && h != nil {
- callerpc := gogetcallerpc(unsafe.Pointer(&t))
- fn := mapiterinit
- pc := **(**uintptr)(unsafe.Pointer(&fn))
- racereadpc(unsafe.Pointer(h), callerpc, pc)
+ callerpc := getcallerpc(unsafe.Pointer(&t))
+ racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapiterinit))
}
if h == nil || h.count == 0 {
@@ -563,7 +562,7 @@ func mapiterinit(t *maptype, h *hmap, it *hiter) {
// iterator state
it.bucket = 0
- it.offset = uint8(fastrand2() & (bucketCnt - 1))
+ it.offset = uint8(fastrand1() & (bucketCnt - 1))
it.done = false
it.bptr = nil
@@ -574,7 +573,7 @@ func mapiterinit(t *maptype, h *hmap, it *hiter) {
if old == old|iterator|oldIterator {
break
}
- if gocas(&h.flags, old, old|iterator|oldIterator) {
+ if cas(&h.flags, old, old|iterator|oldIterator) {
break
}
}
@@ -585,16 +584,15 @@ func mapiterinit(t *maptype, h *hmap, it *hiter) {
func mapiternext(it *hiter) {
h := it.h
if raceenabled {
- callerpc := gogetcallerpc(unsafe.Pointer(&it))
- fn := mapiternext
- pc := **(**uintptr)(unsafe.Pointer(&fn))
- racereadpc(unsafe.Pointer(h), callerpc, pc)
+ callerpc := getcallerpc(unsafe.Pointer(&it))
+ racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapiternext))
}
t := it.t
bucket := it.bucket
b := it.bptr
i := it.i
checkBucket := it.checkBucket
+ alg := goalg(t.key.alg)
next:
if b == nil {
@@ -642,13 +640,13 @@ next:
// to the other new bucket (each oldbucket expands to two
// buckets during a grow).
k2 := k
- if t.indirectkey != 0 {
+ if t.indirectkey {
k2 = *((*unsafe.Pointer)(k2))
}
- if goeq(t.key.alg, k2, k2, uintptr(t.key.size)) {
+ if alg.equal(k2, k2, uintptr(t.key.size)) {
// If the item in the oldbucket is not destined for
// the current new bucket in the iteration, skip it.
- hash := goalg(t.key.alg).hash(k2, uintptr(t.key.size), uintptr(h.hash0))
+ hash := alg.hash(k2, uintptr(t.key.size), uintptr(h.hash0))
if hash&(uintptr(1)<<it.B-1) != checkBucket {
continue
}
@@ -667,11 +665,11 @@ next:
}
if b.tophash[offi] != evacuatedX && b.tophash[offi] != evacuatedY {
// this is the golden data, we can return it.
- if t.indirectkey != 0 {
+ if t.indirectkey {
k = *((*unsafe.Pointer)(k))
}
it.key = k
- if t.indirectvalue != 0 {
+ if t.indirectvalue {
v = *((*unsafe.Pointer)(v))
}
it.value = v
@@ -679,10 +677,10 @@ next:
// The hash table has grown since the iterator was started.
// The golden data for this key is now somewhere else.
k2 := k
- if t.indirectkey != 0 {
+ if t.indirectkey {
k2 = *((*unsafe.Pointer)(k2))
}
- if goeq(t.key.alg, k2, k2, uintptr(t.key.size)) {
+ if alg.equal(k2, k2, uintptr(t.key.size)) {
// Check the current hash table for the data.
// This code handles the case where the key
// has been deleted, updated, or deleted and reinserted.
@@ -700,7 +698,7 @@ next:
// us because when key!=key we can't look it up
// successfully in the current table.
it.key = k2
- if t.indirectvalue != 0 {
+ if t.indirectvalue {
v = *((*unsafe.Pointer)(v))
}
it.value = v
@@ -758,6 +756,7 @@ func growWork(t *maptype, h *hmap, bucket uintptr) {
func evacuate(t *maptype, h *hmap, oldbucket uintptr) {
b := (*bmap)(add(h.oldbuckets, oldbucket*uintptr(t.bucketsize)))
newbit := uintptr(1) << (h.B - 1)
+ alg := goalg(t.key.alg)
if !evacuated(b) {
// TODO: reuse overflow buckets instead of using new ones, if there
// is no iterator using the old buckets. (If !oldIterator.)
@@ -783,14 +782,14 @@ func evacuate(t *maptype, h *hmap, oldbucket uintptr) {
gothrow("bad map state")
}
k2 := k
- if t.indirectkey != 0 {
+ if t.indirectkey {
k2 = *((*unsafe.Pointer)(k2))
}
// Compute hash to make our evacuation decision (whether we need
// to send this key/value to bucket x or bucket y).
- hash := goalg(t.key.alg).hash(k2, uintptr(t.key.size), uintptr(h.hash0))
+ hash := alg.hash(k2, uintptr(t.key.size), uintptr(h.hash0))
if h.flags&iterator != 0 {
- if !goeq(t.key.alg, k2, k2, uintptr(t.key.size)) {
+ if !alg.equal(k2, k2, uintptr(t.key.size)) {
// If key != key (NaNs), then the hash could be (and probably
// will be) entirely different from the old hash. Moreover,
// it isn't reproducible. Reproducibility is required in the
@@ -827,12 +826,12 @@ func evacuate(t *maptype, h *hmap, oldbucket uintptr) {
xv = add(xk, bucketCnt*uintptr(t.keysize))
}
x.tophash[xi] = top
- if t.indirectkey != 0 {
+ if t.indirectkey {
*(*unsafe.Pointer)(xk) = k2 // copy pointer
} else {
memmove(xk, k, uintptr(t.key.size)) // copy value
}
- if t.indirectvalue != 0 {
+ if t.indirectvalue {
*(*unsafe.Pointer)(xv) = *(*unsafe.Pointer)(v)
} else {
memmove(xv, v, uintptr(t.elem.size))
@@ -854,12 +853,12 @@ func evacuate(t *maptype, h *hmap, oldbucket uintptr) {
yv = add(yk, bucketCnt*uintptr(t.keysize))
}
y.tophash[yi] = top
- if t.indirectkey != 0 {
+ if t.indirectkey {
*(*unsafe.Pointer)(yk) = k2
} else {
memmove(yk, k, uintptr(t.key.size))
}
- if t.indirectvalue != 0 {
+ if t.indirectvalue {
*(*unsafe.Pointer)(yv) = *(*unsafe.Pointer)(v)
} else {
memmove(yv, v, uintptr(t.elem.size))
@@ -889,7 +888,7 @@ func evacuate(t *maptype, h *hmap, oldbucket uintptr) {
}
func ismapkey(t *_type) bool {
- return **(**uintptr)(unsafe.Pointer(&t.alg.hash)) != nohashcode
+ return goalg(t.alg).hash != nil
}
// Reflect stubs. Called from ../reflect/asm_*.s
@@ -934,10 +933,8 @@ func reflect_maplen(h *hmap) int {
return 0
}
if raceenabled {
- callerpc := gogetcallerpc(unsafe.Pointer(&h))
- fn := reflect_maplen
- pc := **(**uintptr)(unsafe.Pointer(&fn))
- racereadpc(unsafe.Pointer(h), callerpc, pc)
+ callerpc := getcallerpc(unsafe.Pointer(&h))
+ racereadpc(unsafe.Pointer(h), callerpc, funcPC(reflect_maplen))
}
return h.count
}
diff --git a/src/pkg/runtime/hashmap_fast.go b/src/pkg/runtime/hashmap_fast.go
index 989ae032bd..8e21e02d64 100644
--- a/src/pkg/runtime/hashmap_fast.go
+++ b/src/pkg/runtime/hashmap_fast.go
@@ -10,10 +10,8 @@ import (
func mapaccess1_fast32(t *maptype, h *hmap, key uint32) unsafe.Pointer {
if raceenabled && h != nil {
- callerpc := gogetcallerpc(unsafe.Pointer(&t))
- fn := mapaccess1_fast32
- pc := **(**uintptr)(unsafe.Pointer(&fn))
- racereadpc(unsafe.Pointer(h), callerpc, pc)
+ callerpc := getcallerpc(unsafe.Pointer(&t))
+ racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess1_fast32))
}
if h == nil || h.count == 0 {
return unsafe.Pointer(t.elem.zero)
@@ -54,10 +52,8 @@ func mapaccess1_fast32(t *maptype, h *hmap, key uint32) unsafe.Pointer {
func mapaccess2_fast32(t *maptype, h *hmap, key uint32) (unsafe.Pointer, bool) {
if raceenabled && h != nil {
- callerpc := gogetcallerpc(unsafe.Pointer(&t))
- fn := mapaccess2_fast32
- pc := **(**uintptr)(unsafe.Pointer(&fn))
- racereadpc(unsafe.Pointer(h), callerpc, pc)
+ callerpc := getcallerpc(unsafe.Pointer(&t))
+ racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_fast32))
}
if h == nil || h.count == 0 {
return unsafe.Pointer(t.elem.zero), false
@@ -98,10 +94,8 @@ func mapaccess2_fast32(t *maptype, h *hmap, key uint32) (unsafe.Pointer, bool) {
func mapaccess1_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer {
if raceenabled && h != nil {
- callerpc := gogetcallerpc(unsafe.Pointer(&t))
- fn := mapaccess1_fast64
- pc := **(**uintptr)(unsafe.Pointer(&fn))
- racereadpc(unsafe.Pointer(h), callerpc, pc)
+ callerpc := getcallerpc(unsafe.Pointer(&t))
+ racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess1_fast64))
}
if h == nil || h.count == 0 {
return unsafe.Pointer(t.elem.zero)
@@ -142,10 +136,8 @@ func mapaccess1_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer {
func mapaccess2_fast64(t *maptype, h *hmap, key uint64) (unsafe.Pointer, bool) {
if raceenabled && h != nil {
- callerpc := gogetcallerpc(unsafe.Pointer(&t))
- fn := mapaccess2_fast64
- pc := **(**uintptr)(unsafe.Pointer(&fn))
- racereadpc(unsafe.Pointer(h), callerpc, pc)
+ callerpc := getcallerpc(unsafe.Pointer(&t))
+ racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_fast64))
}
if h == nil || h.count == 0 {
return unsafe.Pointer(t.elem.zero), false
@@ -186,10 +178,8 @@ func mapaccess2_fast64(t *maptype, h *hmap, key uint64) (unsafe.Pointer, bool) {
func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer {
if raceenabled && h != nil {
- callerpc := gogetcallerpc(unsafe.Pointer(&t))
- fn := mapaccess1_faststr
- pc := **(**uintptr)(unsafe.Pointer(&fn))
- racereadpc(unsafe.Pointer(h), callerpc, pc)
+ callerpc := getcallerpc(unsafe.Pointer(&t))
+ racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess1_faststr))
}
if h == nil || h.count == 0 {
return unsafe.Pointer(t.elem.zero)
@@ -209,7 +199,7 @@ func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer {
if k.len != key.len {
continue
}
- if k.str == key.str || gomemeq(k.str, key.str, uintptr(key.len)) {
+ if k.str == key.str || memeq(k.str, key.str, uintptr(key.len)) {
return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*ptrSize+i*uintptr(t.valuesize))
}
}
@@ -247,7 +237,7 @@ func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer {
}
if keymaybe != bucketCnt {
k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+keymaybe*2*ptrSize))
- if gomemeq(k.str, key.str, uintptr(key.len)) {
+ if memeq(k.str, key.str, uintptr(key.len)) {
return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*ptrSize+keymaybe*uintptr(t.valuesize))
}
}
@@ -277,7 +267,7 @@ dohash:
if k.len != key.len {
continue
}
- if k.str == key.str || gomemeq(k.str, key.str, uintptr(key.len)) {
+ if k.str == key.str || memeq(k.str, key.str, uintptr(key.len)) {
return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*ptrSize+i*uintptr(t.valuesize))
}
}
@@ -290,10 +280,8 @@ dohash:
func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) {
if raceenabled && h != nil {
- callerpc := gogetcallerpc(unsafe.Pointer(&t))
- fn := mapaccess2_faststr
- pc := **(**uintptr)(unsafe.Pointer(&fn))
- racereadpc(unsafe.Pointer(h), callerpc, pc)
+ callerpc := getcallerpc(unsafe.Pointer(&t))
+ racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_faststr))
}
if h == nil || h.count == 0 {
return unsafe.Pointer(t.elem.zero), false
@@ -313,7 +301,7 @@ func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) {
if k.len != key.len {
continue
}
- if k.str == key.str || gomemeq(k.str, key.str, uintptr(key.len)) {
+ if k.str == key.str || memeq(k.str, key.str, uintptr(key.len)) {
return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*ptrSize+i*uintptr(t.valuesize)), true
}
}
@@ -349,7 +337,7 @@ func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) {
}
if keymaybe != bucketCnt {
k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+keymaybe*2*ptrSize))
- if gomemeq(k.str, key.str, uintptr(key.len)) {
+ if memeq(k.str, key.str, uintptr(key.len)) {
return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*ptrSize+keymaybe*uintptr(t.valuesize)), true
}
}
@@ -379,7 +367,7 @@ dohash:
if k.len != key.len {
continue
}
- if k.str == key.str || gomemeq(k.str, key.str, uintptr(key.len)) {
+ if k.str == key.str || memeq(k.str, key.str, uintptr(key.len)) {
return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*ptrSize+i*uintptr(t.valuesize)), true
}
}
diff --git a/src/pkg/runtime/heapdump.c b/src/pkg/runtime/heapdump.c
index f29cf0108f..5696470f29 100644
--- a/src/pkg/runtime/heapdump.c
+++ b/src/pkg/runtime/heapdump.c
@@ -17,14 +17,12 @@
#include "typekind.h"
#include "funcdata.h"
#include "zaexperiment.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
-extern byte data[];
-extern byte edata[];
-extern byte bss[];
-extern byte ebss[];
-extern byte gcdata[];
-extern byte gcbss[];
+extern byte runtime·data[];
+extern byte runtime·edata[];
+extern byte runtime·bss[];
+extern byte runtime·ebss[];
enum {
FieldKindEol = 0,
@@ -198,7 +196,7 @@ dumptype(Type *t)
write((byte*)".", 1);
write(t->x->name->str, t->x->name->len);
}
- dumpbool(t->size > PtrSize || (t->kind & KindNoPointers) == 0);
+ dumpbool((t->kind & KindDirectIface) == 0 || (t->kind & KindNoPointers) == 0);
dumpfields((BitVector){0, nil});
}
@@ -262,16 +260,8 @@ dumpbv(BitVector *bv, uintptr offset)
break;
case BitsMultiWord:
switch(bv->bytedata[(i+BitsPerPointer)/8] >> (i+BitsPerPointer)%8 & 3) {
- case BitsString:
- dumpint(FieldKindString);
- dumpint(offset + i / BitsPerPointer * PtrSize);
- i += BitsPerPointer;
- break;
- case BitsSlice:
- dumpint(FieldKindSlice);
- dumpint(offset + i / BitsPerPointer * PtrSize);
- i += 2 * BitsPerPointer;
- break;
+ default:
+ runtime·throw("unexpected garbage collection bits");
case BitsIface:
dumpint(FieldKindIface);
dumpint(offset + i / BitsPerPointer * PtrSize);
@@ -319,7 +309,7 @@ dumpframe(Stkframe *s, void *arg)
dumpbvtypes(&child->args, (byte*)s->sp + child->argoff);
if(stackmap != nil && stackmap->n > 0) {
bv = runtime·stackmapdata(stackmap, pcdata);
- dumpbvtypes(&bv, s->varp - bv.n / BitsPerPointer * PtrSize);
+ dumpbvtypes(&bv, (byte*)(s->varp - bv.n / BitsPerPointer * PtrSize));
} else {
bv.n = -1;
}
@@ -352,26 +342,26 @@ dumpframe(Stkframe *s, void *arg)
// Dump fields in the local vars section
if(stackmap == nil) {
// No locals information, dump everything.
- for(off = child->arglen; off < s->varp - (byte*)s->sp; off += PtrSize) {
+ for(off = child->arglen; off < s->varp - s->sp; off += PtrSize) {
dumpint(FieldKindPtr);
dumpint(off);
}
} else if(stackmap->n < 0) {
// Locals size information, dump just the locals.
size = -stackmap->n;
- for(off = s->varp - size - (byte*)s->sp; off < s->varp - (byte*)s->sp; off += PtrSize) {
+ for(off = s->varp - size - s->sp; off < s->varp - s->sp; off += PtrSize) {
dumpint(FieldKindPtr);
dumpint(off);
}
} else if(stackmap->n > 0) {
// Locals bitmap information, scan just the pointers in
// locals.
- dumpbv(&bv, s->varp - bv.n / BitsPerPointer * PtrSize - (byte*)s->sp);
+ dumpbv(&bv, s->varp - bv.n / BitsPerPointer * PtrSize - s->sp);
}
dumpint(FieldKindEol);
// Record arg info for parent.
- child->argoff = s->argp - (byte*)s->fp;
+ child->argoff = s->argp - s->fp;
child->arglen = s->arglen;
child->sp = (byte*)s->sp;
child->depth++;
@@ -390,6 +380,7 @@ dumpgoroutine(G *gp)
ChildInfo child;
Defer *d;
Panic *p;
+ bool (*fn)(Stkframe*, void*);
if(gp->syscallstack != (uintptr)nil) {
sp = gp->syscallsp;
@@ -406,11 +397,11 @@ dumpgoroutine(G *gp)
dumpint((uintptr)sp);
dumpint(gp->goid);
dumpint(gp->gopc);
- dumpint(gp->status);
+ dumpint(runtime·readgstatus(gp));
dumpbool(gp->issystem);
- dumpbool(gp->isbackground);
+ dumpbool(false); // isbackground
dumpint(gp->waitsince);
- dumpcstr(gp->waitreason);
+ dumpstr(gp->waitreason);
dumpint((uintptr)gp->sched.ctxt);
dumpint((uintptr)gp->m);
dumpint((uintptr)gp->defer);
@@ -423,7 +414,8 @@ dumpgoroutine(G *gp)
child.depth = 0;
if(!ScanStackByFrames)
runtime·throw("need frame info to dump stacks");
- runtime·gentraceback(pc, sp, lr, gp, 0, nil, 0x7fffffff, dumpframe, &child, false);
+ fn = dumpframe;
+ runtime·gentraceback(pc, sp, lr, gp, 0, nil, 0x7fffffff, &fn, &child, false);
// dump defer & panic records
for(d = gp->defer; d != nil; d = d->link) {
@@ -452,14 +444,16 @@ dumpgs(void)
{
G *gp;
uint32 i;
+ uint32 status;
// goroutines & stacks
for(i = 0; i < runtime·allglen; i++) {
gp = runtime·allg[i];
- switch(gp->status){
+ status = runtime·readgstatus(gp); // The world is stopped so gp will not be in a scan state.
+ switch(status){
default:
- runtime·printf("unexpected G.status %d\n", gp->status);
- runtime·throw("mark - bad status");
+ runtime·printf("runtime: unexpected G.status %d\n", status);
+ runtime·throw("dumpgs in STW - bad status");
case Gdead:
break;
case Grunnable:
@@ -495,15 +489,15 @@ dumproots(void)
// data segment
dumpint(TagData);
- dumpint((uintptr)data);
- dumpmemrange(data, edata - data);
- dumpfields((BitVector){(edata - data)*8, (byte*)gcdata}); /* WRONG! gcbss is not a bitmap */
+ dumpint((uintptr)runtime·data);
+ dumpmemrange(runtime·data, runtime·edata - runtime·data);
+ dumpfields(runtime·gcdatamask);
// bss segment
dumpint(TagBss);
- dumpint((uintptr)bss);
- dumpmemrange(bss, ebss - bss);
- dumpfields((BitVector){(ebss - bss)*8, (byte*)gcbss}); /* WRONG! gcbss is not a bitmap */
+ dumpint((uintptr)runtime·bss);
+ dumpmemrange(runtime·bss, runtime·ebss - runtime·bss);
+ dumpfields(runtime·gcdatamask);
// MSpan.types
allspans = runtime·mheap.allspans;
@@ -515,7 +509,7 @@ dumproots(void)
if(sp->kind != KindSpecialFinalizer)
continue;
spf = (SpecialFinalizer*)sp;
- p = (byte*)((s->start << PageShift) + spf->offset);
+ p = (byte*)((s->start << PageShift) + spf->special.offset);
dumpfinalizer(p, spf->fn, spf->fint, spf->ot);
}
}
@@ -525,11 +519,17 @@ dumproots(void)
runtime·iterate_finq(finq_callback);
}
+// Bit vector of free marks.
+// Needs to be as big as the largest number of objects per span.
+#pragma dataflag NOPTR
+static byte free[PageSize/8];
+
static void
dumpobjs(void)
{
- uintptr i, j, size, n, off, shift, *bitp, bits;
+ uintptr i, j, size, n;
MSpan *s;
+ MLink *l;
byte *p;
for(i = 0; i < runtime·mheap.nspan; i++) {
@@ -539,15 +539,15 @@ dumpobjs(void)
p = (byte*)(s->start << PageShift);
size = s->elemsize;
n = (s->npages << PageShift) / size;
+ if(n > nelem(free))
+ runtime·throw("free array doesn't have enough entries");
+ for(l = s->freelist; l != nil; l = l->next)
+ free[((byte*)l - p) / size] = true;
for(j = 0; j < n; j++, p += size) {
- off = (uintptr*)p - (uintptr*)runtime·mheap.arena_start;
- bitp = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1;
- shift = (off % wordsPerBitmapWord) * gcBits;
- bits = (*bitp >> shift) & bitMask;
-
- // Skip FlagNoGC allocations (stacks)
- if(bits != bitAllocated)
- continue;
+ if(free[j]) {
+ free[j] = false;
+ continue;
+ }
dumpobj(p, size, makeheapobjbv(p, size));
}
}
@@ -580,13 +580,16 @@ itab_callback(Itab *tab)
dumpint(TagItab);
dumpint((uintptr)tab);
t = tab->type;
- dumpbool(t->size > PtrSize || (t->kind & KindNoPointers) == 0);
+ dumpbool((t->kind & KindDirectIface) == 0 || (t->kind & KindNoPointers) == 0);
}
static void
dumpitabs(void)
{
- runtime·iterate_itabs(itab_callback);
+ void (*fn)(Itab*);
+
+ fn = itab_callback;
+ runtime·iterate_itabs(&fn);
}
static void
@@ -685,8 +688,10 @@ dumpmemprof(void)
Special *sp;
SpecialProfile *spp;
byte *p;
-
- runtime·iterate_memprof(dumpmemprof_callback);
+ void (*fn)(Bucket*, uintptr, uintptr*, uintptr, uintptr, uintptr);
+
+ fn = dumpmemprof_callback;
+ runtime·iterate_memprof(&fn);
allspans = runtime·mheap.allspans;
for(spanidx=0; spanidx<runtime·mheap.nspan; spanidx++) {
@@ -697,7 +702,7 @@ dumpmemprof(void)
if(sp->kind != KindSpecialProfile)
continue;
spp = (SpecialProfile*)sp;
- p = (byte*)((s->start << PageShift) + spp->offset);
+ p = (byte*)((s->start << PageShift) + spp->special.offset);
dumpint(TagAllocSample);
dumpint((uintptr)p);
dumpint((uintptr)spp->b);
@@ -734,17 +739,18 @@ mdump(G *gp)
flush();
gp->param = nil;
- gp->status = Grunning;
+ runtime·casgstatus(gp, Gwaiting, Grunning);
runtime·gogo(&gp->sched);
}
void
runtime∕debug·WriteHeapDump(uintptr fd)
{
+ void (*fn)(G*);
+
// Stop the world.
runtime·semacquire(&runtime·worldsema, false);
g->m->gcing = 1;
- g->m->locks++;
runtime·stoptheworld();
// Update stats so we can dump them.
@@ -756,9 +762,10 @@ runtime∕debug·WriteHeapDump(uintptr fd)
dumpfd = fd;
// Call dump routine on M stack.
- g->status = Gwaiting;
- g->waitreason = "dumping heap";
- runtime·mcall(mdump);
+ runtime·casgstatus(g, Grunning, Gwaiting);
+ g->waitreason = runtime·gostringnocopy((byte*)"dumping heap");
+ fn = mdump;
+ runtime·mcall(&fn);
// Reset dump file.
dumpfd = 0;
@@ -770,6 +777,7 @@ runtime∕debug·WriteHeapDump(uintptr fd)
// Start up the world again.
g->m->gcing = 0;
+ g->m->locks++;
runtime·semrelease(&runtime·worldsema);
runtime·starttheworld();
g->m->locks--;
@@ -798,13 +806,11 @@ dumpbvtypes(BitVector *bv, byte *base)
if((bv->bytedata[i/8] >> i%8 & 3) != BitsMultiWord)
continue;
switch(bv->bytedata[(i+BitsPerPointer)/8] >> (i+BitsPerPointer)%8 & 3) {
- case BitsString:
+ default:
+ runtime·throw("unexpected garbage collection bits");
case BitsIface:
i += BitsPerPointer;
break;
- case BitsSlice:
- i += 2 * BitsPerPointer;
- break;
case BitsEface:
dumptype(*(Type**)(base + i / BitsPerPointer * PtrSize));
i += BitsPerPointer;
@@ -816,7 +822,8 @@ dumpbvtypes(BitVector *bv, byte *base)
static BitVector
makeheapobjbv(byte *p, uintptr size)
{
- uintptr off, shift, *bitp, bits, nptr, i;
+ uintptr off, nptr, i;
+ byte shift, *bitp, bits;
bool mw;
// Extend the temp buffer if necessary.
@@ -825,7 +832,7 @@ makeheapobjbv(byte *p, uintptr size)
if(tmpbuf != nil)
runtime·SysFree(tmpbuf, tmpbufsize, &mstats.other_sys);
tmpbufsize = nptr*BitsPerPointer/8+1;
- tmpbuf = runtime·SysAlloc(tmpbufsize, &mstats.other_sys);
+ tmpbuf = runtime·sysAlloc(tmpbufsize, &mstats.other_sys);
if(tmpbuf == nil)
runtime·throw("heapdump: out of memory");
}
@@ -834,13 +841,13 @@ makeheapobjbv(byte *p, uintptr size)
mw = false;
for(i = 0; i < nptr; i++) {
off = (uintptr*)(p + i*PtrSize) - (uintptr*)runtime·mheap.arena_start;
- bitp = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1;
- shift = (off % wordsPerBitmapWord) * gcBits;
- bits = (*bitp >> (shift + 2)) & 3;
+ bitp = runtime·mheap.arena_start - off/wordsPerBitmapByte - 1;
+ shift = (off % wordsPerBitmapByte) * gcBits;
+ bits = (*bitp >> (shift + 2)) & BitsMask;
if(!mw && bits == BitsDead)
break; // end of heap object
mw = !mw && bits == BitsMultiWord;
- tmpbuf[i*BitsPerPointer/8] &= ~(3<<((i*BitsPerPointer)%8));
+ tmpbuf[i*BitsPerPointer/8] &= ~(BitsMask<<((i*BitsPerPointer)%8));
tmpbuf[i*BitsPerPointer/8] |= bits<<((i*BitsPerPointer)%8);
}
return (BitVector){i*BitsPerPointer, (byte*)tmpbuf};
diff --git a/src/pkg/runtime/iface.go b/src/pkg/runtime/iface.go
new file mode 100644
index 0000000000..f60b6a79cc
--- /dev/null
+++ b/src/pkg/runtime/iface.go
@@ -0,0 +1,439 @@
+// 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"
+)
+
+const (
+ hashSize = 1009
+)
+
+var (
+ ifaceLock mutex // lock for accessing hash
+ hash [hashSize]*itab
+)
+
+// fInterface is our standard non-empty interface. We use it instead
+// of interface{f()} in function prototypes because gofmt insists on
+// putting lots of newlines in the otherwise concise interface{f()}.
+type fInterface interface {
+ f()
+}
+
+func getitab(inter *interfacetype, typ *_type, canfail bool) *itab {
+ if len(inter.mhdr) == 0 {
+ gothrow("internal error - misuse of itab")
+ }
+
+ // easy case
+ x := typ.x
+ if x == nil {
+ if canfail {
+ return nil
+ }
+ i := (*imethod)(add(unsafe.Pointer(inter), unsafe.Sizeof(interfacetype{})))
+ panic(&TypeAssertionError{"", *typ._string, *inter.typ._string, *i.name})
+ }
+
+ // compiler has provided some good hash codes for us.
+ h := inter.typ.hash
+ h += 17 * typ.hash
+ // TODO(rsc): h += 23 * x.mhash ?
+ h %= hashSize
+
+ // look twice - once without lock, once with.
+ // common case will be no lock contention.
+ var m *itab
+ var locked int
+ for locked = 0; locked < 2; locked++ {
+ if locked != 0 {
+ lock(&ifaceLock)
+ }
+ for m = (*itab)(atomicloadp(unsafe.Pointer(&hash[h]))); m != nil; m = m.link {
+ if m.inter == inter && m._type == typ {
+ if m.bad != 0 {
+ m = nil
+ if !canfail {
+ // this can only happen if the conversion
+ // was already done once using the , ok form
+ // and we have a cached negative result.
+ // the cached result doesn't record which
+ // interface function was missing, so jump
+ // down to the interface check, which will
+ // do more work but give a better error.
+ goto search
+ }
+ }
+ if locked != 0 {
+ unlock(&ifaceLock)
+ }
+ return m
+ }
+ }
+ }
+
+ m = (*itab)(persistentalloc(unsafe.Sizeof(itab{})+uintptr(len(inter.mhdr))*ptrSize, 0, &memstats.other_sys))
+ m.inter = inter
+ m._type = typ
+
+search:
+ // both inter and typ have method sorted by name,
+ // and interface names are unique,
+ // 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)
+ j := 0
+ for k := 0; k < ni; k++ {
+ i := (*imethod)(add(unsafe.Pointer(inter), unsafe.Sizeof(interfacetype{})+uintptr(k)*unsafe.Sizeof(imethod{})))
+ iname := i.name
+ ipkgpath := i.pkgpath
+ itype := i._type
+ for ; j < nt; j++ {
+ t := (*method)(add(unsafe.Pointer(x), unsafe.Sizeof(uncommontype{})+uintptr(j)*unsafe.Sizeof(method{})))
+ if t.mtyp == itype && t.name == iname && t.pkgpath == ipkgpath {
+ if m != nil {
+ *(*unsafe.Pointer)(add(unsafe.Pointer(m), unsafe.Sizeof(itab{})+uintptr(k)*ptrSize)) = t.ifn
+ }
+ goto nextimethod
+ }
+ }
+ // didn't find method
+ if !canfail {
+ if locked != 0 {
+ unlock(&ifaceLock)
+ }
+ panic(&TypeAssertionError{"", *typ._string, *inter.typ._string, *iname})
+ }
+ m.bad = 1
+ break
+ nextimethod:
+ }
+ if locked == 0 {
+ gothrow("invalid itab locking")
+ }
+ m.link = hash[h]
+ atomicstorep(unsafe.Pointer(&hash[h]), unsafe.Pointer(m))
+ unlock(&ifaceLock)
+ if m.bad != 0 {
+ return nil
+ }
+ return m
+}
+
+func typ2Itab(t *_type, inter *interfacetype, cache **itab) *itab {
+ tab := getitab(inter, t, false)
+ atomicstorep(unsafe.Pointer(cache), unsafe.Pointer(tab))
+ return tab
+}
+
+func convT2E(t *_type, elem unsafe.Pointer) (e interface{}) {
+ size := uintptr(t.size)
+ ep := (*eface)(unsafe.Pointer(&e))
+ if isDirectIface(t) {
+ ep._type = t
+ memmove(unsafe.Pointer(&ep.data), elem, size)
+ } else {
+ 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.
+ memmove(x, elem, size)
+ ep._type = t
+ ep.data = x
+ }
+ return
+}
+
+func convT2I(t *_type, inter *interfacetype, cache **itab, elem unsafe.Pointer) (i fInterface) {
+ tab := (*itab)(atomicloadp(unsafe.Pointer(cache)))
+ if tab == nil {
+ tab = getitab(inter, t, false)
+ atomicstorep(unsafe.Pointer(cache), unsafe.Pointer(tab))
+ }
+ size := uintptr(t.size)
+ pi := (*iface)(unsafe.Pointer(&i))
+ if isDirectIface(t) {
+ pi.tab = tab
+ memmove(unsafe.Pointer(&pi.data), elem, size)
+ } else {
+ x := newobject(t)
+ memmove(x, elem, size)
+ pi.tab = tab
+ pi.data = x
+ }
+ return
+}
+
+// TODO: give these routines a pointer to the result area instead of writing
+// extra data in the outargs section. Then we can get rid of go:nosplit.
+//go:nosplit
+func assertI2T(t *_type, i fInterface) (r struct{}) {
+ ip := (*iface)(unsafe.Pointer(&i))
+ tab := ip.tab
+ if tab == nil {
+ panic(&TypeAssertionError{"", "", *t._string, ""})
+ }
+ if tab._type != t {
+ panic(&TypeAssertionError{*tab.inter.typ._string, *tab._type._string, *t._string, ""})
+ }
+ size := uintptr(t.size)
+ if isDirectIface(t) {
+ memmove(unsafe.Pointer(&r), unsafe.Pointer(&ip.data), size)
+ } else {
+ memmove(unsafe.Pointer(&r), ip.data, size)
+ }
+ return
+}
+
+//go:nosplit
+func assertI2T2(t *_type, i fInterface) (r byte) {
+ ip := (*iface)(unsafe.Pointer(&i))
+ size := uintptr(t.size)
+ ok := (*bool)(add(unsafe.Pointer(&r), size))
+ tab := ip.tab
+ if tab == nil || tab._type != t {
+ *ok = false
+ memclr(unsafe.Pointer(&r), size)
+ return
+ }
+ *ok = true
+ if isDirectIface(t) {
+ memmove(unsafe.Pointer(&r), unsafe.Pointer(&ip.data), size)
+ } else {
+ memmove(unsafe.Pointer(&r), ip.data, size)
+ }
+ return
+}
+
+func assertI2TOK(t *_type, i fInterface) bool {
+ ip := (*iface)(unsafe.Pointer(&i))
+ tab := ip.tab
+ return tab != nil && tab._type == t
+}
+
+//go:nosplit
+func assertE2T(t *_type, e interface{}) (r struct{}) {
+ ep := (*eface)(unsafe.Pointer(&e))
+ if ep._type == nil {
+ panic(&TypeAssertionError{"", "", *t._string, ""})
+ }
+ if ep._type != t {
+ panic(&TypeAssertionError{"", *ep._type._string, *t._string, ""})
+ }
+ size := uintptr(t.size)
+ if isDirectIface(t) {
+ memmove(unsafe.Pointer(&r), unsafe.Pointer(&ep.data), size)
+ } else {
+ memmove(unsafe.Pointer(&r), ep.data, size)
+ }
+ return
+}
+
+//go:nosplit
+func assertE2T2(t *_type, e interface{}) (r byte) {
+ ep := (*eface)(unsafe.Pointer(&e))
+ size := uintptr(t.size)
+ ok := (*bool)(add(unsafe.Pointer(&r), size))
+ if ep._type != t {
+ *ok = false
+ memclr(unsafe.Pointer(&r), size)
+ return
+ }
+ *ok = true
+ if isDirectIface(t) {
+ memmove(unsafe.Pointer(&r), unsafe.Pointer(&ep.data), size)
+ } else {
+ memmove(unsafe.Pointer(&r), ep.data, size)
+ }
+ return
+}
+
+func assertE2TOK(t *_type, e interface{}) bool {
+ ep := (*eface)(unsafe.Pointer(&e))
+ return t == ep._type
+}
+
+func convI2E(i fInterface) (r interface{}) {
+ ip := (*iface)(unsafe.Pointer(&i))
+ tab := ip.tab
+ if tab == nil {
+ return
+ }
+ rp := (*eface)(unsafe.Pointer(&r))
+ rp._type = tab._type
+ rp.data = ip.data
+ return
+}
+
+func assertI2E(inter *interfacetype, i fInterface) (r interface{}) {
+ ip := (*iface)(unsafe.Pointer(&i))
+ tab := ip.tab
+ if tab == nil {
+ // explicit conversions require non-nil interface value.
+ panic(&TypeAssertionError{"", "", *inter.typ._string, ""})
+ }
+ rp := (*eface)(unsafe.Pointer(&r))
+ rp._type = tab._type
+ rp.data = ip.data
+ return
+}
+
+func assertI2E2(inter *interfacetype, i fInterface) (r interface{}, ok bool) {
+ ip := (*iface)(unsafe.Pointer(&i))
+ tab := ip.tab
+ if tab == nil {
+ return
+ }
+ rp := (*eface)(unsafe.Pointer(&r))
+ rp._type = tab._type
+ rp.data = ip.data
+ ok = true
+ return
+}
+
+func convI2I(inter *interfacetype, i fInterface) (r fInterface) {
+ ip := (*iface)(unsafe.Pointer(&i))
+ tab := ip.tab
+ if tab == nil {
+ return
+ }
+ rp := (*iface)(unsafe.Pointer(&r))
+ if tab.inter == inter {
+ rp.tab = tab
+ rp.data = ip.data
+ return
+ }
+ rp.tab = getitab(inter, tab._type, false)
+ rp.data = ip.data
+ return
+}
+
+func assertI2I(inter *interfacetype, i fInterface) (r fInterface) {
+ ip := (*iface)(unsafe.Pointer(&i))
+ tab := ip.tab
+ if tab == nil {
+ // explicit conversions require non-nil interface value.
+ panic(&TypeAssertionError{"", "", *inter.typ._string, ""})
+ }
+ rp := (*iface)(unsafe.Pointer(&r))
+ if tab.inter == inter {
+ rp.tab = tab
+ rp.data = ip.data
+ return
+ }
+ rp.tab = getitab(inter, tab._type, false)
+ rp.data = ip.data
+ return
+}
+
+func assertI2I2(inter *interfacetype, i fInterface) (r fInterface, ok bool) {
+ ip := (*iface)(unsafe.Pointer(&i))
+ tab := ip.tab
+ if tab == nil {
+ return
+ }
+ rp := (*iface)(unsafe.Pointer(&r))
+ if tab.inter == inter {
+ rp.tab = tab
+ rp.data = ip.data
+ ok = true
+ return
+ }
+ tab = getitab(inter, tab._type, true)
+ if tab == nil {
+ rp.data = nil
+ rp.tab = nil
+ ok = false
+ return
+ }
+ rp.tab = tab
+ rp.data = ip.data
+ ok = true
+ return
+}
+
+func assertE2I(inter *interfacetype, e interface{}) (r fInterface) {
+ ep := (*eface)(unsafe.Pointer(&e))
+ t := ep._type
+ if t == nil {
+ // explicit conversions require non-nil interface value.
+ panic(&TypeAssertionError{"", "", *inter.typ._string, ""})
+ }
+ rp := (*iface)(unsafe.Pointer(&r))
+ rp.tab = getitab(inter, t, false)
+ rp.data = ep.data
+ return
+}
+
+func assertE2I2(inter *interfacetype, e interface{}) (r fInterface, ok bool) {
+ ep := (*eface)(unsafe.Pointer(&e))
+ t := ep._type
+ if t == nil {
+ return
+ }
+ tab := getitab(inter, t, true)
+ if tab == nil {
+ return
+ }
+ rp := (*iface)(unsafe.Pointer(&r))
+ rp.tab = tab
+ rp.data = ep.data
+ ok = true
+ return
+}
+
+func reflect_ifaceE2I(inter *interfacetype, e interface{}, dst *fInterface) {
+ *dst = assertE2I(inter, e)
+}
+
+func assertE2E(inter *interfacetype, e interface{}) interface{} {
+ ep := (*eface)(unsafe.Pointer(&e))
+ if ep._type == nil {
+ // explicit conversions require non-nil interface value.
+ panic(&TypeAssertionError{"", "", *inter.typ._string, ""})
+ }
+ return e
+}
+
+func assertE2E2(inter *interfacetype, e interface{}) (interface{}, bool) {
+ ep := (*eface)(unsafe.Pointer(&e))
+ if ep._type == nil {
+ return nil, false
+ }
+ return e, true
+}
+
+func ifacethash(i fInterface) uint32 {
+ ip := (*iface)(unsafe.Pointer(&i))
+ tab := ip.tab
+ if tab == nil {
+ return 0
+ }
+ return tab._type.hash
+}
+
+func efacethash(e interface{}) uint32 {
+ ep := (*eface)(unsafe.Pointer(&e))
+ t := ep._type
+ if t == nil {
+ return 0
+ }
+ return t.hash
+}
+
+func iterate_itabs(fn func(*itab)) {
+ for _, h := range &hash {
+ for ; h != nil; h = h.link {
+ fn(h)
+ }
+ }
+}
+
+func ifaceE2I2(inter *interfacetype, e interface{}, r *fInterface) (ok bool) {
+ *r, ok = assertE2I2(inter, e)
+ return
+}
diff --git a/src/pkg/runtime/iface.goc b/src/pkg/runtime/iface.goc
deleted file mode 100644
index ec89746470..0000000000
--- a/src/pkg/runtime/iface.goc
+++ /dev/null
@@ -1,611 +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
-#include "runtime.h"
-#include "arch_GOARCH.h"
-#include "type.h"
-#include "typekind.h"
-#include "malloc.h"
-#include "../../cmd/ld/textflag.h"
-
-static Itab* hash[1009];
-static Lock ifacelock;
-
-static Itab*
-itab(InterfaceType *inter, Type *type, int32 canfail)
-{
- int32 locked;
- int32 ni;
- Method *t, *et;
- IMethod *i, *ei;
- uint32 h;
- String *iname, *ipkgPath;
- Itab *m;
- UncommonType *x;
- Type *itype;
- Eface err;
-
- if(inter->mhdr.len == 0)
- runtime·throw("internal error - misuse of itab");
-
- locked = 0;
-
- // easy case
- x = type->x;
- if(x == nil) {
- if(canfail)
- return nil;
- iname = inter->m[0].name;
- goto throw;
- }
-
- // compiler has provided some good hash codes for us.
- h = inter->hash;
- h += 17 * type->hash;
- // TODO(rsc): h += 23 * x->mhash ?
- h %= nelem(hash);
-
- // look twice - once without lock, once with.
- // common case will be no lock contention.
- for(locked=0; locked<2; locked++) {
- if(locked)
- runtime·lock(&ifacelock);
- for(m=runtime·atomicloadp(&hash[h]); m!=nil; m=m->link) {
- if(m->inter == inter && m->type == type) {
- if(m->bad) {
- m = nil;
- if(!canfail) {
- // this can only happen if the conversion
- // was already done once using the , ok form
- // and we have a cached negative result.
- // the cached result doesn't record which
- // interface function was missing, so jump
- // down to the interface check, which will
- // do more work but give a better error.
- goto search;
- }
- }
- if(locked)
- runtime·unlock(&ifacelock);
- return m;
- }
- }
- }
-
- ni = inter->mhdr.len;
- m = runtime·persistentalloc(sizeof(*m) + ni*sizeof m->fun[0], 0, &mstats.other_sys);
- m->inter = inter;
- m->type = type;
-
-search:
- // both inter and type have method sorted by name,
- // and interface names are unique,
- // so can iterate over both in lock step;
- // the loop is O(ni+nt) not O(ni*nt).
- i = inter->m;
- ei = i + inter->mhdr.len;
- t = x->m;
- et = t + x->mhdr.len;
- for(; i < ei; i++) {
- itype = i->type;
- iname = i->name;
- ipkgPath = i->pkgPath;
- for(;; t++) {
- if(t >= et) {
- if(!canfail) {
- throw:
- // didn't find method
- runtime·newTypeAssertionError(
- nil, type->string, inter->string,
- iname, &err);
- if(locked)
- runtime·unlock(&ifacelock);
- runtime·panic(err);
- return nil; // not reached
- }
- m->bad = 1;
- goto out;
- }
- if(t->mtyp == itype && t->name == iname && t->pkgPath == ipkgPath)
- break;
- }
- if(m)
- m->fun[i - inter->m] = t->ifn;
- }
-
-out:
- if(!locked)
- runtime·panicstring("invalid itab locking");
- m->link = hash[h];
- runtime·atomicstorep(&hash[h], m);
- runtime·unlock(&ifacelock);
- if(m->bad)
- return nil;
- return m;
-}
-
-// call the callback for every itab that is currently allocated.
-void
-runtime·iterate_itabs(void (*callback)(Itab*))
-{
- int32 i;
- Itab *tab;
-
- for(i = 0; i < nelem(hash); i++) {
- for(tab = hash[i]; tab != nil; tab = tab->link) {
- callback(tab);
- }
- }
-}
-
-static void
-copyin(Type *t, void *src, void **dst)
-{
- uintptr size;
- void *p;
- Alg *alg;
-
- size = t->size;
- alg = t->alg;
-
- if(size <= sizeof(*dst))
- alg->copy(size, dst, src);
- else {
- p = runtime·cnew(t);
- alg->copy(size, p, src);
- *dst = p;
- }
-}
-
-static void
-copyout(Type *t, void **src, void *dst)
-{
- uintptr size;
- Alg *alg;
-
- size = t->size;
- alg = t->alg;
-
- if(size <= sizeof(*src))
- alg->copy(size, dst, src);
- else
- alg->copy(size, dst, *src);
-}
-
-#pragma textflag NOSPLIT
-func typ2Itab(t *Type, inter *InterfaceType, cache **Itab) (tab *Itab) {
- tab = itab(inter, t, 0);
- runtime·atomicstorep(cache, tab);
-}
-
-#pragma textflag NOSPLIT
-func convT2I(t *Type, inter *InterfaceType, cache **Itab, elem *byte) (ret Iface) {
- Itab *tab;
-
- tab = runtime·atomicloadp(cache);
- if(!tab) {
- tab = itab(inter, t, 0);
- runtime·atomicstorep(cache, tab);
- }
- ret.tab = tab;
- copyin(t, elem, &ret.data);
-}
-
-#pragma textflag NOSPLIT
-func convT2E(t *Type, elem *byte) (ret Eface) {
- ret.type = t;
- copyin(t, elem, &ret.data);
-}
-
-static void assertI2Tret(Type *t, Iface i, byte *ret);
-
-/*
- * NOTE: Cannot use 'func' here, because we have to declare
- * a return value, the only types we have are at least 1 byte large,
- * goc2c will zero the return value, and the actual return value
- * might have size 0 bytes, in which case the zeroing of the
- * 1 or more bytes would be wrong.
- * Using C lets us control (avoid) the initial zeroing.
- */
-#pragma textflag NOSPLIT
-void
-runtime·assertI2T(Type *t, Iface i, GoOutput retbase)
-{
- assertI2Tret(t, i, (byte*)&retbase);
-}
-
-static void
-assertI2Tret(Type *t, Iface i, byte *ret)
-{
- Itab *tab;
- Eface err;
-
- tab = i.tab;
- if(tab == nil) {
- runtime·newTypeAssertionError(
- nil, nil, t->string,
- nil, &err);
- runtime·panic(err);
- }
- if(tab->type != t) {
- runtime·newTypeAssertionError(
- tab->inter->string, tab->type->string, t->string,
- nil, &err);
- runtime·panic(err);
- }
- copyout(t, &i.data, ret);
-}
-
-#pragma textflag NOSPLIT
-func assertI2T2(t *Type, i Iface) (ret byte, ...) {
- bool *ok;
- int32 wid;
-
- wid = t->size;
- ok = (bool*)(&ret + wid);
-
- if(i.tab == nil || i.tab->type != t) {
- *ok = false;
- runtime·memclr(&ret, wid);
- return;
- }
-
- *ok = true;
- copyout(t, &i.data, &ret);
-}
-
-func assertI2TOK(t *Type, i Iface) (ok bool) {
- ok = i.tab!=nil && i.tab->type==t;
-}
-
-static void assertE2Tret(Type *t, Eface e, byte *ret);
-
-/*
- * NOTE: Cannot use 'func' here. See assertI2T above.
- */
-#pragma textflag NOSPLIT
-void
-runtime·assertE2T(Type *t, Eface e, GoOutput retbase)
-{
- assertE2Tret(t, e, (byte*)&retbase);
-}
-
-static void
-assertE2Tret(Type *t, Eface e, byte *ret)
-{
- Eface err;
-
- if(e.type == nil) {
- runtime·newTypeAssertionError(
- nil, nil, t->string,
- nil, &err);
- runtime·panic(err);
- }
- if(e.type != t) {
- runtime·newTypeAssertionError(
- nil, e.type->string, t->string,
- nil, &err);
- runtime·panic(err);
- }
- copyout(t, &e.data, ret);
-}
-
-#pragma textflag NOSPLIT
-func assertE2T2(t *Type, e Eface) (ret byte, ...) {
- bool *ok;
- int32 wid;
-
- wid = t->size;
- ok = (bool*)(&ret + wid);
-
- if(t != e.type) {
- *ok = false;
- runtime·memclr(&ret, wid);
- return;
- }
-
- *ok = true;
- copyout(t, &e.data, &ret);
-}
-
-func assertE2TOK(t *Type, e Eface) (ok bool) {
- ok = t==e.type;
-}
-
-func convI2E(i Iface) (ret Eface) {
- Itab *tab;
-
- ret.data = i.data;
- if((tab = i.tab) == nil)
- ret.type = nil;
- else
- ret.type = tab->type;
-}
-
-func assertI2E(inter *InterfaceType, i Iface) (ret Eface) {
- Itab *tab;
- Eface err;
-
- tab = i.tab;
- if(tab == nil) {
- // explicit conversions require non-nil interface value.
- runtime·newTypeAssertionError(
- nil, nil, inter->string,
- nil, &err);
- runtime·panic(err);
- }
- ret.data = i.data;
- ret.type = tab->type;
-}
-
-func assertI2E2(inter *InterfaceType, i Iface) (ret Eface, ok bool) {
- Itab *tab;
-
- USED(inter);
- tab = i.tab;
- if(tab == nil) {
- ret.type = nil;
- ok = 0;
- } else {
- ret.type = tab->type;
- ok = 1;
- }
- ret.data = i.data;
-}
-
-func convI2I(inter *InterfaceType, i Iface) (ret Iface) {
- Itab *tab;
-
- ret.data = i.data;
- if((tab = i.tab) == nil)
- ret.tab = nil;
- else if(tab->inter == inter)
- ret.tab = tab;
- else
- ret.tab = itab(inter, tab->type, 0);
-}
-
-void
-runtime·ifaceI2I(InterfaceType *inter, Iface i, Iface *ret)
-{
- Itab *tab;
- Eface err;
-
- tab = i.tab;
- if(tab == nil) {
- // explicit conversions require non-nil interface value.
- runtime·newTypeAssertionError(
- nil, nil, inter->string,
- nil, &err);
- runtime·panic(err);
- }
- ret->data = i.data;
- ret->tab = itab(inter, tab->type, 0);
-}
-
-func assertI2I(inter *InterfaceType, i Iface) (ret Iface) {
- runtime·ifaceI2I(inter, i, &ret);
-}
-
-func assertI2I2(inter *InterfaceType, i Iface) (ret Iface, ok bool) {
- Itab *tab;
-
- tab = i.tab;
- if(tab != nil && (tab->inter == inter || (tab = itab(inter, tab->type, 1)) != nil)) {
- ret.data = i.data;
- ret.tab = tab;
- ok = 1;
- } else {
- ret.data = 0;
- ret.tab = 0;
- ok = 0;
- }
-}
-
-void
-runtime·ifaceE2I(InterfaceType *inter, Eface e, Iface *ret)
-{
- Type *t;
- Eface err;
-
- t = e.type;
- if(t == nil) {
- // explicit conversions require non-nil interface value.
- runtime·newTypeAssertionError(
- nil, nil, inter->string,
- nil, &err);
- runtime·panic(err);
- }
- ret->data = e.data;
- ret->tab = itab(inter, t, 0);
-}
-
-bool
-runtime·ifaceE2I2(InterfaceType *inter, Eface e, Iface *ret)
-{
- ret->tab = itab(inter, e.type, 1);
- if(ret->tab == nil)
- return false;
- ret->data = e.data;
- return true;
-}
-
-func reflect·ifaceE2I(inter *InterfaceType, e Eface, dst *Iface) {
- runtime·ifaceE2I(inter, e, dst);
-}
-
-func assertE2I(inter *InterfaceType, e Eface) (ret Iface) {
- runtime·ifaceE2I(inter, e, &ret);
-}
-
-func assertE2I2(inter *InterfaceType, e Eface) (ret Iface, ok bool) {
- if(e.type == nil) {
- ok = 0;
- ret.data = nil;
- ret.tab = nil;
- } else if((ret.tab = itab(inter, e.type, 1)) == nil) {
- ok = 0;
- ret.data = nil;
- } else {
- ok = 1;
- ret.data = e.data;
- }
-}
-
-func assertE2E(inter *InterfaceType, e Eface) (ret Eface) {
- Type *t;
- Eface err;
-
- t = e.type;
- if(t == nil) {
- // explicit conversions require non-nil interface value.
- runtime·newTypeAssertionError(
- nil, nil, inter->string,
- nil, &err);
- runtime·panic(err);
- }
- ret = e;
-}
-
-func assertE2E2(inter *InterfaceType, e Eface) (ret Eface, ok bool) {
- USED(inter);
- ret = e;
- ok = e.type != nil;
-}
-
-static uintptr
-ifacehash1(void *data, Type *t, uintptr h)
-{
- Alg *alg;
- uintptr size;
- Eface err;
-
- if(t == nil)
- return 0;
-
- alg = t->alg;
- size = t->size;
- if(alg->hash->fn == (void(*)())runtime·nohash) {
- // calling nohash will panic too,
- // but we can print a better error.
- runtime·newErrorString(runtime·catstring(runtime·gostringnocopy((byte*)"hash of unhashable type "), *t->string), &err);
- runtime·panic(err);
- }
- if(size <= sizeof(data))
- return ((uintptr(*)(void**,uintptr,uintptr))alg->hash)(&data, size, h);
- else
- return ((uintptr(*)(void*,uintptr,uintptr))alg->hash)(data, size, h);
-}
-
-uintptr
-runtime·ifacehash(Iface a, uintptr h)
-{
- if(a.tab == nil)
- return h;
- return ifacehash1(a.data, a.tab->type, h);
-}
-
-uintptr
-runtime·efacehash(Eface a, uintptr h)
-{
- return ifacehash1(a.data, a.type, h);
-}
-
-static bool
-ifaceeq1(void *data1, void *data2, Type *t)
-{
- uintptr size;
- Alg *alg;
- Eface err;
- bool eq;
-
- alg = t->alg;
- size = t->size;
-
- if(alg->equal == runtime·noequal) {
- // calling noequal will panic too,
- // but we can print a better error.
- runtime·newErrorString(runtime·catstring(runtime·gostringnocopy((byte*)"comparing uncomparable type "), *t->string), &err);
- runtime·panic(err);
- }
-
- eq = 0;
- if(size <= sizeof(data1))
- alg->equal(&eq, size, &data1, &data2);
- else
- alg->equal(&eq, size, data1, data2);
- return eq;
-}
-
-bool
-runtime·ifaceeq_c(Iface i1, Iface i2)
-{
- if(i1.tab != i2.tab)
- return false;
- if(i1.tab == nil)
- return true;
- return ifaceeq1(i1.data, i2.data, i1.tab->type);
-}
-
-bool
-runtime·efaceeq_c(Eface e1, Eface e2)
-{
- if(e1.type != e2.type)
- return false;
- if(e1.type == nil)
- return true;
- return ifaceeq1(e1.data, e2.data, e1.type);
-}
-
-func ifaceeq(i1 Iface, i2 Iface) (ret bool) {
- ret = runtime·ifaceeq_c(i1, i2);
-}
-
-func efaceeq(e1 Eface, e2 Eface) (ret bool) {
- ret = runtime·efaceeq_c(e1, e2);
-}
-
-func ifacethash(i1 Iface) (ret uint32) {
- Itab *tab;
-
- ret = 0;
- tab = i1.tab;
- if(tab != nil)
- ret = tab->type->hash;
-}
-
-func efacethash(e1 Eface) (ret uint32) {
- Type *t;
-
- ret = 0;
- t = e1.type;
- if(t != nil)
- ret = t->hash;
-}
-
-func reflect·unsafe_Typeof(e Eface) (ret Eface) {
- if(e.type == nil) {
- ret.type = nil;
- ret.data = nil;
- } else {
- ret = *(Eface*)(e.type);
- }
-}
-
-func reflect·unsafe_New(t *Type) (ret *byte) {
- ret = runtime·cnew(t);
-}
-
-func reflect·unsafe_NewArray(t *Type, n int) (ret *byte) {
- ret = runtime·cnewarray(t, n);
-}
-
-func reflect·typelinks() (ret Slice) {
- extern Type *typelink[], *etypelink[];
- static int32 first = 1;
- ret.array = (byte*)typelink;
- ret.len = etypelink - typelink;
- ret.cap = ret.len;
-}
diff --git a/src/pkg/runtime/lfstack.goc b/src/pkg/runtime/lfstack.c
index f7b8effa07..57e0af2829 100644
--- a/src/pkg/runtime/lfstack.goc
+++ b/src/pkg/runtime/lfstack.c
@@ -3,8 +3,8 @@
// license that can be found in the LICENSE file.
// Lock-free stack.
+// The following code runs only on g0 stack.
-package runtime
#include "runtime.h"
#include "arch_GOARCH.h"
@@ -72,10 +72,16 @@ runtime·lfstackpop(uint64 *head)
}
}
-func lfstackpush_go(head *uint64, node *LFNode) {
- runtime·lfstackpush(head, node);
+void
+runtime·lfstackpush_m(void)
+{
+ runtime·lfstackpush(g->m->ptrarg[0], g->m->ptrarg[1]);
+ g->m->ptrarg[0] = nil;
+ g->m->ptrarg[1] = nil;
}
-func lfstackpop_go(head *uint64) (node *LFNode) {
- node = runtime·lfstackpop(head);
+void
+runtime·lfstackpop_m(void)
+{
+ g->m->ptrarg[0] = runtime·lfstackpop(g->m->ptrarg[0]);
}
diff --git a/src/pkg/runtime/lock_futex.c b/src/pkg/runtime/lock_futex.c
deleted file mode 100644
index 7fc2d5547d..0000000000
--- a/src/pkg/runtime/lock_futex.c
+++ /dev/null
@@ -1,201 +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.
-
-// +build dragonfly freebsd linux
-
-#include "runtime.h"
-#include "stack.h"
-#include "../../cmd/ld/textflag.h"
-
-// This implementation depends on OS-specific implementations of
-//
-// runtime·futexsleep(uint32 *addr, uint32 val, int64 ns)
-// Atomically,
-// 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)
-// If any procs are sleeping on addr, wake up at most cnt.
-
-enum
-{
- MUTEX_UNLOCKED = 0,
- MUTEX_LOCKED = 1,
- MUTEX_SLEEPING = 2,
-
- ACTIVE_SPIN = 4,
- ACTIVE_SPIN_CNT = 30,
- PASSIVE_SPIN = 1,
-};
-
-// Possible lock states are MUTEX_UNLOCKED, MUTEX_LOCKED and MUTEX_SLEEPING.
-// MUTEX_SLEEPING means that there is presumably at least one sleeping thread.
-// Note that there can be spinning threads during all states - they do not
-// affect mutex's state.
-void
-runtime·lock(Lock *l)
-{
- uint32 i, v, wait, spin;
-
- if(g->m->locks++ < 0)
- runtime·throw("runtime·lock: lock count");
-
- // Speculative grab for lock.
- v = runtime·xchg((uint32*)&l->key, MUTEX_LOCKED);
- if(v == MUTEX_UNLOCKED)
- return;
-
- // wait is either MUTEX_LOCKED or MUTEX_SLEEPING
- // depending on whether there is a thread sleeping
- // on this mutex. If we ever change l->key from
- // MUTEX_SLEEPING to some other value, we must be
- // careful to change it back to MUTEX_SLEEPING before
- // returning, to ensure that the sleeping thread gets
- // its wakeup call.
- wait = v;
-
- // On uniprocessor's, no point spinning.
- // On multiprocessors, spin for ACTIVE_SPIN attempts.
- spin = 0;
- if(runtime·ncpu > 1)
- spin = ACTIVE_SPIN;
-
- for(;;) {
- // Try for lock, spinning.
- for(i = 0; i < spin; i++) {
- while(l->key == MUTEX_UNLOCKED)
- if(runtime·cas((uint32*)&l->key, MUTEX_UNLOCKED, wait))
- return;
- runtime·procyield(ACTIVE_SPIN_CNT);
- }
-
- // Try for lock, rescheduling.
- for(i=0; i < PASSIVE_SPIN; i++) {
- while(l->key == MUTEX_UNLOCKED)
- if(runtime·cas((uint32*)&l->key, MUTEX_UNLOCKED, wait))
- return;
- runtime·osyield();
- }
-
- // Sleep.
- v = runtime·xchg((uint32*)&l->key, MUTEX_SLEEPING);
- if(v == MUTEX_UNLOCKED)
- return;
- wait = MUTEX_SLEEPING;
- runtime·futexsleep((uint32*)&l->key, MUTEX_SLEEPING, -1);
- }
-}
-
-void
-runtime·unlock(Lock *l)
-{
- uint32 v;
-
- v = runtime·xchg((uint32*)&l->key, MUTEX_UNLOCKED);
- if(v == MUTEX_UNLOCKED)
- runtime·throw("unlock of unlocked lock");
- if(v == MUTEX_SLEEPING)
- runtime·futexwakeup((uint32*)&l->key, 1);
-
- if(--g->m->locks < 0)
- runtime·throw("runtime·unlock: lock count");
- if(g->m->locks == 0 && g->preempt) // restore the preemption request in case we've cleared it in newstack
- g->stackguard0 = StackPreempt;
-}
-
-// One-time notifications.
-void
-runtime·noteclear(Note *n)
-{
- n->key = 0;
-}
-
-void
-runtime·notewakeup(Note *n)
-{
- uint32 old;
-
- old = runtime·xchg((uint32*)&n->key, 1);
- if(old != 0) {
- runtime·printf("notewakeup - double wakeup (%d)\n", old);
- runtime·throw("notewakeup - double wakeup");
- }
- runtime·futexwakeup((uint32*)&n->key, 1);
-}
-
-void
-runtime·notesleep(Note *n)
-{
- if(g != g->m->g0)
- runtime·throw("notesleep not on g0");
- while(runtime·atomicload((uint32*)&n->key) == 0) {
- g->m->blocked = true;
- runtime·futexsleep((uint32*)&n->key, 0, -1);
- g->m->blocked = false;
- }
-}
-
-#pragma textflag NOSPLIT
-static bool
-notetsleep(Note *n, int64 ns, int64 deadline, int64 now)
-{
- // Conceptually, deadline and now are local variables.
- // They are passed as arguments so that the space for them
- // does not count against our nosplit stack sequence.
-
- if(ns < 0) {
- while(runtime·atomicload((uint32*)&n->key) == 0) {
- g->m->blocked = true;
- runtime·futexsleep((uint32*)&n->key, 0, -1);
- g->m->blocked = false;
- }
- return true;
- }
-
- if(runtime·atomicload((uint32*)&n->key) != 0)
- return true;
-
- deadline = runtime·nanotime() + ns;
- for(;;) {
- g->m->blocked = true;
- runtime·futexsleep((uint32*)&n->key, 0, ns);
- g->m->blocked = false;
- if(runtime·atomicload((uint32*)&n->key) != 0)
- break;
- now = runtime·nanotime();
- if(now >= deadline)
- break;
- ns = deadline - now;
- }
- return runtime·atomicload((uint32*)&n->key) != 0;
-}
-
-bool
-runtime·notetsleep(Note *n, int64 ns)
-{
- bool res;
-
- if(g != g->m->g0 && !g->m->gcing)
- runtime·throw("notetsleep not on g0");
-
- res = notetsleep(n, ns, 0, 0);
- return res;
-}
-
-// same as runtime·notetsleep, but called on user g (not g0)
-// calls only nosplit functions between entersyscallblock/exitsyscall
-bool
-runtime·notetsleepg(Note *n, int64 ns)
-{
- bool res;
-
- if(g == g->m->g0)
- runtime·throw("notetsleepg on g0");
-
- runtime·entersyscallblock();
- res = notetsleep(n, ns, 0, 0);
- runtime·exitsyscall();
- return res;
-}
diff --git a/src/pkg/runtime/lock_futex.go b/src/pkg/runtime/lock_futex.go
new file mode 100644
index 0000000000..725962341d
--- /dev/null
+++ b/src/pkg/runtime/lock_futex.go
@@ -0,0 +1,205 @@
+// 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.
+
+// +build dragonfly freebsd linux
+
+package runtime
+
+import "unsafe"
+
+// This implementation depends on OS-specific implementations of
+//
+// runtime·futexsleep(uint32 *addr, uint32 val, int64 ns)
+// Atomically,
+// 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)
+// If any procs are sleeping on addr, wake up at most cnt.
+
+const (
+ mutex_unlocked = 0
+ mutex_locked = 1
+ mutex_sleeping = 2
+
+ active_spin = 4
+ active_spin_cnt = 30
+ passive_spin = 1
+)
+
+// Possible lock states are mutex_unlocked, mutex_locked and mutex_sleeping.
+// mutex_sleeping means that there is presumably at least one sleeping thread.
+// Note that there can be spinning threads during all states - they do not
+// affect mutex's state.
+
+func futexsleep(addr *uint32, val uint32, ns int64)
+func futexwakeup(addr *uint32, cnt uint32)
+
+// We use the uintptr mutex.key and note.key as a uint32.
+func key32(p *uintptr) *uint32 {
+ return (*uint32)(unsafe.Pointer(p))
+}
+
+func lock(l *mutex) {
+ gp := getg()
+
+ if gp.m.locks < 0 {
+ gothrow("runtime·lock: lock count")
+ }
+ gp.m.locks++
+
+ // Speculative grab for lock.
+ v := xchg(key32(&l.key), mutex_locked)
+ if v == mutex_unlocked {
+ return
+ }
+
+ // wait is either MUTEX_LOCKED or MUTEX_SLEEPING
+ // depending on whether there is a thread sleeping
+ // on this mutex. If we ever change l->key from
+ // MUTEX_SLEEPING to some other value, we must be
+ // careful to change it back to MUTEX_SLEEPING before
+ // returning, to ensure that the sleeping thread gets
+ // its wakeup call.
+ wait := v
+
+ // On uniprocessors, no point spinning.
+ // On multiprocessors, spin for ACTIVE_SPIN attempts.
+ spin := 0
+ if ncpu > 1 {
+ spin = active_spin
+ }
+ for {
+ // Try for lock, spinning.
+ for i := 0; i < spin; i++ {
+ for l.key == mutex_unlocked {
+ if cas(key32(&l.key), mutex_unlocked, wait) {
+ return
+ }
+ }
+ procyield(active_spin_cnt)
+ }
+
+ // Try for lock, rescheduling.
+ for i := 0; i < passive_spin; i++ {
+ for l.key == mutex_unlocked {
+ if cas(key32(&l.key), mutex_unlocked, wait) {
+ return
+ }
+ }
+ osyield()
+ }
+
+ // Sleep.
+ v = xchg(key32(&l.key), mutex_sleeping)
+ if v == mutex_unlocked {
+ return
+ }
+ wait = mutex_sleeping
+ futexsleep(key32(&l.key), mutex_sleeping, -1)
+ }
+}
+
+func unlock(l *mutex) {
+ v := xchg(key32(&l.key), mutex_unlocked)
+ if v == mutex_unlocked {
+ gothrow("unlock of unlocked lock")
+ }
+ if v == mutex_sleeping {
+ futexwakeup(key32(&l.key), 1)
+ }
+
+ gp := getg()
+ gp.m.locks--
+ if gp.m.locks < 0 {
+ gothrow("runtime·unlock: lock count")
+ }
+ if gp.m.locks == 0 && gp.preempt { // restore the preemption request in case we've cleared it in newstack
+ gp.stackguard0 = stackPreempt
+ }
+}
+
+// One-time notifications.
+func noteclear(n *note) {
+ n.key = 0
+}
+
+func notewakeup(n *note) {
+ old := xchg(key32(&n.key), 1)
+ if old != 0 {
+ print("notewakeup - double wakeup (", old, ")\n")
+ gothrow("notewakeup - double wakeup")
+ }
+ futexwakeup(key32(&n.key), 1)
+}
+
+func notesleep(n *note) {
+ gp := getg()
+ if gp != gp.m.g0 {
+ gothrow("notesleep not on g0")
+ }
+ for atomicload(key32(&n.key)) == 0 {
+ gp.m.blocked = true
+ futexsleep(key32(&n.key), 0, -1)
+ gp.m.blocked = false
+ }
+}
+
+//go:nosplit
+func notetsleep_internal(n *note, ns int64) bool {
+ gp := getg()
+
+ if ns < 0 {
+ for atomicload(key32(&n.key)) == 0 {
+ gp.m.blocked = true
+ futexsleep(key32(&n.key), 0, -1)
+ gp.m.blocked = false
+ }
+ return true
+ }
+
+ if atomicload(key32(&n.key)) != 0 {
+ return true
+ }
+
+ deadline := nanotime() + ns
+ for {
+ gp.m.blocked = true
+ futexsleep(key32(&n.key), 0, ns)
+ gp.m.blocked = false
+ if atomicload(key32(&n.key)) != 0 {
+ break
+ }
+ now := nanotime()
+ if now >= deadline {
+ break
+ }
+ ns = deadline - now
+ }
+ return atomicload(key32(&n.key)) != 0
+}
+
+func notetsleep(n *note, ns int64) bool {
+ gp := getg()
+ if gp != gp.m.g0 && gp.m.gcing == 0 {
+ gothrow("notetsleep not on g0")
+ }
+
+ return notetsleep_internal(n, ns)
+}
+
+// same as runtime·notetsleep, but called on user g (not g0)
+// calls only nosplit functions between entersyscallblock/exitsyscall
+func notetsleepg(n *note, ns int64) bool {
+ gp := getg()
+ if gp == gp.m.g0 {
+ gothrow("notetsleepg on g0")
+ }
+
+ entersyscallblock()
+ ok := notetsleep_internal(n, ns)
+ exitsyscall()
+ return ok
+}
diff --git a/src/pkg/runtime/lock_sema.c b/src/pkg/runtime/lock_sema.c
deleted file mode 100644
index a4274e6555..0000000000
--- a/src/pkg/runtime/lock_sema.c
+++ /dev/null
@@ -1,266 +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.
-
-// +build darwin nacl netbsd openbsd plan9 solaris windows
-
-#include "runtime.h"
-#include "stack.h"
-#include "../../cmd/ld/textflag.h"
-
-// This implementation depends on OS-specific implementations of
-//
-// uintptr runtime·semacreate(void)
-// Create a semaphore, which will be assigned to m->waitsema.
-// The zero value is treated as absence of any semaphore,
-// so be sure to return a non-zero value.
-//
-// int32 runtime·semasleep(int64 ns)
-// If ns < 0, acquire m->waitsema and return 0.
-// If ns >= 0, try to acquire m->waitsema for at most ns nanoseconds.
-// Return 0 if the semaphore was acquired, -1 if interrupted or timed out.
-//
-// int32 runtime·semawakeup(M *mp)
-// Wake up mp, which is or will soon be sleeping on mp->waitsema.
-//
-
-enum
-{
- LOCKED = 1,
-
- ACTIVE_SPIN = 4,
- ACTIVE_SPIN_CNT = 30,
- PASSIVE_SPIN = 1,
-};
-
-void
-runtime·lock(Lock *l)
-{
- uintptr v;
- uint32 i, spin;
-
- if(g->m->locks++ < 0)
- runtime·throw("runtime·lock: lock count");
-
- // Speculative grab for lock.
- if(runtime·casp((void**)&l->key, nil, (void*)LOCKED))
- return;
-
- if(g->m->waitsema == 0)
- g->m->waitsema = runtime·semacreate();
-
- // On uniprocessor's, no point spinning.
- // On multiprocessors, spin for ACTIVE_SPIN attempts.
- spin = 0;
- if(runtime·ncpu > 1)
- spin = ACTIVE_SPIN;
-
- for(i=0;; i++) {
- v = (uintptr)runtime·atomicloadp((void**)&l->key);
- if((v&LOCKED) == 0) {
-unlocked:
- if(runtime·casp((void**)&l->key, (void*)v, (void*)(v|LOCKED)))
- return;
- i = 0;
- }
- if(i<spin)
- runtime·procyield(ACTIVE_SPIN_CNT);
- else if(i<spin+PASSIVE_SPIN)
- runtime·osyield();
- else {
- // Someone else has it.
- // l->waitm points to a linked list of M's waiting
- // for this lock, chained through m->nextwaitm.
- // Queue this M.
- for(;;) {
- g->m->nextwaitm = (void*)(v&~LOCKED);
- if(runtime·casp((void**)&l->key, (void*)v, (void*)((uintptr)g->m|LOCKED)))
- break;
- v = (uintptr)runtime·atomicloadp((void**)&l->key);
- if((v&LOCKED) == 0)
- goto unlocked;
- }
- if(v&LOCKED) {
- // Queued. Wait.
- runtime·semasleep(-1);
- i = 0;
- }
- }
- }
-}
-
-void
-runtime·unlock(Lock *l)
-{
- uintptr v;
- M *mp;
-
- for(;;) {
- v = (uintptr)runtime·atomicloadp((void**)&l->key);
- if(v == LOCKED) {
- if(runtime·casp((void**)&l->key, (void*)LOCKED, nil))
- break;
- } else {
- // Other M's are waiting for the lock.
- // Dequeue an M.
- mp = (void*)(v&~LOCKED);
- if(runtime·casp((void**)&l->key, (void*)v, mp->nextwaitm)) {
- // Dequeued an M. Wake it.
- runtime·semawakeup(mp);
- break;
- }
- }
- }
-
- if(--g->m->locks < 0)
- runtime·throw("runtime·unlock: lock count");
- if(g->m->locks == 0 && g->preempt) // restore the preemption request in case we've cleared it in newstack
- g->stackguard0 = StackPreempt;
-}
-
-// One-time notifications.
-void
-runtime·noteclear(Note *n)
-{
- n->key = 0;
-}
-
-void
-runtime·notewakeup(Note *n)
-{
- M *mp;
-
- do
- mp = runtime·atomicloadp((void**)&n->key);
- while(!runtime·casp((void**)&n->key, mp, (void*)LOCKED));
-
- // Successfully set waitm to LOCKED.
- // What was it before?
- if(mp == nil) {
- // Nothing was waiting. Done.
- } else if(mp == (M*)LOCKED) {
- // Two notewakeups! Not allowed.
- runtime·throw("notewakeup - double wakeup");
- } else {
- // Must be the waiting m. Wake it up.
- runtime·semawakeup(mp);
- }
-}
-
-void
-runtime·notesleep(Note *n)
-{
- if(g != g->m->g0)
- runtime·throw("notesleep not on g0");
-
- if(g->m->waitsema == 0)
- g->m->waitsema = runtime·semacreate();
- if(!runtime·casp((void**)&n->key, nil, g->m)) { // must be LOCKED (got wakeup)
- if(n->key != LOCKED)
- runtime·throw("notesleep - waitm out of sync");
- return;
- }
- // Queued. Sleep.
- g->m->blocked = true;
- runtime·semasleep(-1);
- g->m->blocked = false;
-}
-
-#pragma textflag NOSPLIT
-static bool
-notetsleep(Note *n, int64 ns, int64 deadline, M *mp)
-{
- // Conceptually, deadline and mp are local variables.
- // They are passed as arguments so that the space for them
- // does not count against our nosplit stack sequence.
-
- // Register for wakeup on n->waitm.
- if(!runtime·casp((void**)&n->key, nil, g->m)) { // must be LOCKED (got wakeup already)
- if(n->key != LOCKED)
- runtime·throw("notetsleep - waitm out of sync");
- return true;
- }
-
- if(ns < 0) {
- // Queued. Sleep.
- g->m->blocked = true;
- runtime·semasleep(-1);
- g->m->blocked = false;
- return true;
- }
-
- deadline = runtime·nanotime() + ns;
- for(;;) {
- // Registered. Sleep.
- g->m->blocked = true;
- if(runtime·semasleep(ns) >= 0) {
- g->m->blocked = false;
- // Acquired semaphore, semawakeup unregistered us.
- // Done.
- return true;
- }
- g->m->blocked = false;
-
- // Interrupted or timed out. Still registered. Semaphore not acquired.
- ns = deadline - runtime·nanotime();
- if(ns <= 0)
- break;
- // Deadline hasn't arrived. Keep sleeping.
- }
-
- // Deadline arrived. Still registered. Semaphore not acquired.
- // Want to give up and return, but have to unregister first,
- // so that any notewakeup racing with the return does not
- // try to grant us the semaphore when we don't expect it.
- for(;;) {
- mp = runtime·atomicloadp((void**)&n->key);
- if(mp == g->m) {
- // No wakeup yet; unregister if possible.
- if(runtime·casp((void**)&n->key, mp, nil))
- return false;
- } else if(mp == (M*)LOCKED) {
- // Wakeup happened so semaphore is available.
- // Grab it to avoid getting out of sync.
- g->m->blocked = true;
- if(runtime·semasleep(-1) < 0)
- runtime·throw("runtime: unable to acquire - semaphore out of sync");
- g->m->blocked = false;
- return true;
- } else
- runtime·throw("runtime: unexpected waitm - semaphore out of sync");
- }
-}
-
-bool
-runtime·notetsleep(Note *n, int64 ns)
-{
- bool res;
-
- if(g != g->m->g0 && !g->m->gcing)
- runtime·throw("notetsleep not on g0");
-
- if(g->m->waitsema == 0)
- g->m->waitsema = runtime·semacreate();
-
- res = notetsleep(n, ns, 0, nil);
- return res;
-}
-
-// same as runtime·notetsleep, but called on user g (not g0)
-// calls only nosplit functions between entersyscallblock/exitsyscall
-bool
-runtime·notetsleepg(Note *n, int64 ns)
-{
- bool res;
-
- if(g == g->m->g0)
- runtime·throw("notetsleepg on g0");
-
- if(g->m->waitsema == 0)
- g->m->waitsema = runtime·semacreate();
-
- runtime·entersyscallblock();
- res = notetsleep(n, ns, 0, nil);
- runtime·exitsyscall();
- return res;
-}
diff --git a/src/pkg/runtime/lock_sema.go b/src/pkg/runtime/lock_sema.go
new file mode 100644
index 0000000000..d136b82806
--- /dev/null
+++ b/src/pkg/runtime/lock_sema.go
@@ -0,0 +1,270 @@
+// 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.
+
+// +build darwin nacl netbsd openbsd plan9 solaris windows
+
+package runtime
+
+import "unsafe"
+
+// This implementation depends on OS-specific implementations of
+//
+// uintptr runtime·semacreate(void)
+// Create a semaphore, which will be assigned to m->waitsema.
+// The zero value is treated as absence of any semaphore,
+// so be sure to return a non-zero value.
+//
+// int32 runtime·semasleep(int64 ns)
+// If ns < 0, acquire m->waitsema and return 0.
+// If ns >= 0, try to acquire m->waitsema for at most ns nanoseconds.
+// Return 0 if the semaphore was acquired, -1 if interrupted or timed out.
+//
+// int32 runtime·semawakeup(M *mp)
+// Wake up mp, which is or will soon be sleeping on mp->waitsema.
+//
+const (
+ locked uintptr = 1
+
+ active_spin = 4
+ active_spin_cnt = 30
+ passive_spin = 1
+)
+
+func semacreate() uintptr
+func semasleep(int64) int32
+func semawakeup(mp *m)
+
+func lock(l *mutex) {
+ gp := getg()
+ if gp.m.locks < 0 {
+ gothrow("runtime·lock: lock count")
+ }
+ gp.m.locks++
+
+ // Speculative grab for lock.
+ if casuintptr(&l.key, 0, locked) {
+ return
+ }
+ if gp.m.waitsema == 0 {
+ gp.m.waitsema = semacreate()
+ }
+
+ // On uniprocessor's, no point spinning.
+ // On multiprocessors, spin for ACTIVE_SPIN attempts.
+ spin := 0
+ if ncpu > 1 {
+ spin = active_spin
+ }
+Loop:
+ for i := 0; ; i++ {
+ v := atomicloaduintptr(&l.key)
+ if v&locked == 0 {
+ // Unlocked. Try to lock.
+ if casuintptr(&l.key, v, v|locked) {
+ return
+ }
+ i = 0
+ }
+ if i < spin {
+ procyield(active_spin_cnt)
+ } else if i < spin+passive_spin {
+ osyield()
+ } else {
+ // Someone else has it.
+ // l->waitm points to a linked list of M's waiting
+ // for this lock, chained through m->nextwaitm.
+ // Queue this M.
+ for {
+ gp.m.nextwaitm = (*m)((unsafe.Pointer)(v &^ locked))
+ if casuintptr(&l.key, v, uintptr(unsafe.Pointer(gp.m))|locked) {
+ break
+ }
+ v = atomicloaduintptr(&l.key)
+ if v&locked == 0 {
+ continue Loop
+ }
+ }
+ if v&locked != 0 {
+ // Queued. Wait.
+ semasleep(-1)
+ i = 0
+ }
+ }
+ }
+}
+
+func unlock(l *mutex) {
+ gp := getg()
+ var mp *m
+ for {
+ v := atomicloaduintptr(&l.key)
+ if v == locked {
+ if casuintptr(&l.key, locked, 0) {
+ break
+ }
+ } else {
+ // Other M's are waiting for the lock.
+ // Dequeue an M.
+ mp = (*m)((unsafe.Pointer)(v &^ locked))
+ if casuintptr(&l.key, v, uintptr(unsafe.Pointer(mp.nextwaitm))) {
+ // Dequeued an M. Wake it.
+ semawakeup(mp)
+ break
+ }
+ }
+ }
+ gp.m.locks--
+ if gp.m.locks < 0 {
+ gothrow("runtime·unlock: lock count")
+ }
+ if gp.m.locks == 0 && gp.preempt { // restore the preemption request in case we've cleared it in newstack
+ gp.stackguard0 = stackPreempt
+ }
+}
+
+// One-time notifications.
+func noteclear(n *note) {
+ n.key = 0
+}
+
+func notewakeup(n *note) {
+ var v uintptr
+ for {
+ v = atomicloaduintptr(&n.key)
+ if casuintptr(&n.key, v, locked) {
+ break
+ }
+ }
+
+ // Successfully set waitm to locked.
+ // What was it before?
+ switch {
+ case v == 0:
+ // Nothing was waiting. Done.
+ case v == locked:
+ // Two notewakeups! Not allowed.
+ gothrow("notewakeup - double wakeup")
+ default:
+ // Must be the waiting m. Wake it up.
+ semawakeup((*m)(unsafe.Pointer(v)))
+ }
+}
+
+func notesleep(n *note) {
+ gp := getg()
+ if gp != gp.m.g0 {
+ gothrow("notesleep not on g0")
+ }
+ if gp.m.waitsema == 0 {
+ gp.m.waitsema = semacreate()
+ }
+ if !casuintptr(&n.key, 0, uintptr(unsafe.Pointer(gp.m))) {
+ // Must be locked (got wakeup).
+ if n.key != locked {
+ gothrow("notesleep - waitm out of sync")
+ }
+ return
+ }
+ // Queued. Sleep.
+ gp.m.blocked = true
+ semasleep(-1)
+ gp.m.blocked = false
+}
+
+//go:nosplit
+func notetsleep_internal(n *note, ns int64, gp *g, deadline int64) bool {
+ // gp and deadline are logically local variables, but they are written
+ // as parameters so that the stack space they require is charged
+ // to the caller.
+ // This reduces the nosplit footprint of notetsleep_internal.
+ gp = getg()
+
+ // Register for wakeup on n->waitm.
+ if !casuintptr(&n.key, 0, uintptr(unsafe.Pointer(gp.m))) {
+ // Must be locked (got wakeup).
+ if n.key != locked {
+ gothrow("notetsleep - waitm out of sync")
+ }
+ return true
+ }
+ if ns < 0 {
+ // Queued. Sleep.
+ gp.m.blocked = true
+ semasleep(-1)
+ gp.m.blocked = false
+ return true
+ }
+
+ deadline = nanotime() + ns
+ for {
+ // Registered. Sleep.
+ gp.m.blocked = true
+ if semasleep(ns) >= 0 {
+ gp.m.blocked = false
+ // Acquired semaphore, semawakeup unregistered us.
+ // Done.
+ return true
+ }
+ gp.m.blocked = false
+ // Interrupted or timed out. Still registered. Semaphore not acquired.
+ ns = deadline - nanotime()
+ if ns <= 0 {
+ break
+ }
+ // Deadline hasn't arrived. Keep sleeping.
+ }
+
+ // Deadline arrived. Still registered. Semaphore not acquired.
+ // Want to give up and return, but have to unregister first,
+ // so that any notewakeup racing with the return does not
+ // try to grant us the semaphore when we don't expect it.
+ for {
+ v := atomicloaduintptr(&n.key)
+ switch v {
+ case uintptr(unsafe.Pointer(gp.m)):
+ // No wakeup yet; unregister if possible.
+ if casuintptr(&n.key, v, 0) {
+ return false
+ }
+ case locked:
+ // Wakeup happened so semaphore is available.
+ // Grab it to avoid getting out of sync.
+ gp.m.blocked = true
+ if semasleep(-1) < 0 {
+ gothrow("runtime: unable to acquire - semaphore out of sync")
+ }
+ gp.m.blocked = false
+ return true
+ default:
+ gothrow("runtime: unexpected waitm - semaphore out of sync")
+ }
+ }
+}
+
+func notetsleep(n *note, ns int64) bool {
+ gp := getg()
+ if gp != gp.m.g0 && gp.m.gcing == 0 {
+ gothrow("notetsleep not on g0")
+ }
+ if gp.m.waitsema == 0 {
+ gp.m.waitsema = semacreate()
+ }
+ return notetsleep_internal(n, ns, nil, 0)
+}
+
+// same as runtime·notetsleep, but called on user g (not g0)
+// calls only nosplit functions between entersyscallblock/exitsyscall
+func notetsleepg(n *note, ns int64) bool {
+ gp := getg()
+ if gp == gp.m.g0 {
+ gothrow("notetsleepg on g0")
+ }
+ if gp.m.waitsema == 0 {
+ gp.m.waitsema = semacreate()
+ }
+ entersyscallblock()
+ ok := notetsleep_internal(n, ns, nil, 0)
+ exitsyscall()
+ return ok
+}
diff --git a/src/pkg/runtime/malloc.c b/src/pkg/runtime/malloc.c
index 1e133168b1..91224a38f3 100644
--- a/src/pkg/runtime/malloc.c
+++ b/src/pkg/runtime/malloc.c
@@ -13,7 +13,7 @@
#include "typekind.h"
#include "race.h"
#include "stack.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// Mark mheap as 'no pointers', it does not contain interesting pointers but occupies ~45K.
#pragma dataflag NOPTR
@@ -21,7 +21,10 @@ MHeap runtime·mheap;
#pragma dataflag NOPTR
MStats runtime·memstats;
+Type* runtime·conservative;
+
void runtime·cmallocgc(uintptr size, Type *typ, intgo flag, void **ret);
+void runtime·gc_notype_ptr(Eface*);
void*
runtime·mallocgc(uintptr size, Type *typ, uint32 flag)
@@ -31,16 +34,12 @@ runtime·mallocgc(uintptr size, Type *typ, uint32 flag)
// Call into the Go version of mallocgc.
// TODO: maybe someday we can get rid of this. It is
// probably the only location where we run Go code on the M stack.
+ if((flag&FlagNoScan) == 0 && typ == nil)
+ typ = runtime·conservative;
runtime·cmallocgc(size, typ, flag, &ret);
return ret;
}
-void*
-runtime·malloc(uintptr size)
-{
- return runtime·mallocgc(size, nil, FlagNoInvokeGC);
-}
-
int32
runtime·mlookup(void *v, byte **base, uintptr *size, MSpan **sp)
{
@@ -51,9 +50,9 @@ runtime·mlookup(void *v, byte **base, uintptr *size, MSpan **sp)
g->m->mcache->local_nlookup++;
if (sizeof(void*) == 4 && g->m->mcache->local_nlookup >= (1<<30)) {
// purge cache stats to prevent overflow
- runtime·lock(&runtime·mheap);
+ runtime·lock(&runtime·mheap.lock);
runtime·purgecachedstats(g->m->mcache);
- runtime·unlock(&runtime·mheap);
+ runtime·unlock(&runtime·mheap.lock);
}
s = runtime·MHeap_LookupMaybe(&runtime·mheap, v);
@@ -88,6 +87,7 @@ runtime·mlookup(void *v, byte **base, uintptr *size, MSpan **sp)
return 1;
}
+#pragma textflag NOSPLIT
void
runtime·purgecachedstats(MCache *c)
{
@@ -126,10 +126,11 @@ runtime·mallocinit(void)
{
byte *p, *p1;
uintptr arena_size, bitmap_size, spans_size, p_size;
- extern byte end[];
+ extern byte runtime·end[];
uintptr limit;
uint64 i;
bool reserved;
+ Eface notype_eface;
p = nil;
p_size = 0;
@@ -232,7 +233,7 @@ runtime·mallocinit(void)
// So adjust it upward a little bit ourselves: 1/4 MB to get
// away from the running binary image and then round up
// to a MB boundary.
- p = (byte*)ROUND((uintptr)end + (1<<18), 1<<20);
+ p = (byte*)ROUND((uintptr)runtime·end + (1<<18), 1<<20);
p_size = bitmap_size + spans_size + arena_size + PageSize;
p = runtime·SysReserve(p, p_size, &reserved);
if(p == nil)
@@ -257,6 +258,9 @@ runtime·mallocinit(void)
// Initialize the rest of the allocator.
runtime·MHeap_Init(&runtime·mheap);
g->m->mcache = runtime·allocmcache();
+
+ runtime·gc_notype_ptr(&notype_eface);
+ runtime·conservative = notype_eface.type;
}
void*
@@ -317,7 +321,7 @@ runtime·MHeap_SysAlloc(MHeap *h, uintptr n)
// try to get memory at a location chosen by the OS
// and hope that it is in the range we allocated bitmap for.
p_size = ROUND(n, PageSize) + PageSize;
- p = runtime·SysAlloc(p_size, &mstats.heap_sys);
+ p = runtime·sysAlloc(p_size, &mstats.heap_sys);
if(p == nil)
return nil;
@@ -345,66 +349,8 @@ runtime·MHeap_SysAlloc(MHeap *h, uintptr n)
return p;
}
-static struct
-{
- Lock;
- byte* pos;
- byte* end;
-} persistent;
-
-enum
-{
- PersistentAllocChunk = 256<<10,
- PersistentAllocMaxBlock = 64<<10, // VM reservation granularity is 64K on windows
-};
-
-// Wrapper around SysAlloc that can allocate small chunks.
-// There is no associated free operation.
-// Intended for things like function/type/debug-related persistent data.
-// If align is 0, uses default align (currently 8).
-void*
-runtime·persistentalloc(uintptr size, uintptr align, uint64 *stat)
-{
- byte *p;
-
- if(align != 0) {
- if(align&(align-1))
- runtime·throw("persistentalloc: align is not a power of 2");
- if(align > PageSize)
- runtime·throw("persistentalloc: align is too large");
- } else
- align = 8;
- if(size >= PersistentAllocMaxBlock)
- return runtime·SysAlloc(size, stat);
- runtime·lock(&persistent);
- persistent.pos = (byte*)ROUND((uintptr)persistent.pos, align);
- if(persistent.pos + size > persistent.end) {
- persistent.pos = runtime·SysAlloc(PersistentAllocChunk, &mstats.other_sys);
- if(persistent.pos == nil) {
- runtime·unlock(&persistent);
- runtime·throw("runtime: cannot allocate memory");
- }
- persistent.end = persistent.pos + PersistentAllocChunk;
- }
- p = persistent.pos;
- persistent.pos += size;
- runtime·unlock(&persistent);
- if(stat != &mstats.other_sys) {
- // reaccount the allocation against provided stat
- runtime·xadd64(stat, size);
- runtime·xadd64(&mstats.other_sys, -(uint64)size);
- }
- return p;
-}
-
// Runtime stubs.
-void*
-runtime·mal(uintptr n)
-{
- return runtime·mallocgc(n, nil, 0);
-}
-
static void*
cnew(Type *typ, intgo n)
{
@@ -426,118 +372,47 @@ runtime·cnewarray(Type *typ, intgo n)
return cnew(typ, n);
}
-static void
-setFinalizer(Eface obj, Eface finalizer)
+void
+runtime·setFinalizer_m(void)
{
- byte *base;
- uintptr size;
- FuncType *ft;
- int32 i;
+ FuncVal *fn;
+ void *arg;
uintptr nret;
- Type *t;
Type *fint;
PtrType *ot;
- Iface iface;
- if(obj.type == nil) {
- runtime·printf("runtime.SetFinalizer: first argument is nil interface\n");
- goto throw;
- }
- if((obj.type->kind&KindMask) != KindPtr) {
- runtime·printf("runtime.SetFinalizer: first argument is %S, not pointer\n", *obj.type->string);
- goto throw;
- }
- ot = (PtrType*)obj.type;
- // As an implementation detail we do not run finalizers for zero-sized objects,
- // because we use &runtime·zerobase for all such allocations.
- if(ot->elem != nil && ot->elem->size == 0)
- return;
- // The following check is required for cases when a user passes a pointer to composite literal,
- // but compiler makes it a pointer to global. For example:
- // var Foo = &Object{}
- // func main() {
- // runtime.SetFinalizer(Foo, nil)
- // }
- // See issue 7656.
- if((byte*)obj.data < runtime·mheap.arena_start || runtime·mheap.arena_used <= (byte*)obj.data)
- return;
- if(!runtime·mlookup(obj.data, &base, &size, nil) || obj.data != base) {
- // As an implementation detail we allow to set finalizers for an inner byte
- // of an object if it could come from tiny alloc (see mallocgc for details).
- if(ot->elem == nil || (ot->elem->kind&KindNoPointers) == 0 || ot->elem->size >= TinySize) {
- runtime·printf("runtime.SetFinalizer: pointer not at beginning of allocated block (%p)\n", obj.data);
- goto throw;
- }
- }
- if(finalizer.type != nil) {
- runtime·createfing();
- if(finalizer.type->kind != KindFunc)
- goto badfunc;
- ft = (FuncType*)finalizer.type;
- if(ft->dotdotdot || ft->in.len != 1)
- goto badfunc;
- fint = *(Type**)ft->in.array;
- if(fint == obj.type) {
- // ok - same type
- } else if(fint->kind == KindPtr && (fint->x == nil || fint->x->name == nil || obj.type->x == nil || obj.type->x->name == nil) && ((PtrType*)fint)->elem == ((PtrType*)obj.type)->elem) {
- // ok - not same type, but both pointers,
- // one or the other is unnamed, and same element type, so assignable.
- } else if(fint->kind == KindInterface && ((InterfaceType*)fint)->mhdr.len == 0) {
- // ok - satisfies empty interface
- } else if(fint->kind == KindInterface && runtime·ifaceE2I2((InterfaceType*)fint, obj, &iface)) {
- // ok - satisfies non-empty interface
- } else
- goto badfunc;
-
- // compute size needed for return parameters
- nret = 0;
- for(i=0; i<ft->out.len; i++) {
- t = ((Type**)ft->out.array)[i];
- nret = ROUND(nret, t->align) + t->size;
- }
- nret = ROUND(nret, sizeof(void*));
- ot = (PtrType*)obj.type;
- if(!runtime·addfinalizer(obj.data, finalizer.data, nret, fint, ot)) {
- runtime·printf("runtime.SetFinalizer: finalizer already set\n");
- goto throw;
- }
- } else {
- // NOTE: asking to remove a finalizer when there currently isn't one set is OK.
- runtime·removefinalizer(obj.data);
- }
- return;
+ fn = g->m->ptrarg[0];
+ arg = g->m->ptrarg[1];
+ nret = g->m->scalararg[0];
+ fint = g->m->ptrarg[2];
+ ot = g->m->ptrarg[3];
+ g->m->ptrarg[0] = nil;
+ g->m->ptrarg[1] = nil;
+ g->m->ptrarg[2] = nil;
+ g->m->ptrarg[3] = nil;
-badfunc:
- runtime·printf("runtime.SetFinalizer: cannot pass %S to finalizer %S\n", *obj.type->string, *finalizer.type->string);
-throw:
- runtime·throw("runtime.SetFinalizer");
+ g->m->scalararg[0] = runtime·addfinalizer(arg, fn, nret, fint, ot);
}
void
-runtime·setFinalizer(void)
+runtime·removeFinalizer_m(void)
{
- Eface obj, finalizer;
+ void *p;
- obj.type = g->m->ptrarg[0];
- obj.data = g->m->ptrarg[1];
- finalizer.type = g->m->ptrarg[2];
- finalizer.data = g->m->ptrarg[3];
+ p = g->m->ptrarg[0];
g->m->ptrarg[0] = nil;
- g->m->ptrarg[1] = nil;
- g->m->ptrarg[2] = nil;
- g->m->ptrarg[3] = nil;
- setFinalizer(obj, finalizer);
+ runtime·removefinalizer(p);
}
// mcallable cache refill
void
-runtime·mcacheRefill(void)
+runtime·mcacheRefill_m(void)
{
runtime·MCache_Refill(g->m->mcache, (int32)g->m->scalararg[0]);
}
void
-runtime·largeAlloc(void)
+runtime·largeAlloc_m(void)
{
uintptr npages, size;
MSpan *s;
diff --git a/src/pkg/runtime/malloc.go b/src/pkg/runtime/malloc.go
index df030794b5..ea3a467b8f 100644
--- a/src/pkg/runtime/malloc.go
+++ b/src/pkg/runtime/malloc.go
@@ -7,19 +7,10 @@ package runtime
import "unsafe"
const (
- flagNoScan = 1 << 0 // GC doesn't have to scan object
- flagNoProfiling = 1 << 1 // must not profile
- flagNoZero = 1 << 3 // don't zero memory
- flagNoInvokeGC = 1 << 4 // don't invoke GC
-
- kindArray = 17
- kindFunc = 19
- kindInterface = 20
- kindPtr = 22
- kindStruct = 25
- kindMask = 1<<6 - 1
- kindGCProg = 1 << 6
- kindNoPointers = 1 << 7
+ debugMalloc = false
+
+ flagNoScan = 1 << 0 // GC doesn't have to scan object
+ flagNoZero = 1 << 1 // don't zero memory
maxTinySize = 16
tinySizeClass = 2
@@ -28,8 +19,25 @@ const (
pageShift = 13
pageSize = 1 << pageShift
pageMask = pageSize - 1
+
+ bitsPerPointer = 2
+ bitsMask = 1<<bitsPerPointer - 1
+ pointersPerByte = 8 / bitsPerPointer
+ bitPtrMask = bitsMask << 2
+ maxGCMask = 64
+ bitsDead = 0
+ bitsPointer = 2
+
+ bitBoundary = 1
+ bitMarked = 2
+ bitMask = bitBoundary | bitMarked
+
+ mSpanInUse = 0
)
+// Page number (address>>pageShift)
+type pageID uintptr
+
// All zero-sized allocations return a pointer to this byte.
var zeroObject byte
@@ -43,14 +51,25 @@ func gomallocgc(size uintptr, typ *_type, flags int) unsafe.Pointer {
if size == 0 {
return unsafe.Pointer(&zeroObject)
}
- mp := acquirem()
- if mp.mallocing != 0 {
- gothrow("malloc/free - deadlock")
- }
- mp.mallocing = 1
size0 := size
- c := mp.mcache
+ // This function must be atomic wrt GC, but for performance reasons
+ // we don't acquirem/releasem on fast path. The code below does not have
+ // split stack checks, so it can't be preempted by GC.
+ // Functions like roundup/add are inlined. And onM/racemalloc are nosplit.
+ // If debugMalloc = true, these assumptions are checked below.
+ if debugMalloc {
+ mp := acquirem()
+ if mp.mallocing != 0 {
+ gothrow("malloc deadlock")
+ }
+ mp.mallocing = 1
+ if mp.curg != nil {
+ mp.curg.stackguard0 = ^uintptr(0xfff) | 0xbad
+ }
+ }
+
+ c := gomcache()
var s *mspan
var x unsafe.Pointer
if size <= maxSmallSize {
@@ -101,9 +120,19 @@ func gomallocgc(size uintptr, typ *_type, flags int) unsafe.Pointer {
// The object fits into existing tiny block.
x = tiny
c.tiny = (*byte)(add(x, size))
- c.tinysize -= uint(size1)
- mp.mallocing = 0
- releasem(mp)
+ c.tinysize -= uintptr(size1)
+ if debugMalloc {
+ mp := acquirem()
+ if mp.mallocing == 0 {
+ gothrow("bad malloc")
+ }
+ mp.mallocing = 0
+ if mp.curg != nil {
+ mp.curg.stackguard0 = mp.curg.stackguard
+ }
+ releasem(mp)
+ releasem(mp)
+ }
return x
}
}
@@ -111,8 +140,10 @@ func gomallocgc(size uintptr, typ *_type, flags int) unsafe.Pointer {
s = c.alloc[tinySizeClass]
v := s.freelist
if v == nil {
+ mp := acquirem()
mp.scalararg[0] = tinySizeClass
- onM(&mcacheRefill)
+ onM(mcacheRefill_m)
+ releasem(mp)
s = c.alloc[tinySizeClass]
v = s.freelist
}
@@ -126,7 +157,7 @@ func gomallocgc(size uintptr, typ *_type, flags int) unsafe.Pointer {
// based on amount of remaining free space.
if maxTinySize-size > tinysize {
c.tiny = (*byte)(add(x, size))
- c.tinysize = uint(maxTinySize - size)
+ c.tinysize = uintptr(maxTinySize - size)
}
size = maxTinySize
} else {
@@ -140,8 +171,10 @@ func gomallocgc(size uintptr, typ *_type, flags int) unsafe.Pointer {
s = c.alloc[sizeclass]
v := s.freelist
if v == nil {
- mp.scalararg[0] = uint(sizeclass)
- onM(&mcacheRefill)
+ mp := acquirem()
+ mp.scalararg[0] = uintptr(sizeclass)
+ onM(mcacheRefill_m)
+ releasem(mp)
s = c.alloc[sizeclass]
v = s.freelist
}
@@ -156,47 +189,143 @@ func gomallocgc(size uintptr, typ *_type, flags int) unsafe.Pointer {
}
}
}
- c.local_cachealloc += int(size)
+ c.local_cachealloc += intptr(size)
} else {
- mp.scalararg[0] = uint(size)
- mp.scalararg[1] = uint(flags)
- onM(&largeAlloc)
+ mp := acquirem()
+ mp.scalararg[0] = uintptr(size)
+ mp.scalararg[1] = uintptr(flags)
+ onM(largeAlloc_m)
s = (*mspan)(mp.ptrarg[0])
mp.ptrarg[0] = nil
+ releasem(mp)
x = unsafe.Pointer(uintptr(s.start << pageShift))
size = uintptr(s.elemsize)
}
- // TODO: write markallocated in Go
- mp.ptrarg[0] = x
- mp.scalararg[0] = uint(size)
- mp.scalararg[1] = uint(size0)
- mp.ptrarg[1] = unsafe.Pointer(typ)
- mp.scalararg[2] = uint(flags & flagNoScan)
- onM(&markallocated_m)
+ if flags&flagNoScan != 0 {
+ // All objects are pre-marked as noscan.
+ goto marked
+ }
+
+ // From here till marked label marking the object as allocated
+ // and storing type info in the GC bitmap.
+ {
+ arena_start := uintptr(unsafe.Pointer(mheap_.arena_start))
+ off := (uintptr(x) - arena_start) / ptrSize
+ xbits := (*uint8)(unsafe.Pointer(arena_start - off/wordsPerBitmapByte - 1))
+ shift := (off % wordsPerBitmapByte) * gcBits
+ if debugMalloc && ((*xbits>>shift)&(bitMask|bitPtrMask)) != bitBoundary {
+ println("runtime: bits =", (*xbits>>shift)&(bitMask|bitPtrMask))
+ gothrow("bad bits in markallocated")
+ }
- mp.mallocing = 0
+ var ti, te uintptr
+ var ptrmask *uint8
+ if size == ptrSize {
+ // It's one word and it has pointers, it must be a pointer.
+ *xbits |= (bitsPointer << 2) << shift
+ goto marked
+ }
+ if typ.kind&kindGCProg != 0 {
+ nptr := (uintptr(typ.size) + ptrSize - 1) / ptrSize
+ masksize := nptr
+ if masksize%2 != 0 {
+ masksize *= 2 // repeated
+ }
+ masksize = masksize * pointersPerByte / 8 // 4 bits per word
+ masksize++ // unroll flag in the beginning
+ if masksize > maxGCMask && typ.gc[1] != 0 {
+ // If the mask is too large, unroll the program directly
+ // into the GC bitmap. It's 7 times slower than copying
+ // from the pre-unrolled mask, but saves 1/16 of type size
+ // memory for the mask.
+ mp := acquirem()
+ mp.ptrarg[0] = x
+ mp.ptrarg[1] = unsafe.Pointer(typ)
+ mp.scalararg[0] = uintptr(size)
+ mp.scalararg[1] = uintptr(size0)
+ onM(unrollgcproginplace_m)
+ releasem(mp)
+ goto marked
+ }
+ ptrmask = (*uint8)(unsafe.Pointer(uintptr(typ.gc[0])))
+ // Check whether the program is already unrolled
+ // by checking if the unroll flag byte is set
+ maskword := uintptr(atomicloadp(unsafe.Pointer(ptrmask)))
+ if *(*uint8)(unsafe.Pointer(&maskword)) == 0 {
+ mp := acquirem()
+ mp.ptrarg[0] = unsafe.Pointer(typ)
+ onM(unrollgcprog_m)
+ releasem(mp)
+ }
+ ptrmask = (*uint8)(add(unsafe.Pointer(ptrmask), 1)) // skip the unroll flag byte
+ } else {
+ ptrmask = (*uint8)(unsafe.Pointer(&typ.gc[0])) // embed mask
+ }
+ if size == 2*ptrSize {
+ *xbits = *ptrmask | bitBoundary
+ goto marked
+ }
+ te = uintptr(typ.size) / ptrSize
+ // If the type occupies odd number of words, its mask is repeated.
+ if te%2 == 0 {
+ te /= 2
+ }
+ // Copy pointer bitmask into the bitmap.
+ for i := uintptr(0); i < size0; i += 2 * ptrSize {
+ v := *(*uint8)(add(unsafe.Pointer(ptrmask), ti))
+ ti++
+ if ti == te {
+ ti = 0
+ }
+ if i == 0 {
+ v |= bitBoundary
+ }
+ if i+ptrSize == size0 {
+ v &^= uint8(bitPtrMask << 4)
+ }
+ *xbits = v
+ xbits = (*byte)(add(unsafe.Pointer(xbits), ^uintptr(0)))
+ }
+ if size0%(2*ptrSize) == 0 && size0 < size {
+ // Mark the word after last object's word as bitsDead.
+ *xbits = bitsDead << 2
+ }
+ }
+marked:
if raceenabled {
racemalloc(x, size)
}
+
+ if debugMalloc {
+ mp := acquirem()
+ if mp.mallocing == 0 {
+ gothrow("bad malloc")
+ }
+ mp.mallocing = 0
+ if mp.curg != nil {
+ mp.curg.stackguard0 = mp.curg.stackguard
+ }
+ releasem(mp)
+ releasem(mp)
+ }
+
if debug.allocfreetrace != 0 {
tracealloc(x, size, typ)
}
- if flags&flagNoProfiling == 0 {
- rate := MemProfileRate
- if rate > 0 {
- if size < uintptr(rate) && int32(size) < c.next_sample {
- c.next_sample -= int32(size)
- } else {
- profilealloc(mp, x, size)
- }
+
+ if rate := MemProfileRate; rate > 0 {
+ if size < uintptr(rate) && int32(size) < c.next_sample {
+ c.next_sample -= int32(size)
+ } else {
+ mp := acquirem()
+ profilealloc(mp, x, size)
+ releasem(mp)
}
}
- releasem(mp)
-
- if flags&flagNoInvokeGC == 0 && memstats.heap_alloc >= memstats.next_gc {
+ if memstats.heap_alloc >= memstats.next_gc {
gogc(0)
}
@@ -258,7 +387,7 @@ func profilealloc(mp *m, x unsafe.Pointer, size uintptr) {
if rate > 0x3fffffff { // make 2*rate not overflow
rate = 0x3fffffff
}
- next := int32(fastrand2()) % (2 * int32(rate))
+ next := int32(fastrand1()) % (2 * int32(rate))
// Subtract the "remainder" of the current allocation.
// Otherwise objects that are close in size to sampling rate
// will be under-sampled, because we consistently discard this remainder.
@@ -268,42 +397,25 @@ func profilealloc(mp *m, x unsafe.Pointer, size uintptr) {
}
c.next_sample = next
}
- mp.scalararg[0] = uint(size)
- mp.ptrarg[0] = x
- onM(&mprofMalloc)
+
+ mProf_Malloc(x, size)
}
// force = 1 - do GC regardless of current heap usage
// force = 2 - go GC and eager sweep
func gogc(force int32) {
- if false && (GOARCH == "power64" || GOARCH == "power64le") {
- return
- }
- if memstats.enablegc == 0 {
- return
- }
-
- // TODO: should never happen? Only C calls malloc while holding a lock?
+ // The gc is turned off (via enablegc) until the bootstrap has completed.
+ // Also, malloc gets called in the guts of a number of libraries that might be
+ // holding locks. To avoid deadlocks during stoptheworld, don't bother
+ // trying to run gc while holding a lock. The next mallocgc without a lock
+ // will do the gc instead.
mp := acquirem()
- if mp.locks > 1 {
+ if gp := getg(); gp == mp.g0 || mp.locks > 1 || !memstats.enablegc || panicking != 0 || gcpercent < 0 {
releasem(mp)
return
}
releasem(mp)
-
- if panicking != 0 {
- return
- }
- if gcpercent == gcpercentUnknown {
- golock(&mheap_.lock)
- if gcpercent == gcpercentUnknown {
- gcpercent = goreadgogc()
- }
- gounlock(&mheap_.lock)
- }
- if gcpercent < 0 {
- return
- }
+ mp = nil
semacquire(&worldsema, false)
@@ -315,10 +427,14 @@ func gogc(force int32) {
}
// Ok, we're doing it! Stop everybody else
- startTime := gonanotime()
+ startTime := nanotime()
mp = acquirem()
mp.gcing = 1
- stoptheworld()
+ releasem(mp)
+ onM(stoptheworld)
+ if mp != acquirem() {
+ gothrow("gogc: rescheduled")
+ }
clearpools()
@@ -333,23 +449,25 @@ func gogc(force int32) {
}
for i := 0; i < n; i++ {
if i > 0 {
- startTime = gonanotime()
+ startTime = nanotime()
}
// switch to g0, call gc, then switch back
- mp.scalararg[0] = uint(startTime)
+ mp.scalararg[0] = uintptr(uint32(startTime)) // low 32 bits
+ mp.scalararg[1] = uintptr(startTime >> 32) // high 32 bits
if force >= 2 {
- mp.scalararg[1] = 1 // eagersweep
+ mp.scalararg[2] = 1 // eagersweep
} else {
- mp.scalararg[1] = 0
+ mp.scalararg[2] = 0
}
- onM(&mgc2)
+ onM(gc_m)
}
// all done
mp.gcing = 0
semrelease(&worldsema)
- starttheworld()
+ onM(starttheworld)
releasem(mp)
+ mp = nil
// now that gc is done, kick off finalizer thread if needed
if !concurrentSweep {
@@ -406,27 +524,291 @@ func GC() {
// If a finalizer must run for a long time, it should do so by starting
// a new goroutine.
func SetFinalizer(obj interface{}, finalizer interface{}) {
- // We do just enough work here to make the mcall type safe.
- // The rest is done on the M stack.
e := (*eface)(unsafe.Pointer(&obj))
- typ := e._type
- if typ == nil {
+ etyp := e._type
+ if etyp == nil {
gothrow("runtime.SetFinalizer: first argument is nil")
}
- if typ.kind&kindMask != kindPtr {
- gothrow("runtime.SetFinalizer: first argument is " + *typ._string + ", not pointer")
+ if etyp.kind&kindMask != kindPtr {
+ gothrow("runtime.SetFinalizer: first argument is " + *etyp._string + ", not pointer")
+ }
+ ot := (*ptrtype)(unsafe.Pointer(etyp))
+ if ot.elem == nil {
+ gothrow("nil elem type!")
+ }
+
+ // As an implementation detail we do not run finalizers for zero-sized objects,
+ // because we use &runtime·zerobase for all such allocations.
+ if ot.elem.size == 0 {
+ return
+ }
+
+ // find the containing object
+ _, base, _ := findObject(e.data)
+
+ // The following check is required for cases when a user passes a pointer to composite
+ // literal, but compiler makes it a pointer to global. For example:
+ // var Foo = &Object{}
+ // func main() {
+ // runtime.SetFinalizer(Foo, nil)
+ // }
+ // See issue 7656.
+ if base == nil {
+ return
+ }
+
+ if e.data != base {
+ // As an implementation detail we allow to set finalizers for an inner byte
+ // of an object if it could come from tiny alloc (see mallocgc for details).
+ if ot.elem == nil || ot.elem.kind&kindNoPointers == 0 || ot.elem.size >= maxTinySize {
+ gothrow("runtime.SetFinalizer: pointer not at beginning of allocated block")
+ }
}
f := (*eface)(unsafe.Pointer(&finalizer))
ftyp := f._type
- if ftyp != nil && ftyp.kind&kindMask != kindFunc {
+ if ftyp == nil {
+ // switch to M stack and remove finalizer
+ mp := acquirem()
+ mp.ptrarg[0] = e.data
+ onM(removeFinalizer_m)
+ releasem(mp)
+ return
+ }
+
+ if ftyp.kind&kindMask != kindFunc {
gothrow("runtime.SetFinalizer: second argument is " + *ftyp._string + ", not a function")
}
+ ft := (*functype)(unsafe.Pointer(ftyp))
+ ins := *(*[]*_type)(unsafe.Pointer(&ft.in))
+ if ft.dotdotdot || len(ins) != 1 {
+ gothrow("runtime.SetFinalizer: cannot pass " + *etyp._string + " to finalizer " + *ftyp._string)
+ }
+ fint := ins[0]
+ switch {
+ case fint == etyp:
+ // ok - same type
+ goto okarg
+ case fint.kind&kindMask == kindPtr:
+ if (fint.x == nil || fint.x.name == nil || etyp.x == nil || etyp.x.name == nil) && (*ptrtype)(unsafe.Pointer(fint)).elem == ot.elem {
+ // ok - not same type, but both pointers,
+ // one or the other is unnamed, and same element type, so assignable.
+ goto okarg
+ }
+ case fint.kind&kindMask == kindInterface:
+ ityp := (*interfacetype)(unsafe.Pointer(fint))
+ if len(ityp.mhdr) == 0 {
+ // ok - satisfies empty interface
+ goto okarg
+ }
+ if _, ok := assertE2I2(ityp, obj); ok {
+ goto okarg
+ }
+ }
+ gothrow("runtime.SetFinalizer: cannot pass " + *etyp._string + " to finalizer " + *ftyp._string)
+okarg:
+ // compute size needed for return parameters
+ nret := uintptr(0)
+ for _, t := range *(*[]*_type)(unsafe.Pointer(&ft.out)) {
+ nret = round(nret, uintptr(t.align)) + uintptr(t.size)
+ }
+ nret = round(nret, ptrSize)
+
+ // make sure we have a finalizer goroutine
+ createfing()
+
+ // switch to M stack to add finalizer record
mp := acquirem()
- mp.ptrarg[0] = unsafe.Pointer(typ)
+ mp.ptrarg[0] = f.data
mp.ptrarg[1] = e.data
- mp.ptrarg[2] = unsafe.Pointer(ftyp)
- mp.ptrarg[3] = f.data
- onM(&setFinalizer)
+ mp.scalararg[0] = nret
+ mp.ptrarg[2] = unsafe.Pointer(fint)
+ mp.ptrarg[3] = unsafe.Pointer(ot)
+ onM(setFinalizer_m)
+ if mp.scalararg[0] != 1 {
+ gothrow("runtime.SetFinalizer: finalizer already set")
+ }
releasem(mp)
}
+
+// round n up to a multiple of a. a must be a power of 2.
+func round(n, a uintptr) uintptr {
+ return (n + a - 1) &^ (a - 1)
+}
+
+// Look up pointer v in heap. Return the span containing the object,
+// the start of the object, and the size of the object. If the object
+// does not exist, return nil, nil, 0.
+func findObject(v unsafe.Pointer) (s *mspan, x unsafe.Pointer, n uintptr) {
+ c := gomcache()
+ c.local_nlookup++
+ if ptrSize == 4 && c.local_nlookup >= 1<<30 {
+ // purge cache stats to prevent overflow
+ lock(&mheap_.lock)
+ purgecachedstats(c)
+ unlock(&mheap_.lock)
+ }
+
+ // find span
+ arena_start := uintptr(unsafe.Pointer(mheap_.arena_start))
+ arena_used := uintptr(unsafe.Pointer(mheap_.arena_used))
+ if uintptr(v) < arena_start || uintptr(v) >= arena_used {
+ return
+ }
+ p := uintptr(v) >> pageShift
+ q := p - arena_start>>pageShift
+ s = *(**mspan)(add(unsafe.Pointer(mheap_.spans), q*ptrSize))
+ if s == nil {
+ return
+ }
+ x = unsafe.Pointer(uintptr(s.start) << pageShift)
+
+ if uintptr(v) < uintptr(x) || uintptr(v) >= uintptr(unsafe.Pointer(s.limit)) || s.state != mSpanInUse {
+ s = nil
+ x = nil
+ return
+ }
+
+ n = uintptr(s.elemsize)
+ if s.sizeclass != 0 {
+ x = add(x, (uintptr(v)-uintptr(x))/n*n)
+ }
+ return
+}
+
+var fingCreate uint32
+
+func createfing() {
+ // start the finalizer goroutine exactly once
+ if fingCreate == 0 && cas(&fingCreate, 0, 1) {
+ go runfinq()
+ }
+}
+
+// This is the goroutine that runs all of the finalizers
+func runfinq() {
+ var (
+ frame unsafe.Pointer
+ framecap uintptr
+ )
+
+ for {
+ lock(&finlock)
+ fb := finq
+ finq = nil
+ if fb == nil {
+ gp := getg()
+ fing = gp
+ fingwait = true
+ gp.issystem = true
+ goparkunlock(&finlock, "finalizer wait")
+ gp.issystem = false
+ continue
+ }
+ unlock(&finlock)
+ if raceenabled {
+ racefingo()
+ }
+ for fb != nil {
+ for i := int32(0); i < fb.cnt; i++ {
+ f := (*finalizer)(add(unsafe.Pointer(&fb.fin), uintptr(i)*unsafe.Sizeof(finalizer{})))
+
+ framesz := unsafe.Sizeof((interface{})(nil)) + uintptr(f.nret)
+ if framecap < framesz {
+ // The frame does not contain pointers interesting for GC,
+ // 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 = gomallocgc(framesz, nil, flagNoScan)
+ framecap = framesz
+ }
+
+ if f.fint == nil {
+ gothrow("missing type in runfinq")
+ }
+ switch f.fint.kind & kindMask {
+ case kindPtr:
+ // direct use of pointer
+ *(*unsafe.Pointer)(frame) = f.arg
+ case kindInterface:
+ ityp := (*interfacetype)(unsafe.Pointer(f.fint))
+ // set up with empty interface
+ (*eface)(frame)._type = &f.ot.typ
+ (*eface)(frame).data = f.arg
+ if len(ityp.mhdr) != 0 {
+ // convert to interface with methods
+ // this conversion is guaranteed to succeed - we checked in SetFinalizer
+ *(*fInterface)(frame) = assertE2I(ityp, *(*interface{})(frame))
+ }
+ default:
+ gothrow("bad kind in runfinq")
+ }
+ reflectcall(unsafe.Pointer(f.fn), frame, uint32(framesz), uint32(framesz))
+
+ // drop finalizer queue references to finalized object
+ f.fn = nil
+ f.arg = nil
+ f.ot = nil
+ }
+ fb.cnt = 0
+ next := fb.next
+ lock(&finlock)
+ fb.next = finc
+ finc = fb
+ unlock(&finlock)
+ fb = next
+ }
+ }
+}
+
+var persistent struct {
+ lock mutex
+ pos unsafe.Pointer
+ end unsafe.Pointer
+}
+
+// Wrapper around sysAlloc that can allocate small chunks.
+// There is no associated free operation.
+// Intended for things like function/type/debug-related persistent data.
+// If align is 0, uses default align (currently 8).
+func persistentalloc(size, align uintptr, stat *uint64) unsafe.Pointer {
+ const (
+ chunk = 256 << 10
+ maxBlock = 64 << 10 // VM reservation granularity is 64K on windows
+ )
+
+ if align != 0 {
+ if align&(align-1) != 0 {
+ gothrow("persistentalloc: align is not a power of 2")
+ }
+ if align > _PageSize {
+ gothrow("persistentalloc: align is too large")
+ }
+ } else {
+ align = 8
+ }
+
+ if size >= maxBlock {
+ return sysAlloc(size, stat)
+ }
+
+ lock(&persistent.lock)
+ persistent.pos = roundup(persistent.pos, align)
+ if uintptr(persistent.pos)+size > uintptr(persistent.end) {
+ persistent.pos = sysAlloc(chunk, &memstats.other_sys)
+ if persistent.pos == nil {
+ unlock(&persistent.lock)
+ gothrow("runtime: cannot allocate memory")
+ }
+ persistent.end = add(persistent.pos, chunk)
+ }
+ p := persistent.pos
+ persistent.pos = add(persistent.pos, size)
+ unlock(&persistent.lock)
+
+ if stat != &memstats.other_sys {
+ xadd64(stat, int64(size))
+ xadd64(&memstats.other_sys, -int64(size))
+ }
+ return p
+}
diff --git a/src/pkg/runtime/malloc.h b/src/pkg/runtime/malloc.h
index 41988415e0..f941570692 100644
--- a/src/pkg/runtime/malloc.h
+++ b/src/pkg/runtime/malloc.h
@@ -93,7 +93,7 @@ enum
PageSize = 1<<PageShift,
PageMask = PageSize - 1,
};
-typedef uintptr PageID; // address >> PageShift
+typedef uintptr pageID; // address >> PageShift
enum
{
@@ -141,8 +141,8 @@ enum
// Max number of threads to run garbage collection.
// 2, 3, and 4 are all plausible maximums depending
// on the hardware details of the machine. The garbage
- // collector scales well to 8 cpus.
- MaxGcproc = 8,
+ // collector scales well to 32 cpus.
+ MaxGcproc = 32,
};
// Maximum memory allocation size, a hint for callers.
@@ -160,12 +160,12 @@ struct MLink
MLink *next;
};
-// SysAlloc obtains a large chunk of zeroed memory from the
+// sysAlloc obtains a large chunk of zeroed memory from the
// operating system, typically on the order of a hundred kilobytes
// or a megabyte.
-// NOTE: SysAlloc returns OS-aligned memory, but the heap allocator
+// NOTE: sysAlloc returns OS-aligned memory, but the heap allocator
// may use larger alignment, so the caller must be careful to realign the
-// memory obtained by SysAlloc.
+// memory obtained by sysAlloc.
//
// SysUnused notifies the operating system that the contents
// of the memory region are no longer needed and can be reused
@@ -187,16 +187,16 @@ struct MLink
// reserved, false if it has merely been checked.
// NOTE: SysReserve returns OS-aligned memory, but the heap allocator
// may use larger alignment, so the caller must be careful to realign the
-// memory obtained by SysAlloc.
+// memory obtained by sysAlloc.
//
// SysMap maps previously reserved address space for use.
// The reserved argument is true if the address space was really
// reserved, not merely checked.
//
-// SysFault marks a (already SysAlloc'd) region to fault
+// SysFault marks a (already sysAlloc'd) region to fault
// if accessed. Used only for debugging the runtime.
-void* runtime·SysAlloc(uintptr nbytes, uint64 *stat);
+void* runtime·sysAlloc(uintptr nbytes, uint64 *stat);
void runtime·SysFree(void *v, uintptr nbytes, uint64 *stat);
void runtime·SysUnused(void *v, uintptr nbytes);
void runtime·SysUsed(void *v, uintptr nbytes);
@@ -205,7 +205,7 @@ void* runtime·SysReserve(void *v, uintptr nbytes, bool *reserved);
void runtime·SysFault(void *v, uintptr nbytes);
// FixAlloc is a simple free-list allocator for fixed size objects.
-// Malloc uses a FixAlloc wrapped around SysAlloc to manages its
+// Malloc uses a FixAlloc wrapped around sysAlloc to manages its
// MCache and MSpan objects.
//
// Memory returned by FixAlloc_Alloc is not zeroed.
@@ -242,7 +242,7 @@ struct MStats
uint64 nfree; // number of frees
// Statistics about malloc heap.
- // protected by mheap.Lock
+ // protected by mheap.lock
uint64 heap_alloc; // bytes allocated and still in use
uint64 heap_sys; // bytes obtained from system
uint64 heap_idle; // bytes in idle spans
@@ -283,6 +283,7 @@ struct MStats
#define mstats runtime·memstats
extern MStats mstats;
void runtime·updatememstats(GCStats *stats);
+void runtime·ReadMemStats(MStats *stats);
// Size classes. Computed and initialized by InitSizes.
//
@@ -302,7 +303,6 @@ extern int8 runtime·size_to_class8[1024/8 + 1];
extern int8 runtime·size_to_class128[(MaxSmallSize-1024)/128 + 1];
extern void runtime·InitSizes(void);
-
typedef struct MCacheList MCacheList;
struct MCacheList
{
@@ -317,6 +317,8 @@ struct StackFreeList
uintptr size; // total size of stacks in list
};
+typedef struct SudoG SudoG;
+
// Per-thread (in Go, per-P) cache for small objects.
// No locking needed because it is per-thread (per-P).
struct MCache
@@ -334,6 +336,8 @@ struct MCache
StackFreeList stackcache[NumStackOrders];
+ SudoG* sudogcache;
+
void* gcworkbuf;
// Local allocator stats, flushed during GC.
@@ -370,7 +374,7 @@ struct Special
typedef struct SpecialFinalizer SpecialFinalizer;
struct SpecialFinalizer
{
- Special;
+ Special special;
FuncVal* fn;
uintptr nret;
Type* fint;
@@ -382,7 +386,7 @@ typedef struct Bucket Bucket; // from mprof.h
typedef struct SpecialProfile SpecialProfile;
struct SpecialProfile
{
- Special;
+ Special special;
Bucket* b;
};
@@ -399,7 +403,7 @@ struct MSpan
{
MSpan *next; // in a span linked list
MSpan *prev; // in a span linked list
- PageID start; // starting page number
+ pageID start; // starting page number
uintptr npages; // number of pages in span
MLink *freelist; // list of free objects
// sweep generation:
@@ -417,13 +421,13 @@ struct MSpan
int64 unusedsince; // First time spotted by GC in MSpanFree state
uintptr npreleased; // number of pages released to the OS
byte *limit; // end of data in span
- Lock specialLock; // guards specials list
+ Mutex specialLock; // guards specials list
Special *specials; // linked list of special records sorted by offset.
};
-void runtime·MSpan_Init(MSpan *span, PageID start, uintptr npages);
+void runtime·MSpan_Init(MSpan *span, pageID start, uintptr npages);
void runtime·MSpan_EnsureSwept(MSpan *span);
-bool runtime·MSpan_Sweep(MSpan *span);
+bool runtime·MSpan_Sweep(MSpan *span, bool preserve);
// Every MSpan is in one doubly-linked list,
// either one of the MHeap's free lists or one of the
@@ -438,7 +442,7 @@ void runtime·MSpanList_Remove(MSpan *span); // from whatever list it is in
// Central list of free objects of a given size.
struct MCentral
{
- Lock;
+ Mutex lock;
int32 sizeclass;
MSpan nonempty; // list of spans with a free object
MSpan empty; // list of spans with no free objects (or cached in an MCache)
@@ -447,20 +451,20 @@ struct MCentral
void runtime·MCentral_Init(MCentral *c, int32 sizeclass);
MSpan* runtime·MCentral_CacheSpan(MCentral *c);
void runtime·MCentral_UncacheSpan(MCentral *c, MSpan *s);
-bool runtime·MCentral_FreeSpan(MCentral *c, MSpan *s, int32 n, MLink *start, MLink *end);
+bool runtime·MCentral_FreeSpan(MCentral *c, MSpan *s, int32 n, MLink *start, MLink *end, bool preserve);
// Main malloc heap.
// The heap itself is the "free[]" and "large" arrays,
// but all the other global data is here too.
struct MHeap
{
- Lock;
+ Mutex lock;
MSpan free[MaxMHeapList]; // free lists of given length
MSpan freelarge; // free lists length >= MaxMHeapList
MSpan busy[MaxMHeapList]; // busy lists of large objects of given length
MSpan busylarge; // busy lists of large objects length >= MaxMHeapList
MSpan **allspans; // all spans out there
- MSpan **sweepspans; // copy of allspans referenced by sweeper
+ MSpan **gcspans; // copy of allspans referenced by GC marker or sweeper
uint32 nspan;
uint32 nspancap;
uint32 sweepgen; // sweep generation, see comment in MSpan
@@ -480,10 +484,10 @@ struct MHeap
// central free lists for small size classes.
// the padding makes sure that the MCentrals are
- // spaced CacheLineSize bytes apart, so that each MCentral.Lock
+ // spaced CacheLineSize bytes apart, so that each MCentral.lock
// gets its own cache line.
struct {
- MCentral;
+ MCentral mcentral;
byte pad[CacheLineSize];
} central[NumSizeClasses];
@@ -491,7 +495,7 @@ struct MHeap
FixAlloc cachealloc; // allocator for MCache*
FixAlloc specialfinalizeralloc; // allocator for SpecialFinalizer*
FixAlloc specialprofilealloc; // allocator for SpecialProfile*
- Lock speciallock; // lock for sepcial record allocators.
+ Mutex speciallock; // lock for sepcial record allocators.
// Malloc stats.
uint64 largefree; // bytes freed for large objects (>MaxSmallSize)
@@ -511,14 +515,11 @@ MSpan* runtime·MHeap_LookupMaybe(MHeap *h, void *v);
void* runtime·MHeap_SysAlloc(MHeap *h, uintptr n);
void runtime·MHeap_MapBits(MHeap *h);
void runtime·MHeap_MapSpans(MHeap *h);
-void runtime·MHeap_Scavenger(void);
+void runtime·MHeap_Scavenge(int32 k, uint64 now, uint64 limit);
-void* runtime·mallocgc(uintptr size, Type* typ, uint32 flag);
void* runtime·persistentalloc(uintptr size, uintptr align, uint64 *stat);
int32 runtime·mlookup(void *v, byte **base, uintptr *size, MSpan **s);
-void runtime·gc(int32 force);
uintptr runtime·sweepone(void);
-void runtime·markallocated(void *v, uintptr size, uintptr size0, Type* typ, bool scan);
void runtime·markspan(void *v, uintptr size, uintptr n, bool leftover);
void runtime·unmarkspan(void *v, uintptr size);
void runtime·purgecachedstats(MCache*);
@@ -527,6 +528,7 @@ void* runtime·cnewarray(Type*, intgo);
void runtime·tracealloc(void*, uintptr, Type*);
void runtime·tracefree(void*, uintptr);
void runtime·tracegc(void);
+extern Type* runtime·conservative;
int32 runtime·gcpercent;
int32 runtime·readgogc(void);
@@ -536,27 +538,47 @@ enum
{
// flags to malloc
FlagNoScan = 1<<0, // GC doesn't have to scan object
- FlagNoProfiling = 1<<1, // must not profile
- FlagNoGC = 1<<2, // must not free or scan for pointers
- FlagNoZero = 1<<3, // don't zero memory
- FlagNoInvokeGC = 1<<4, // don't invoke GC
+ FlagNoZero = 1<<1, // don't zero memory
};
-void runtime·MProf_Malloc(void*, uintptr);
-void runtime·MProf_Free(Bucket*, uintptr, bool);
-void runtime·MProf_GC(void);
-void runtime·iterate_memprof(void (*callback)(Bucket*, uintptr, uintptr*, uintptr, uintptr, uintptr));
+void runtime·mProf_Malloc(void*, uintptr);
+void runtime·mProf_Free(Bucket*, uintptr, bool);
+void runtime·mProf_GC(void);
+void runtime·iterate_memprof(void (**callback)(Bucket*, uintptr, uintptr*, uintptr, uintptr, uintptr));
int32 runtime·gcprocs(void);
void runtime·helpgc(int32 nproc);
void runtime·gchelper(void);
void runtime·createfing(void);
G* runtime·wakefing(void);
void runtime·getgcmask(byte*, Type*, byte**, uintptr*);
+
+typedef struct Finalizer Finalizer;
+struct Finalizer
+{
+ FuncVal *fn; // function to call
+ void *arg; // ptr to object
+ uintptr nret; // bytes of return values from fn
+ Type *fint; // type of first argument of fn
+ PtrType *ot; // type of ptr to object
+};
+
+typedef struct FinBlock FinBlock;
+struct FinBlock
+{
+ FinBlock *alllink;
+ FinBlock *next;
+ int32 cnt;
+ int32 cap;
+ Finalizer fin[1];
+};
+extern Mutex runtime·finlock; // protects the following variables
extern G* runtime·fing;
extern bool runtime·fingwait;
extern bool runtime·fingwake;
+extern FinBlock *runtime·finq; // list of finalizers that are to be executed
+extern FinBlock *runtime·finc; // cache of free blocks
-void runtime·setprofilebucket(void *p, Bucket *b);
+void runtime·setprofilebucket_m(void);
bool runtime·addfinalizer(void*, FuncVal *fn, uintptr, Type*, PtrType*);
void runtime·removefinalizer(void*);
@@ -581,12 +603,15 @@ struct StackMap
// (the index is encoded in PCDATA_StackMapIndex).
BitVector runtime·stackmapdata(StackMap *stackmap, int32 n);
+extern BitVector runtime·gcdatamask;
+extern BitVector runtime·gcbssmask;
+
// defined in mgc0.go
void runtime·gc_m_ptr(Eface*);
void runtime·gc_g_ptr(Eface*);
void runtime·gc_itab_ptr(Eface*);
-int32 runtime·setgcpercent(int32);
+void runtime·setgcpercent_m(void);
// Value we use to mark dead pointers when GODEBUG=gcdead=1.
#define PoisonGC ((uintptr)0xf969696969696969ULL)
diff --git a/src/pkg/runtime/malloc1.go b/src/pkg/runtime/malloc1.go
deleted file mode 100644
index da92f4c2fb..0000000000
--- a/src/pkg/runtime/malloc1.go
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build ignore
-
-// trivial malloc test
-
-package main
-
-import (
- "flag"
- "fmt"
- "runtime"
-)
-
-var chatty = flag.Bool("v", false, "chatty")
-
-func main() {
- memstats := new(runtime.MemStats)
- runtime.Free(runtime.Alloc(1))
- runtime.ReadMemStats(memstats)
- if *chatty {
- fmt.Printf("%+v %v\n", memstats, uint64(0))
- }
-}
diff --git a/src/pkg/runtime/malloc_test.go b/src/pkg/runtime/malloc_test.go
index 252760a07e..b7795aa1d6 100644
--- a/src/pkg/runtime/malloc_test.go
+++ b/src/pkg/runtime/malloc_test.go
@@ -16,14 +16,34 @@ func TestMemStats(t *testing.T) {
// Test that MemStats has sane values.
st := new(MemStats)
ReadMemStats(st)
- if st.HeapSys == 0 || st.StackSys == 0 || st.MSpanSys == 0 || st.MCacheSys == 0 ||
- st.BuckHashSys == 0 || st.GCSys == 0 || st.OtherSys == 0 {
- t.Fatalf("Zero sys value: %+v", *st)
+
+ // Everything except HeapReleased and HeapIdle, because they indeed can be 0.
+ if st.Alloc == 0 || st.TotalAlloc == 0 || st.Sys == 0 || st.Lookups == 0 ||
+ st.Mallocs == 0 || st.Frees == 0 || st.HeapAlloc == 0 || st.HeapSys == 0 ||
+ st.HeapInuse == 0 || st.HeapObjects == 0 || st.StackInuse == 0 ||
+ st.StackSys == 0 || st.MSpanInuse == 0 || st.MSpanSys == 0 || st.MCacheInuse == 0 ||
+ st.MCacheSys == 0 || st.BuckHashSys == 0 || st.GCSys == 0 || st.OtherSys == 0 ||
+ st.NextGC == 0 || st.NumGC == 0 {
+ t.Fatalf("Zero value: %+v", *st)
+ }
+
+ if st.Alloc > 1e10 || st.TotalAlloc > 1e11 || st.Sys > 1e10 || st.Lookups > 1e10 ||
+ st.Mallocs > 1e10 || st.Frees > 1e10 || st.HeapAlloc > 1e10 || st.HeapSys > 1e10 ||
+ st.HeapIdle > 1e10 || st.HeapInuse > 1e10 || st.HeapObjects > 1e10 || st.StackInuse > 1e10 ||
+ st.StackSys > 1e10 || st.MSpanInuse > 1e10 || st.MSpanSys > 1e10 || st.MCacheInuse > 1e10 ||
+ st.MCacheSys > 1e10 || st.BuckHashSys > 1e10 || st.GCSys > 1e10 || st.OtherSys > 1e10 ||
+ st.NextGC > 1e10 || st.NumGC > 1e9 {
+ t.Fatalf("Insanely high value (overflow?): %+v", *st)
}
+
if st.Sys != st.HeapSys+st.StackSys+st.MSpanSys+st.MCacheSys+
st.BuckHashSys+st.GCSys+st.OtherSys {
t.Fatalf("Bad sys value: %+v", *st)
}
+
+ if st.HeapIdle+st.HeapInuse != st.HeapSys {
+ t.Fatalf("HeapIdle(%d) + HeapInuse(%d) should be equal to HeapSys(%d), but isn't.", st.HeapIdle, st.HeapInuse, st.HeapSys)
+ }
}
var mallocSink uintptr
diff --git a/src/pkg/runtime/mallocrand.go b/src/pkg/runtime/mallocrand.go
deleted file mode 100644
index f1bcb89cfa..0000000000
--- a/src/pkg/runtime/mallocrand.go
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build ignore
-
-// Random malloc test.
-
-package main
-
-import (
- "flag"
- "math/rand"
- "runtime"
- "unsafe"
-)
-
-var chatty = flag.Bool("v", false, "chatty")
-
-var footprint uint64
-var allocated uint64
-
-func bigger() {
- memstats := new(runtime.MemStats)
- runtime.ReadMemStats(memstats)
- if f := memstats.Sys; footprint < f {
- footprint = f
- if *chatty {
- println("Footprint", footprint, " for ", allocated)
- }
- if footprint > 1e9 {
- println("too big")
- panic("fail")
- }
- }
-}
-
-// Prime the data structures by allocating one of
-// each block in order. After this, there should be
-// little reason to ask for more memory from the OS.
-func prime() {
- for i := 0; i < 16; i++ {
- b := runtime.Alloc(1 << uint(i))
- runtime.Free(b)
- }
- for i := uintptr(0); i < 256; i++ {
- b := runtime.Alloc(i << 12)
- runtime.Free(b)
- }
-}
-
-func memset(b *byte, c byte, n uintptr) {
- np := uintptr(n)
- for i := uintptr(0); i < np; i++ {
- *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(b)) + i)) = c
- }
-}
-
-func main() {
- flag.Parse()
- // prime()
- var blocks [1]struct {
- base *byte
- siz uintptr
- }
- for i := 0; i < 1<<10; i++ {
- if i%(1<<10) == 0 && *chatty {
- println(i)
- }
- b := rand.Int() % len(blocks)
- if blocks[b].base != nil {
- // println("Free", blocks[b].siz, blocks[b].base)
- runtime.Free(blocks[b].base)
- blocks[b].base = nil
- allocated -= uint64(blocks[b].siz)
- continue
- }
- siz := uintptr(rand.Int() >> (11 + rand.Uint32()%20))
- base := runtime.Alloc(siz)
- // ptr := uintptr(syscall.BytePtr(base))+uintptr(siz/2)
- // obj, size, ref, ok := allocator.find(ptr)
- // if obj != base || *ref != 0 || !ok {
- // println("find", siz, obj, ref, ok)
- // panic("fail")
- // }
- blocks[b].base = base
- blocks[b].siz = siz
- allocated += uint64(siz)
- // println("Alloc", siz, base)
- memset(base, 0xbb, siz)
- bigger()
- }
-}
diff --git a/src/pkg/runtime/mallocrep.go b/src/pkg/runtime/mallocrep.go
deleted file mode 100644
index 03ee71edb4..0000000000
--- a/src/pkg/runtime/mallocrep.go
+++ /dev/null
@@ -1,72 +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.
-
-// Repeated malloc test.
-
-// +build ignore
-
-package main
-
-import (
- "flag"
- "runtime"
-)
-
-var chatty = flag.Bool("v", false, "chatty")
-
-var oldsys uint64
-var memstats runtime.MemStats
-
-func bigger() {
- st := &memstats
- runtime.ReadMemStats(st)
- if oldsys < st.Sys {
- oldsys = st.Sys
- if *chatty {
- println(st.Sys, " system bytes for ", st.Alloc, " Go bytes")
- }
- if st.Sys > 1e9 {
- println("too big")
- panic("fail")
- }
- }
-}
-
-func main() {
- runtime.GC() // clean up garbage from init
- runtime.ReadMemStats(&memstats) // first call can do some allocations
- runtime.MemProfileRate = 0 // disable profiler
- stacks := memstats.Alloc // ignore stacks
- flag.Parse()
- for i := 0; i < 1<<7; i++ {
- for j := 1; j <= 1<<22; j <<= 1 {
- if i == 0 && *chatty {
- println("First alloc:", j)
- }
- if a := memstats.Alloc - stacks; a != 0 {
- println("no allocations but stats report", a, "bytes allocated")
- panic("fail")
- }
- b := runtime.Alloc(uintptr(j))
- runtime.ReadMemStats(&memstats)
- during := memstats.Alloc - stacks
- runtime.Free(b)
- runtime.ReadMemStats(&memstats)
- if a := memstats.Alloc - stacks; a != 0 {
- println("allocated ", j, ": wrong stats: during=", during, " after=", a, " (want 0)")
- panic("fail")
- }
- bigger()
- }
- if i%(1<<10) == 0 && *chatty {
- println(i)
- }
- if i == 0 {
- if *chatty {
- println("Primed", i)
- }
- // runtime.frozen = true
- }
- }
-}
diff --git a/src/pkg/runtime/mallocrep1.go b/src/pkg/runtime/mallocrep1.go
deleted file mode 100644
index bc33e3a6b4..0000000000
--- a/src/pkg/runtime/mallocrep1.go
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build ignore
-
-// Repeated malloc test.
-
-package main
-
-import (
- "flag"
- "fmt"
- "runtime"
- "strconv"
-)
-
-var chatty = flag.Bool("v", false, "chatty")
-var reverse = flag.Bool("r", false, "reverse")
-var longtest = flag.Bool("l", false, "long test")
-
-var b []*byte
-var stats = new(runtime.MemStats)
-
-func OkAmount(size, n uintptr) bool {
- if n < size {
- return false
- }
- if size < 16*8 {
- if n > size+16 {
- return false
- }
- } else {
- if n > size*9/8 {
- return false
- }
- }
- return true
-}
-
-func AllocAndFree(size, count int) {
- defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
- if *chatty {
- fmt.Printf("size=%d count=%d ...\n", size, count)
- }
- runtime.ReadMemStats(stats)
- n1 := stats.Alloc
- for i := 0; i < count; i++ {
- b[i] = runtime.Alloc(uintptr(size))
- base, n := runtime.Lookup(b[i])
- if base != b[i] || !OkAmount(uintptr(size), n) {
- println("lookup failed: got", base, n, "for", b[i])
- panic("fail")
- }
- runtime.ReadMemStats(stats)
- if stats.Sys > 1e9 {
- println("too much memory allocated")
- panic("fail")
- }
- }
- runtime.ReadMemStats(stats)
- n2 := stats.Alloc
- if *chatty {
- fmt.Printf("size=%d count=%d stats=%+v\n", size, count, *stats)
- }
- n3 := stats.Alloc
- for j := 0; j < count; j++ {
- i := j
- if *reverse {
- i = count - 1 - j
- }
- alloc := uintptr(stats.Alloc)
- base, n := runtime.Lookup(b[i])
- if base != b[i] || !OkAmount(uintptr(size), n) {
- println("lookup failed: got", base, n, "for", b[i])
- panic("fail")
- }
- runtime.Free(b[i])
- runtime.ReadMemStats(stats)
- if stats.Alloc != uint64(alloc-n) {
- println("free alloc got", stats.Alloc, "expected", alloc-n, "after free of", n)
- panic("fail")
- }
- if stats.Sys > 1e9 {
- println("too much memory allocated")
- panic("fail")
- }
- }
- runtime.ReadMemStats(stats)
- n4 := stats.Alloc
-
- if *chatty {
- fmt.Printf("size=%d count=%d stats=%+v\n", size, count, *stats)
- }
- if n2-n1 != n3-n4 {
- println("wrong alloc count: ", n2-n1, n3-n4)
- panic("fail")
- }
-}
-
-func atoi(s string) int {
- i, _ := strconv.Atoi(s)
- return i
-}
-
-func main() {
- runtime.MemProfileRate = 0 // disable profiler
- flag.Parse()
- b = make([]*byte, 10000)
- if flag.NArg() > 0 {
- AllocAndFree(atoi(flag.Arg(0)), atoi(flag.Arg(1)))
- return
- }
- maxb := 1 << 22
- if !*longtest {
- maxb = 1 << 19
- }
- for j := 1; j <= maxb; j <<= 1 {
- n := len(b)
- max := uintptr(1 << 28)
- if !*longtest {
- max = uintptr(maxb)
- }
- if uintptr(j)*uintptr(n) > max {
- n = int(max / uintptr(j))
- }
- if n < 10 {
- n = 10
- }
- for m := 1; m <= n; {
- AllocAndFree(j, m)
- if m == n {
- break
- }
- m = 5 * m / 4
- if m < 4 {
- m++
- }
- if m > n {
- m = n
- }
- }
- }
-}
diff --git a/src/pkg/runtime/mcache.c b/src/pkg/runtime/mcache.c
index 665173bff5..bb1fc54032 100644
--- a/src/pkg/runtime/mcache.c
+++ b/src/pkg/runtime/mcache.c
@@ -22,9 +22,9 @@ runtime·allocmcache(void)
MCache *c;
int32 i;
- runtime·lock(&runtime·mheap);
+ runtime·lock(&runtime·mheap.lock);
c = runtime·FixAlloc_Alloc(&runtime·mheap.cachealloc);
- runtime·unlock(&runtime·mheap);
+ runtime·unlock(&runtime·mheap.lock);
runtime·memclr((byte*)c, sizeof(*c));
for(i = 0; i < NumSizeClasses; i++)
c->alloc[i] = &emptymspan;
@@ -45,28 +45,30 @@ freemcache(MCache *c)
runtime·MCache_ReleaseAll(c);
runtime·stackcache_clear(c);
runtime·gcworkbuffree(c->gcworkbuf);
- runtime·lock(&runtime·mheap);
+ runtime·lock(&runtime·mheap.lock);
runtime·purgecachedstats(c);
runtime·FixAlloc_Free(&runtime·mheap.cachealloc, c);
- runtime·unlock(&runtime·mheap);
+ runtime·unlock(&runtime·mheap.lock);
}
static void
-freemcache_m(G *gp)
+freemcache_m(void)
{
MCache *c;
c = g->m->ptrarg[0];
g->m->ptrarg[0] = nil;
freemcache(c);
- runtime·gogo(&gp->sched);
}
void
runtime·freemcache(MCache *c)
{
+ void (*fn)(void);
+
g->m->ptrarg[0] = c;
- runtime·mcall(freemcache_m);
+ fn = freemcache_m;
+ runtime·onM(&fn);
}
// Gets a span that has a free object in it and assigns it
@@ -85,7 +87,7 @@ runtime·MCache_Refill(MCache *c, int32 sizeclass)
s->incache = false;
// Get a new cached span from the central lists.
- s = runtime·MCentral_CacheSpan(&runtime·mheap.central[sizeclass]);
+ s = runtime·MCentral_CacheSpan(&runtime·mheap.central[sizeclass].mcentral);
if(s == nil)
runtime·throw("out of memory");
if(s->freelist == nil) {
@@ -106,7 +108,7 @@ runtime·MCache_ReleaseAll(MCache *c)
for(i=0; i<NumSizeClasses; i++) {
s = c->alloc[i];
if(s != &emptymspan) {
- runtime·MCentral_UncacheSpan(&runtime·mheap.central[i], s);
+ runtime·MCentral_UncacheSpan(&runtime·mheap.central[i].mcentral, s);
c->alloc[i] = &emptymspan;
}
}
diff --git a/src/pkg/runtime/mcentral.c b/src/pkg/runtime/mcentral.c
index 3f64b5ed23..fe6bcfeb13 100644
--- a/src/pkg/runtime/mcentral.c
+++ b/src/pkg/runtime/mcentral.c
@@ -9,16 +9,12 @@
// The MCentral doesn't actually contain the list of free objects; the MSpan does.
// Each MCentral is two lists of MSpans: those with free objects (c->nonempty)
// and those that are completely allocated (c->empty).
-//
-// TODO(rsc): tcmalloc uses a "transfer cache" to split the list
-// into sections of class_to_transfercount[sizeclass] objects
-// so that it is faster to move those lists between MCaches and MCentrals.
#include "runtime.h"
#include "arch_GOARCH.h"
#include "malloc.h"
-static bool MCentral_Grow(MCentral *c);
+static MSpan* MCentral_Grow(MCentral *c);
// Initialize a single central free list.
void
@@ -37,22 +33,25 @@ runtime·MCentral_CacheSpan(MCentral *c)
int32 cap, n;
uint32 sg;
- runtime·lock(c);
+ runtime·lock(&c->lock);
sg = runtime·mheap.sweepgen;
retry:
for(s = c->nonempty.next; s != &c->nonempty; s = s->next) {
if(s->sweepgen == sg-2 && runtime·cas(&s->sweepgen, sg-2, sg-1)) {
- runtime·unlock(c);
- runtime·MSpan_Sweep(s);
- runtime·lock(c);
- // the span could have been moved to heap, retry
- goto retry;
+ runtime·MSpanList_Remove(s);
+ runtime·MSpanList_InsertBack(&c->empty, s);
+ runtime·unlock(&c->lock);
+ runtime·MSpan_Sweep(s, true);
+ goto havespan;
}
if(s->sweepgen == sg-1) {
// the span is being swept by background sweeper, skip
continue;
}
// we have a nonempty span that does not require sweeping, allocate from it
+ runtime·MSpanList_Remove(s);
+ runtime·MSpanList_InsertBack(&c->empty, s);
+ runtime·unlock(&c->lock);
goto havespan;
}
@@ -63,10 +62,13 @@ retry:
runtime·MSpanList_Remove(s);
// swept spans are at the end of the list
runtime·MSpanList_InsertBack(&c->empty, s);
- runtime·unlock(c);
- runtime·MSpan_Sweep(s);
- runtime·lock(c);
- // the span could be moved to nonempty or heap, retry
+ runtime·unlock(&c->lock);
+ runtime·MSpan_Sweep(s, true);
+ if(s->freelist != nil)
+ goto havespan;
+ runtime·lock(&c->lock);
+ // the span is still empty after sweep
+ // it is already in the empty list, so just retry
goto retry;
}
if(s->sweepgen == sg-1) {
@@ -77,25 +79,26 @@ retry:
// all subsequent ones must also be either swept or in process of sweeping
break;
}
+ runtime·unlock(&c->lock);
// Replenish central list if empty.
- if(!MCentral_Grow(c)) {
- runtime·unlock(c);
+ s = MCentral_Grow(c);
+ if(s == nil)
return nil;
- }
- goto retry;
+ runtime·lock(&c->lock);
+ runtime·MSpanList_InsertBack(&c->empty, s);
+ runtime·unlock(&c->lock);
havespan:
+ // At this point s is a non-empty span, queued at the end of the empty list,
+ // c is unlocked.
cap = (s->npages << PageShift) / s->elemsize;
n = cap - s->ref;
if(n == 0)
runtime·throw("empty span");
if(s->freelist == nil)
runtime·throw("freelist empty");
- runtime·MSpanList_Remove(s);
- runtime·MSpanList_InsertBack(&c->empty, s);
s->incache = true;
- runtime·unlock(c);
return s;
}
@@ -105,7 +108,7 @@ runtime·MCentral_UncacheSpan(MCentral *c, MSpan *s)
{
int32 cap, n;
- runtime·lock(c);
+ runtime·lock(&c->lock);
s->incache = false;
@@ -118,31 +121,46 @@ runtime·MCentral_UncacheSpan(MCentral *c, MSpan *s)
runtime·MSpanList_Remove(s);
runtime·MSpanList_Insert(&c->nonempty, s);
}
- runtime·unlock(c);
+ runtime·unlock(&c->lock);
}
// Free n objects from a span s back into the central free list c.
// Called during sweep.
// Returns true if the span was returned to heap. Sets sweepgen to
// the latest generation.
+// If preserve=true, don't return the span to heap nor relink in MCentral lists;
+// caller takes care of it.
bool
-runtime·MCentral_FreeSpan(MCentral *c, MSpan *s, int32 n, MLink *start, MLink *end)
+runtime·MCentral_FreeSpan(MCentral *c, MSpan *s, int32 n, MLink *start, MLink *end, bool preserve)
{
+ bool wasempty;
+
if(s->incache)
runtime·throw("freespan into cached span");
- runtime·lock(c);
+
+ // Add the objects back to s's free list.
+ wasempty = s->freelist == nil;
+ end->next = s->freelist;
+ s->freelist = start;
+ s->ref -= n;
+
+ if(preserve) {
+ // preserve is set only when called from MCentral_CacheSpan above,
+ // the span must be in the empty list.
+ if(s->next == nil)
+ runtime·throw("can't preserve unlinked span");
+ runtime·atomicstore(&s->sweepgen, runtime·mheap.sweepgen);
+ return false;
+ }
+
+ runtime·lock(&c->lock);
// Move to nonempty if necessary.
- if(s->freelist == nil) {
+ if(wasempty) {
runtime·MSpanList_Remove(s);
runtime·MSpanList_Insert(&c->nonempty, s);
}
- // Add the objects back to s's free list.
- end->next = s->freelist;
- s->freelist = start;
- s->ref -= n;
-
// delay updating sweepgen until here. This is the signal that
// the span may be used in an MCache, so it must come after the
// linked list operations above (actually, just after the
@@ -150,7 +168,7 @@ runtime·MCentral_FreeSpan(MCentral *c, MSpan *s, int32 n, MLink *start, MLink *
runtime·atomicstore(&s->sweepgen, runtime·mheap.sweepgen);
if(s->ref != 0) {
- runtime·unlock(c);
+ runtime·unlock(&c->lock);
return false;
}
@@ -158,15 +176,14 @@ runtime·MCentral_FreeSpan(MCentral *c, MSpan *s, int32 n, MLink *start, MLink *
runtime·MSpanList_Remove(s);
s->needzero = 1;
s->freelist = nil;
- runtime·unlock(c);
+ runtime·unlock(&c->lock);
runtime·unmarkspan((byte*)(s->start<<PageShift), s->npages<<PageShift);
runtime·MHeap_Free(&runtime·mheap, s, 0);
return true;
}
-// Fetch a new span from the heap and
-// carve into objects for the free list.
-static bool
+// Fetch a new span from the heap and carve into objects for the free list.
+static MSpan*
MCentral_Grow(MCentral *c)
{
uintptr size, npages, i, n;
@@ -174,16 +191,12 @@ MCentral_Grow(MCentral *c)
byte *p;
MSpan *s;
- runtime·unlock(c);
npages = runtime·class_to_allocnpages[c->sizeclass];
size = runtime·class_to_size[c->sizeclass];
n = (npages << PageShift) / size;
s = runtime·MHeap_Alloc(&runtime·mheap, npages, c->sizeclass, 0, 1);
- if(s == nil) {
- // TODO(rsc): Log out of memory
- runtime·lock(c);
- return false;
- }
+ if(s == nil)
+ return nil;
// Carve span into sequence of blocks.
tailp = &s->freelist;
@@ -197,8 +210,5 @@ MCentral_Grow(MCentral *c)
}
*tailp = nil;
runtime·markspan((byte*)(s->start<<PageShift), size, n, size*n < (s->npages<<PageShift));
-
- runtime·lock(c);
- runtime·MSpanList_Insert(&c->nonempty, s);
- return true;
+ return s;
}
diff --git a/src/pkg/runtime/mem_darwin.c b/src/pkg/runtime/mem_darwin.c
index 878c4e1c55..bf3ede5778 100644
--- a/src/pkg/runtime/mem_darwin.c
+++ b/src/pkg/runtime/mem_darwin.c
@@ -7,9 +7,11 @@
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
#include "malloc.h"
+#include "textflag.h"
+#pragma textflag NOSPLIT
void*
-runtime·SysAlloc(uintptr n, uint64 *stat)
+runtime·sysAlloc(uintptr n, uint64 *stat)
{
void *v;
diff --git a/src/pkg/runtime/mem_dragonfly.c b/src/pkg/runtime/mem_dragonfly.c
index c270332cb9..11457b2c03 100644
--- a/src/pkg/runtime/mem_dragonfly.c
+++ b/src/pkg/runtime/mem_dragonfly.c
@@ -7,14 +7,16 @@
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
#include "malloc.h"
+#include "textflag.h"
enum
{
ENOMEM = 12,
};
+#pragma textflag NOSPLIT
void*
-runtime·SysAlloc(uintptr n, uint64 *stat)
+runtime·sysAlloc(uintptr n, uint64 *stat)
{
void *v;
diff --git a/src/pkg/runtime/mem_freebsd.c b/src/pkg/runtime/mem_freebsd.c
index 586947a2dc..18a9a2f5b3 100644
--- a/src/pkg/runtime/mem_freebsd.c
+++ b/src/pkg/runtime/mem_freebsd.c
@@ -7,14 +7,16 @@
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
#include "malloc.h"
+#include "textflag.h"
enum
{
ENOMEM = 12,
};
+#pragma textflag NOSPLIT
void*
-runtime·SysAlloc(uintptr n, uint64 *stat)
+runtime·sysAlloc(uintptr n, uint64 *stat)
{
void *v;
diff --git a/src/pkg/runtime/mem_linux.c b/src/pkg/runtime/mem_linux.c
index 30594c5282..52e02b34e8 100644
--- a/src/pkg/runtime/mem_linux.c
+++ b/src/pkg/runtime/mem_linux.c
@@ -7,6 +7,7 @@
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
#include "malloc.h"
+#include "textflag.h"
enum
{
@@ -61,8 +62,9 @@ mmap_fixed(byte *v, uintptr n, int32 prot, int32 flags, int32 fd, uint32 offset)
return p;
}
+#pragma textflag NOSPLIT
void*
-runtime·SysAlloc(uintptr n, uint64 *stat)
+runtime·sysAlloc(uintptr n, uint64 *stat)
{
void *p;
diff --git a/src/pkg/runtime/mem_nacl.c b/src/pkg/runtime/mem_nacl.c
index e2bca40a49..6c836f18a7 100644
--- a/src/pkg/runtime/mem_nacl.c
+++ b/src/pkg/runtime/mem_nacl.c
@@ -7,26 +7,28 @@
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
#include "malloc.h"
+#include "textflag.h"
enum
{
Debug = 0,
};
+#pragma textflag NOSPLIT
void*
-runtime·SysAlloc(uintptr n, uint64 *stat)
+runtime·sysAlloc(uintptr n, uint64 *stat)
{
void *v;
v = runtime·mmap(nil, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
if(v < (void*)4096) {
if(Debug)
- runtime·printf("SysAlloc(%p): %p\n", n, v);
+ runtime·printf("sysAlloc(%p): %p\n", n, v);
return nil;
}
runtime·xadd64(stat, n);
if(Debug)
- runtime·printf("SysAlloc(%p) = %p\n", n, v);
+ runtime·printf("sysAlloc(%p) = %p\n", n, v);
return v;
}
diff --git a/src/pkg/runtime/mem_netbsd.c b/src/pkg/runtime/mem_netbsd.c
index 861ae90c7e..31820e5170 100644
--- a/src/pkg/runtime/mem_netbsd.c
+++ b/src/pkg/runtime/mem_netbsd.c
@@ -7,14 +7,16 @@
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
#include "malloc.h"
+#include "textflag.h"
enum
{
ENOMEM = 12,
};
+#pragma textflag NOSPLIT
void*
-runtime·SysAlloc(uintptr n, uint64 *stat)
+runtime·sysAlloc(uintptr n, uint64 *stat)
{
void *v;
diff --git a/src/pkg/runtime/mem_openbsd.c b/src/pkg/runtime/mem_openbsd.c
index 861ae90c7e..31820e5170 100644
--- a/src/pkg/runtime/mem_openbsd.c
+++ b/src/pkg/runtime/mem_openbsd.c
@@ -7,14 +7,16 @@
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
#include "malloc.h"
+#include "textflag.h"
enum
{
ENOMEM = 12,
};
+#pragma textflag NOSPLIT
void*
-runtime·SysAlloc(uintptr n, uint64 *stat)
+runtime·sysAlloc(uintptr n, uint64 *stat)
{
void *v;
diff --git a/src/pkg/runtime/mem_plan9.c b/src/pkg/runtime/mem_plan9.c
index bbf04c7eda..402869f393 100644
--- a/src/pkg/runtime/mem_plan9.c
+++ b/src/pkg/runtime/mem_plan9.c
@@ -7,18 +7,19 @@
#include "arch_GOARCH.h"
#include "malloc.h"
#include "os_GOOS.h"
+#include "textflag.h"
-extern byte end[];
-static byte *bloc = { end };
-static Lock memlock;
+extern byte runtime·end[];
+static byte *bloc = { runtime·end };
+static Mutex memlock;
enum
{
Round = PAGESIZE-1
};
-void*
-runtime·SysAlloc(uintptr nbytes, uint64 *stat)
+static void*
+brk(uintptr nbytes)
{
uintptr bl;
@@ -31,8 +32,42 @@ runtime·SysAlloc(uintptr nbytes, uint64 *stat)
}
bloc = (byte*)bl + nbytes;
runtime·unlock(&memlock);
- runtime·xadd64(stat, nbytes);
- return (void*)bl;
+ return (void*)bl;
+}
+
+static void
+sysalloc(void)
+{
+ uintptr nbytes;
+ uint64 *stat;
+ void *p;
+
+ nbytes = g->m->scalararg[0];
+ stat = g->m->ptrarg[0];
+ g->m->scalararg[0] = 0;
+ g->m->ptrarg[0] = nil;
+
+ p = brk(nbytes);
+ if(p != nil)
+ runtime·xadd64(stat, nbytes);
+
+ g->m->ptrarg[0] = p;
+}
+
+#pragma textflag NOSPLIT
+void*
+runtime·sysAlloc(uintptr nbytes, uint64 *stat)
+{
+ void (*fn)(void);
+ void *p;
+
+ g->m->scalararg[0] = nbytes;
+ g->m->ptrarg[0] = stat;
+ fn = sysalloc;
+ runtime·onM(&fn);
+ p = g->m->ptrarg[0];
+ g->m->ptrarg[0] = nil;
+ return p;
}
void
@@ -42,7 +77,7 @@ runtime·SysFree(void *v, uintptr nbytes, uint64 *stat)
runtime·lock(&memlock);
// from tiny/mem.c
// Push pointer back if this is a free
- // of the most recent SysAlloc.
+ // of the most recent sysAlloc.
nbytes += (nbytes + Round) & ~Round;
if(bloc == (byte*)v+nbytes)
bloc -= nbytes;
@@ -64,7 +99,10 @@ runtime·SysUsed(void *v, uintptr nbytes)
void
runtime·SysMap(void *v, uintptr nbytes, bool reserved, uint64 *stat)
{
- USED(v, nbytes, reserved, stat);
+ // SysReserve has already allocated all heap memory,
+ // but has not adjusted stats.
+ USED(v, reserved);
+ runtime·xadd64(stat, nbytes);
}
void
@@ -78,5 +116,5 @@ runtime·SysReserve(void *v, uintptr nbytes, bool *reserved)
{
USED(v);
*reserved = true;
- return runtime·SysAlloc(nbytes, &mstats.heap_sys);
+ return brk(nbytes);
}
diff --git a/src/pkg/runtime/mem_solaris.c b/src/pkg/runtime/mem_solaris.c
index 034222887b..8e90ba1d9e 100644
--- a/src/pkg/runtime/mem_solaris.c
+++ b/src/pkg/runtime/mem_solaris.c
@@ -7,14 +7,16 @@
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
#include "malloc.h"
+#include "textflag.h"
enum
{
ENOMEM = 12,
};
+#pragma textflag NOSPLIT
void*
-runtime·SysAlloc(uintptr n, uint64 *stat)
+runtime·sysAlloc(uintptr n, uint64 *stat)
{
void *v;
diff --git a/src/pkg/runtime/mem_windows.c b/src/pkg/runtime/mem_windows.c
index 77ec6e9262..7bc028bf3a 100644
--- a/src/pkg/runtime/mem_windows.c
+++ b/src/pkg/runtime/mem_windows.c
@@ -7,6 +7,7 @@
#include "os_GOOS.h"
#include "defs_GOOS_GOARCH.h"
#include "malloc.h"
+#include "textflag.h"
enum {
MEM_COMMIT = 0x1000,
@@ -25,11 +26,12 @@ extern void *runtime·VirtualAlloc;
extern void *runtime·VirtualFree;
extern void *runtime·VirtualProtect;
+#pragma textflag NOSPLIT
void*
-runtime·SysAlloc(uintptr n, uint64 *stat)
+runtime·sysAlloc(uintptr n, uint64 *stat)
{
runtime·xadd64(stat, n);
- return runtime·stdcall(runtime·VirtualAlloc, 4, nil, n, (uintptr)(MEM_COMMIT|MEM_RESERVE), (uintptr)PAGE_READWRITE);
+ return runtime·stdcall4(runtime·VirtualAlloc, 0, n, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
}
void
@@ -38,7 +40,7 @@ runtime·SysUnused(void *v, uintptr n)
void *r;
uintptr small;
- r = runtime·stdcall(runtime·VirtualFree, 3, v, n, (uintptr)MEM_DECOMMIT);
+ r = runtime·stdcall3(runtime·VirtualFree, (uintptr)v, n, MEM_DECOMMIT);
if(r != nil)
return;
@@ -53,7 +55,7 @@ runtime·SysUnused(void *v, uintptr n)
// in the worst case, but that's fast enough.
while(n > 0) {
small = n;
- while(small >= 4096 && runtime·stdcall(runtime·VirtualFree, 3, v, small, (uintptr)MEM_DECOMMIT) == nil)
+ while(small >= 4096 && runtime·stdcall3(runtime·VirtualFree, (uintptr)v, small, MEM_DECOMMIT) == nil)
small = (small / 2) & ~(4096-1);
if(small < 4096)
runtime·throw("runtime: failed to decommit pages");
@@ -67,7 +69,7 @@ runtime·SysUsed(void *v, uintptr n)
{
void *r;
- r = runtime·stdcall(runtime·VirtualAlloc, 4, v, n, (uintptr)MEM_COMMIT, (uintptr)PAGE_READWRITE);
+ r = runtime·stdcall4(runtime·VirtualAlloc, (uintptr)v, n, MEM_COMMIT, PAGE_READWRITE);
if(r != v)
runtime·throw("runtime: failed to commit pages");
}
@@ -78,7 +80,7 @@ runtime·SysFree(void *v, uintptr n, uint64 *stat)
uintptr r;
runtime·xadd64(stat, -(uint64)n);
- r = (uintptr)runtime·stdcall(runtime·VirtualFree, 3, v, (uintptr)0, (uintptr)MEM_RELEASE);
+ r = (uintptr)runtime·stdcall3(runtime·VirtualFree, (uintptr)v, 0, MEM_RELEASE);
if(r == 0)
runtime·throw("runtime: failed to release pages");
}
@@ -96,12 +98,12 @@ runtime·SysReserve(void *v, uintptr n, bool *reserved)
*reserved = true;
// v is just a hint.
// First try at v.
- v = runtime·stdcall(runtime·VirtualAlloc, 4, v, n, (uintptr)MEM_RESERVE, (uintptr)PAGE_READWRITE);
+ v = runtime·stdcall4(runtime·VirtualAlloc, (uintptr)v, n, MEM_RESERVE, PAGE_READWRITE);
if(v != nil)
return v;
// Next let the kernel choose the address.
- return runtime·stdcall(runtime·VirtualAlloc, 4, nil, n, (uintptr)MEM_RESERVE, (uintptr)PAGE_READWRITE);
+ return runtime·stdcall4(runtime·VirtualAlloc, 0, n, MEM_RESERVE, PAGE_READWRITE);
}
void
@@ -112,7 +114,7 @@ runtime·SysMap(void *v, uintptr n, bool reserved, uint64 *stat)
USED(reserved);
runtime·xadd64(stat, n);
- p = runtime·stdcall(runtime·VirtualAlloc, 4, v, n, (uintptr)MEM_COMMIT, (uintptr)PAGE_READWRITE);
+ p = runtime·stdcall4(runtime·VirtualAlloc, (uintptr)v, n, MEM_COMMIT, PAGE_READWRITE);
if(p != v)
runtime·throw("runtime: cannot map pages in arena address space");
}
diff --git a/src/pkg/runtime/memclr_386.s b/src/pkg/runtime/memclr_386.s
index aeabad413e..8b163923e9 100644
--- a/src/pkg/runtime/memclr_386.s
+++ b/src/pkg/runtime/memclr_386.s
@@ -4,7 +4,7 @@
// +build !plan9
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// void runtime·memclr(void*, uintptr)
TEXT runtime·memclr(SB), NOSPLIT, $0-8
diff --git a/src/pkg/runtime/memclr_amd64.s b/src/pkg/runtime/memclr_amd64.s
index 01c6a415c0..35b3b4beef 100644
--- a/src/pkg/runtime/memclr_amd64.s
+++ b/src/pkg/runtime/memclr_amd64.s
@@ -4,7 +4,7 @@
// +build !plan9
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// void runtime·memclr(void*, uintptr)
TEXT runtime·memclr(SB), NOSPLIT, $0-16
diff --git a/src/pkg/runtime/memclr_arm.s b/src/pkg/runtime/memclr_arm.s
index b19ea72a3f..1824d33b14 100644
--- a/src/pkg/runtime/memclr_arm.s
+++ b/src/pkg/runtime/memclr_arm.s
@@ -23,7 +23,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TO = 8
TOE = 11
diff --git a/src/pkg/runtime/memclr_plan9_386.s b/src/pkg/runtime/memclr_plan9_386.s
index ed5dbbd3c6..b4b671f773 100644
--- a/src/pkg/runtime/memclr_plan9_386.s
+++ b/src/pkg/runtime/memclr_plan9_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// void runtime·memclr(void*, uintptr)
TEXT runtime·memclr(SB), NOSPLIT, $0-8
diff --git a/src/pkg/runtime/memclr_plan9_amd64.s b/src/pkg/runtime/memclr_plan9_amd64.s
index 1fabcd5f09..37e61dfbcc 100644
--- a/src/pkg/runtime/memclr_plan9_amd64.s
+++ b/src/pkg/runtime/memclr_plan9_amd64.s
@@ -2,12 +2,12 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// void runtime·memclr(void*, uintptr)
TEXT runtime·memclr(SB),NOSPLIT,$0-16
- MOVQ addr+0(FP), DI
- MOVQ count+8(FP), CX
+ MOVQ ptr+0(FP), DI
+ MOVQ n+8(FP), CX
MOVQ CX, BX
ANDQ $7, BX
SHRQ $3, CX
diff --git a/src/pkg/runtime/memmove_386.s b/src/pkg/runtime/memmove_386.s
index 51b08d56d3..4c0c74c1af 100644
--- a/src/pkg/runtime/memmove_386.s
+++ b/src/pkg/runtime/memmove_386.s
@@ -25,7 +25,7 @@
// +build !plan9
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT runtime·memmove(SB), NOSPLIT, $0-12
MOVL to+0(FP), DI
diff --git a/src/pkg/runtime/memmove_amd64.s b/src/pkg/runtime/memmove_amd64.s
index 339c5dd410..f968435340 100644
--- a/src/pkg/runtime/memmove_amd64.s
+++ b/src/pkg/runtime/memmove_amd64.s
@@ -25,7 +25,7 @@
// +build !plan9
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// void runtime·memmove(void*, void*, uintptr)
TEXT runtime·memmove(SB), NOSPLIT, $0-24
diff --git a/src/pkg/runtime/memmove_arm.s b/src/pkg/runtime/memmove_arm.s
index 9701dc99f2..f187d42678 100644
--- a/src/pkg/runtime/memmove_arm.s
+++ b/src/pkg/runtime/memmove_arm.s
@@ -23,7 +23,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// TE or TS are spilled to the stack during bulk register moves.
TS = 0
diff --git a/src/pkg/runtime/memmove_nacl_amd64p32.s b/src/pkg/runtime/memmove_nacl_amd64p32.s
index 1b5733112c..373607afec 100644
--- a/src/pkg/runtime/memmove_nacl_amd64p32.s
+++ b/src/pkg/runtime/memmove_nacl_amd64p32.s
@@ -2,11 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT runtime·memmove(SB), NOSPLIT, $0-12
MOVL to+0(FP), DI
- MOVL fr+4(FP), SI
+ MOVL from+4(FP), SI
MOVL n+8(FP), BX
CMPL SI, DI
diff --git a/src/pkg/runtime/memmove_plan9_386.s b/src/pkg/runtime/memmove_plan9_386.s
index 5ac5c27d40..025d4ce1bf 100644
--- a/src/pkg/runtime/memmove_plan9_386.s
+++ b/src/pkg/runtime/memmove_plan9_386.s
@@ -23,11 +23,11 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT runtime·memmove(SB), NOSPLIT, $0-12
MOVL to+0(FP), DI
- MOVL fr+4(FP), SI
+ MOVL from+4(FP), SI
MOVL n+8(FP), BX
// REP instructions have a high startup cost, so we handle small sizes
diff --git a/src/pkg/runtime/memmove_plan9_amd64.s b/src/pkg/runtime/memmove_plan9_amd64.s
index 3664e45ae2..8e96b87175 100644
--- a/src/pkg/runtime/memmove_plan9_amd64.s
+++ b/src/pkg/runtime/memmove_plan9_amd64.s
@@ -23,13 +23,13 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// void runtime·memmove(void*, void*, uintptr)
TEXT runtime·memmove(SB), NOSPLIT, $0-24
MOVQ to+0(FP), DI
- MOVQ fr+8(FP), SI
+ MOVQ from+8(FP), SI
MOVQ n+16(FP), BX
// REP instructions have a high startup cost, so we handle small sizes
diff --git a/src/pkg/runtime/mgc0.c b/src/pkg/runtime/mgc0.c
index b1a8943115..7ec9640eb6 100644
--- a/src/pkg/runtime/mgc0.c
+++ b/src/pkg/runtime/mgc0.c
@@ -60,7 +60,7 @@
#include "type.h"
#include "typekind.h"
#include "funcdata.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
enum {
Debug = 0,
@@ -84,42 +84,9 @@ enum {
};
#define ScanConservatively ((byte*)1)
-#define GcpercentUnknown (-2)
// Initialized from $GOGC. GOGC=off means no gc.
-extern int32 runtime·gcpercent = GcpercentUnknown;
-
-static FuncVal* poolcleanup;
-
-void
-sync·runtime_registerPoolCleanup(FuncVal *f)
-{
- poolcleanup = f;
-}
-
-void
-runtime·clearpools(void)
-{
- P *p, **pp;
- MCache *c;
- int32 i;
-
- // clear sync.Pool's
- if(poolcleanup != nil)
- reflect·call(poolcleanup, nil, 0, 0);
-
- for(pp=runtime·allp; p=*pp; pp++) {
- // clear tinyalloc pool
- c = p->mcache;
- if(c != nil) {
- c->tiny = nil;
- c->tinysize = 0;
- }
- // clear defer pools
- for(i=0; i<nelem(p->deferpool); i++)
- p->deferpool[i] = nil;
- }
-}
+extern int32 runtime·gcpercent;
// Holding worldsema grants an M the right to try to stop the world.
// The procedure is:
@@ -144,44 +111,27 @@ struct Workbuf
byte* obj[(WorkbufSize-sizeof(LFNode)-sizeof(uintptr))/PtrSize];
};
-typedef struct Finalizer Finalizer;
-struct Finalizer
-{
- FuncVal *fn;
- void *arg;
- uintptr nret;
- Type *fint;
- PtrType *ot;
-};
-
-typedef struct FinBlock FinBlock;
-struct FinBlock
-{
- FinBlock *alllink;
- FinBlock *next;
- int32 cnt;
- int32 cap;
- Finalizer fin[1];
-};
-
-extern byte data[];
-extern byte edata[];
-extern byte bss[];
-extern byte ebss[];
+extern byte runtime·data[];
+extern byte runtime·edata[];
+extern byte runtime·bss[];
+extern byte runtime·ebss[];
-extern byte gcdata[];
-extern byte gcbss[];
+extern byte runtime·gcdata[];
+extern byte runtime·gcbss[];
-static Lock finlock; // protects the following variables
-static FinBlock *finq; // list of finalizers that are to be executed
-static FinBlock *finc; // cache of free blocks
-static FinBlock *allfin; // list of all blocks
+Mutex runtime·finlock; // protects the following variables
+G* runtime·fing; // goroutine that runs finalizers
+FinBlock* runtime·finq; // list of finalizers that are to be executed
+FinBlock* runtime·finc; // cache of free blocks
bool runtime·fingwait;
bool runtime·fingwake;
+static FinBlock *allfin; // list of all blocks
+
+BitVector runtime·gcdatamask;
+BitVector runtime·gcbssmask;
-static Lock gclock;
+static Mutex gclock;
-static void runfinq(void);
static void bgsweep(void);
static Workbuf* getempty(Workbuf*);
static Workbuf* getfull(Workbuf*);
@@ -191,9 +141,8 @@ static void gchelperstart(void);
static void flushallmcaches(void);
static bool scanframe(Stkframe *frame, void *unused);
static void scanstack(G *gp);
-static byte* unrollglobgcprog(byte *prog, uintptr size);
+static BitVector unrollglobgcprog(byte *prog, uintptr size);
-static FuncVal runfinqv = {runfinq};
static FuncVal bgsweepv = {bgsweep};
static struct {
@@ -206,8 +155,10 @@ static struct {
volatile uint32 ndone;
Note alldone;
ParFor* markfor;
- byte* gcdata;
- byte* gcbss;
+
+ // Copy of mheap.allspans for marker or sweeper.
+ MSpan** spans;
+ uint32 nspan;
} work;
// scanblock scans a block of n bytes starting at pointer b for references
@@ -219,17 +170,15 @@ static struct {
static void
scanblock(byte *b, uintptr n, byte *ptrmask)
{
- byte *obj, *p, *arena_start, *arena_used, **wp, *scanbuf[8];
- uintptr i, nobj, size, idx, *bitp, bits, xbits, shift, x, off, cached, scanbufpos;
+ byte *obj, *p, *arena_start, *arena_used, **wp, *scanbuf[8], *ptrbitp, *bitp, bits, xbits, shift, cached;
+ uintptr i, nobj, size, idx, x, off, scanbufpos;
intptr ncached;
Workbuf *wbuf;
- String *str;
- Slice *slice;
Iface *iface;
Eface *eface;
Type *typ;
MSpan *s;
- PageID k;
+ pageID k;
bool keepworking;
// Cache memory arena parameters in local vars.
@@ -244,6 +193,10 @@ scanblock(byte *b, uintptr n, byte *ptrmask)
for(i = 0; i < nelem(scanbuf); i++)
scanbuf[i] = nil;
+ ptrbitp = nil;
+ cached = 0;
+ ncached = 0;
+
// ptrmask can have 3 possible values:
// 1. nil - obtain pointer mask from GC bitmap.
// 2. ScanConservatively - don't use any mask, scan conservatively.
@@ -302,8 +255,15 @@ scanblock(byte *b, uintptr n, byte *ptrmask)
}
ptrmask = ScanConservatively;
}
- cached = 0;
- ncached = 0;
+ // Find bits of the beginning of the object.
+ if(ptrmask == nil) {
+ off = (uintptr*)b - (uintptr*)arena_start;
+ ptrbitp = arena_start - off/wordsPerBitmapByte - 1;
+ shift = (off % wordsPerBitmapByte) * gcBits;
+ cached = *ptrbitp >> shift;
+ cached &= ~bitBoundary;
+ ncached = (8 - shift)/gcBits;
+ }
for(i = 0; i < n; i += PtrSize) {
obj = nil;
// Find bits for this word.
@@ -315,16 +275,13 @@ scanblock(byte *b, uintptr n, byte *ptrmask)
// Consult GC bitmap.
if(ncached <= 0) {
// Refill cache.
- off = (uintptr*)(b+i) - (uintptr*)arena_start;
- bitp = (uintptr*)arena_start - off/wordsPerBitmapWord - 1;
- shift = (off % wordsPerBitmapWord) * gcBits;
- cached = *bitp >> shift;
- ncached = (PtrSize*8 - shift)/gcBits;
+ cached = *--ptrbitp;
+ ncached = 2;
}
bits = cached;
cached >>= gcBits;
ncached--;
- if(i != 0 && (bits&bitMask) != bitMiddle)
+ if((bits&bitBoundary) != 0)
break; // reached beginning of the next object
bits = (bits>>2)&BitsMask;
if(bits == BitsDead)
@@ -340,41 +297,29 @@ scanblock(byte *b, uintptr n, byte *ptrmask)
obj = *(byte**)(b+i);
goto markobj;
}
+
+ // With those three out of the way, must be multi-word.
+ if(bits != BitsMultiWord)
+ runtime·throw("unexpected garbage collection bits");
// Find the next pair of bits.
if(ptrmask == nil) {
if(ncached <= 0) {
- off = (uintptr*)(b+i+PtrSize) - (uintptr*)arena_start;
- bitp = (uintptr*)arena_start - off/wordsPerBitmapWord - 1;
- shift = (off % wordsPerBitmapWord) * gcBits;
- cached = *bitp >> shift;
- ncached = (PtrSize*8 - shift)/gcBits;
+ // Refill cache.
+ cached = *--ptrbitp;
+ ncached = 2;
}
bits = (cached>>2)&BitsMask;
} else
bits = (ptrmask[((i+PtrSize)/PtrSize)/4]>>((((i+PtrSize)/PtrSize)%4)*BitsPerPointer))&BitsMask;
switch(bits) {
- case BitsString:
- str = (String*)(b+i);
- if(str->len > 0)
- obj = str->str;
- break;
- case BitsSlice:
- slice = (Slice*)(b+i);
- if(Debug && slice->cap < slice->len) {
- g->m->traceback = 2;
- runtime·printf("bad slice in object %p: %p/%p/%p\n",
- b, slice->array, slice->len, slice->cap);
- runtime·throw("bad slice in heap object");
- }
- if(slice->cap > 0)
- obj = slice->array;
- break;
+ default:
+ runtime·throw("unexpected garbage collection bits");
case BitsIface:
iface = (Iface*)(b+i);
if(iface->tab != nil) {
typ = iface->tab->type;
- if(typ->size > PtrSize || !(typ->kind&KindNoPointers))
+ if(!(typ->kind&KindDirectIface) || !(typ->kind&KindNoPointers))
obj = iface->data;
}
break;
@@ -382,21 +327,15 @@ scanblock(byte *b, uintptr n, byte *ptrmask)
eface = (Eface*)(b+i);
typ = eface->type;
if(typ != nil) {
- if(typ->size > PtrSize || !(typ->kind&KindNoPointers))
+ if(!(typ->kind&KindDirectIface) || !(typ->kind&KindNoPointers))
obj = eface->data;
}
break;
}
- if(bits == BitsSlice) {
- i += 2*PtrSize;
- cached >>= 2*gcBits;
- ncached -= 2;
- } else {
- i += PtrSize;
- cached >>= gcBits;
- ncached--;
- }
+ i += PtrSize;
+ cached >>= gcBits;
+ ncached--;
markobj:
// At this point we have extracted the next potential pointer.
@@ -405,20 +344,12 @@ scanblock(byte *b, uintptr n, byte *ptrmask)
continue;
// Mark the object.
off = (uintptr*)obj - (uintptr*)arena_start;
- bitp = (uintptr*)arena_start - off/wordsPerBitmapWord - 1;
- shift = (off % wordsPerBitmapWord) * gcBits;
+ bitp = arena_start - off/wordsPerBitmapByte - 1;
+ shift = (off % wordsPerBitmapByte) * gcBits;
xbits = *bitp;
bits = (xbits >> shift) & bitMask;
- if(bits == bitMiddle) {
- // Not a beginning of a block, check if we have block boundary in xbits.
- while(shift > 0) {
- obj -= PtrSize;
- shift -= gcBits;
- bits = (xbits >> shift) & bitMask;
- if(bits != bitMiddle)
- goto havebits;
- }
- // Otherwise consult span table to find the block beginning.
+ if((bits&bitBoundary) == 0) {
+ // Not a beginning of a block, consult span table to find the block beginning.
k = (uintptr)obj>>PageShift;
x = k;
x -= (uintptr)arena_start>>PageShift;
@@ -432,33 +363,35 @@ scanblock(byte *b, uintptr n, byte *ptrmask)
p = p+idx*size;
}
if(p == obj) {
- runtime·printf("runtime: failed to find block beginning for %p s->limit=%p\n", p, s->limit);
+ runtime·printf("runtime: failed to find block beginning for %p s=%p s->limit=%p\n",
+ p, s->start*PageSize, s->limit);
runtime·throw("failed to find block beginning");
}
obj = p;
goto markobj;
}
- havebits:
// Now we have bits, bitp, and shift correct for
// obj pointing at the base of the object.
- // Only care about allocated and not marked.
- if(bits != bitAllocated)
+ // Only care about not marked objects.
+ if((bits&bitMarked) != 0)
continue;
- if(work.nproc == 1)
- *bitp |= bitMarked<<shift;
- else {
- for(;;) {
- xbits = *bitp;
- bits = (xbits>>shift) & bitMask;
- if(bits != bitAllocated)
- break;
- if(runtime·casp((void**)bitp, (void*)xbits, (void*)(xbits|(bitMarked<<shift))))
- break;
- }
- if(bits != bitAllocated)
- continue;
- }
+ // If obj size is greater than 8, then each byte of GC bitmap
+ // contains info for at most one object. In such case we use
+ // non-atomic byte store to mark the object. This can lead
+ // to double enqueue of the object for scanning, but scanning
+ // is an idempotent operation, so it is OK. This cannot lead
+ // to bitmap corruption because the single marked bit is the
+ // only thing that can change in the byte.
+ // For 8-byte objects we use non-atomic store, if the other
+ // quadruple is already marked. Otherwise we resort to CAS
+ // loop for marking.
+ if((xbits&(bitMask|(bitMask<<gcBits))) != (bitBoundary|(bitBoundary<<gcBits)) ||
+ work.nproc == 1)
+ *bitp = xbits | (bitMarked<<shift);
+ else
+ runtime·atomicor8(bitp, bitMarked<<shift);
+
if(((xbits>>(shift+2))&BitsMask) == BitsDead)
continue; // noscan object
@@ -500,21 +433,22 @@ static void
markroot(ParFor *desc, uint32 i)
{
FinBlock *fb;
- MHeap *h;
- MSpan **allspans, *s;
+ MSpan *s;
uint32 spanidx, sg;
G *gp;
void *p;
+ uint32 status;
+ bool restart;
USED(&desc);
// Note: if you add a case here, please also update heapdump.c:dumproots.
switch(i) {
case RootData:
- scanblock(data, edata - data, work.gcdata);
+ scanblock(runtime·data, runtime·edata - runtime·data, runtime·gcdatamask.bytedata);
break;
case RootBss:
- scanblock(bss, ebss - bss, work.gcbss);
+ scanblock(runtime·bss, runtime·ebss - runtime·bss, runtime·gcbssmask.bytedata);
break;
case RootFinalizers:
@@ -524,14 +458,12 @@ markroot(ParFor *desc, uint32 i)
case RootSpans:
// mark MSpan.specials
- h = &runtime·mheap;
- sg = h->sweepgen;
- allspans = h->allspans;
- for(spanidx=0; spanidx<runtime·mheap.nspan; spanidx++) {
+ sg = runtime·mheap.sweepgen;
+ for(spanidx=0; spanidx<work.nspan; spanidx++) {
Special *sp;
SpecialFinalizer *spf;
- s = allspans[spanidx];
+ s = work.spans[spanidx];
if(s->state != MSpanInUse)
continue;
if(s->sweepgen != sg) {
@@ -545,7 +477,7 @@ markroot(ParFor *desc, uint32 i)
// retain everything it points to.
spf = (SpecialFinalizer*)sp;
// A finalizer can be set for an inner byte of an object, find object beginning.
- p = (void*)((s->start << PageShift) + spf->offset/s->elemsize*s->elemsize);
+ p = (void*)((s->start << PageShift) + spf->special.offset/s->elemsize*s->elemsize);
scanblock(p, s->elemsize, nil);
scanblock((void*)&spf->fn, PtrSize, ScanConservatively);
}
@@ -563,11 +495,20 @@ markroot(ParFor *desc, uint32 i)
gp = runtime·allg[i - RootCount];
// remember when we've first observed the G blocked
// needed only to output in traceback
- if((gp->status == Gwaiting || gp->status == Gsyscall) && gp->waitsince == 0)
+ status = runtime·readgstatus(gp);
+ if((status == Gwaiting || status == Gsyscall) && gp->waitsince == 0)
gp->waitsince = work.tstart;
+ // Shrink a stack if not much of it is being used.
+ runtime·shrinkstack(gp);
+ if(runtime·readgstatus(gp) == Gdead)
+ gp->gcworkdone = true;
+ else
+ gp->gcworkdone = false;
+ restart = runtime·stopg(gp);
scanstack(gp);
+ if(restart)
+ runtime·restartg(gp);
break;
-
}
}
@@ -713,16 +654,16 @@ scanframe(Stkframe *frame, void *unused)
stackmap = runtime·funcdata(f, FUNCDATA_LocalsPointerMaps);
if(stackmap == nil) {
// No locals information, scan everything.
- size = frame->varp - (byte*)frame->sp;
+ size = frame->varp - frame->sp;
if(Debug > 2)
- runtime·printf("frame %s unsized locals %p+%p\n", runtime·funcname(f), frame->varp-size, size);
- scanblock(frame->varp - size, size, ScanConservatively);
+ runtime·printf("frame %s unsized locals %p+%p\n", runtime·funcname(f), (byte*)(frame->varp-size), size);
+ scanblock((byte*)(frame->varp - size), size, ScanConservatively);
} else if(stackmap->n < 0) {
// Locals size information, scan just the locals.
size = -stackmap->n;
if(Debug > 2)
- runtime·printf("frame %s conservative locals %p+%p\n", runtime·funcname(f), frame->varp-size, size);
- scanblock(frame->varp - size, size, ScanConservatively);
+ runtime·printf("frame %s conservative locals %p+%p\n", runtime·funcname(f), (byte*)(frame->varp-size), size);
+ scanblock((byte*)(frame->varp - size), size, ScanConservatively);
} else if(stackmap->n > 0) {
// Locals bitmap information, scan just the pointers in locals.
if(pcdata < 0 || pcdata >= stackmap->n) {
@@ -733,7 +674,7 @@ scanframe(Stkframe *frame, void *unused)
}
bv = runtime·stackmapdata(stackmap, pcdata);
size = (bv.n * PtrSize) / BitsPerPointer;
- scanblock(frame->varp - size, bv.n/BitsPerPointer*PtrSize, (byte*)bv.bytedata);
+ scanblock((byte*)frame->varp - size, bv.n/BitsPerPointer*PtrSize, (byte*)bv.bytedata);
}
// Scan arguments.
@@ -741,11 +682,11 @@ scanframe(Stkframe *frame, void *unused)
stackmap = runtime·funcdata(f, FUNCDATA_ArgsPointerMaps);
if(stackmap != nil) {
bv = runtime·stackmapdata(stackmap, pcdata);
- scanblock(frame->argp, bv.n/BitsPerPointer*PtrSize, (byte*)bv.bytedata);
+ scanblock((byte*)frame->argp, bv.n/BitsPerPointer*PtrSize, (byte*)bv.bytedata);
} else {
if(Debug > 2)
runtime·printf("frame %s conservative args %p+%p\n", runtime·funcname(f), frame->argp, (uintptr)frame->arglen);
- scanblock(frame->argp, frame->arglen, ScanConservatively);
+ scanblock((byte*)frame->argp, frame->arglen, ScanConservatively);
}
return true;
}
@@ -757,14 +698,21 @@ scanstack(G *gp)
int32 n;
Stktop *stk;
uintptr sp, guard;
+ bool (*fn)(Stkframe*, void*);
- switch(gp->status){
+ if(runtime·readgstatus(gp)&Gscan == 0) {
+ runtime·printf("runtime: gp=%p, goid=%D, gp->atomicstatus=%d\n", gp, gp->goid, runtime·readgstatus(gp));
+ runtime·throw("mark - bad status");
+ }
+
+ switch(runtime·readgstatus(gp)&~Gscan) {
default:
- runtime·printf("unexpected G.status %d (goroutine %p %D)\n", gp->status, gp, gp->goid);
+ runtime·printf("runtime: gp=%p, goid=%D, gp->atomicstatus=%d\n", gp, gp->goid, runtime·readgstatus(gp));
runtime·throw("mark - bad status");
case Gdead:
return;
case Grunning:
+ runtime·printf("runtime: gp=%p, goid=%D, gp->atomicstatus=%d\n", gp, gp->goid, runtime·readgstatus(gp));
runtime·throw("mark - world not stopped");
case Grunnable:
case Gsyscall:
@@ -797,7 +745,8 @@ scanstack(G *gp)
USED(sp);
USED(stk);
USED(guard);
- runtime·gentraceback(~(uintptr)0, ~(uintptr)0, 0, gp, 0, nil, 0x7fffffff, scanframe, nil, false);
+ fn = scanframe;
+ runtime·gentraceback(~(uintptr)0, ~(uintptr)0, 0, gp, 0, nil, 0x7fffffff, &fn, nil, false);
} else {
n = 0;
while(stk) {
@@ -816,34 +765,57 @@ scanstack(G *gp)
}
}
+// The gp has been moved to a gc safepoint. If there is gcphase specific
+// work it is done here.
+void
+runtime·gcphasework(G *gp)
+{
+ switch(runtime·gcphase) {
+ default:
+ runtime·throw("gcphasework in bad gcphase");
+ case GCoff:
+ case GCquiesce:
+ case GCstw:
+ case GCsweep:
+ // No work for now.
+ break;
+ case GCmark:
+ // Disabled until concurrent GC is implemented
+ // but indicate the scan has been done.
+ // scanstack(gp);
+ break;
+ }
+ gp->gcworkdone = true;
+}
+
void
runtime·queuefinalizer(byte *p, FuncVal *fn, uintptr nret, Type *fint, PtrType *ot)
{
FinBlock *block;
Finalizer *f;
- runtime·lock(&finlock);
- if(finq == nil || finq->cnt == finq->cap) {
- if(finc == nil) {
- finc = runtime·persistentalloc(FinBlockSize, 0, &mstats.gc_sys);
- finc->cap = (FinBlockSize - sizeof(FinBlock)) / sizeof(Finalizer) + 1;
- finc->alllink = allfin;
- allfin = finc;
+ runtime·lock(&runtime·finlock);
+ if(runtime·finq == nil || runtime·finq->cnt == runtime·finq->cap) {
+ if(runtime·finc == nil) {
+ runtime·finc = runtime·persistentalloc(FinBlockSize, 0, &mstats.gc_sys);
+ runtime·finc->cap = (FinBlockSize - sizeof(FinBlock)) / sizeof(Finalizer) + 1;
+ runtime·finc->alllink = allfin;
+ allfin = runtime·finc;
}
- block = finc;
- finc = block->next;
- block->next = finq;
- finq = block;
+ block = runtime·finc;
+ runtime·finc = block->next;
+ block->next = runtime·finq;
+ runtime·finq = block;
}
- f = &finq->fin[finq->cnt];
- finq->cnt++;
+ f = &runtime·finq->fin[runtime·finq->cnt];
+ runtime·finq->cnt++;
f->fn = fn;
f->nret = nret;
f->fint = fint;
f->ot = ot;
f->arg = p;
runtime·fingwake = true;
- runtime·unlock(&finlock);
+ runtime·unlock(&runtime·finlock);
}
void
@@ -876,27 +848,29 @@ runtime·MSpan_EnsureSwept(MSpan *s)
if(runtime·atomicload(&s->sweepgen) == sg)
return;
if(runtime·cas(&s->sweepgen, sg-2, sg-1)) {
- runtime·MSpan_Sweep(s);
+ runtime·MSpan_Sweep(s, false);
return;
}
// unfortunate condition, and we don't have efficient means to wait
while(runtime·atomicload(&s->sweepgen) != sg)
- runtime·osyield();
+ runtime·osyield();
}
// Sweep frees or collects finalizers for blocks not marked in the mark phase.
// It clears the mark bits in preparation for the next GC round.
// Returns true if the span was returned to heap.
+// If preserve=true, don't return it to heap nor relink in MCentral lists;
+// caller takes care of it.
bool
-runtime·MSpan_Sweep(MSpan *s)
+runtime·MSpan_Sweep(MSpan *s, bool preserve)
{
int32 cl, n, npages, nfree;
- uintptr size, off, *bitp, shift, xbits, bits;
+ uintptr size, off, step;
uint32 sweepgen;
- byte *p;
+ byte *p, *bitp, shift, xbits, bits;
MCache *c;
byte *arena_start;
- MLink head, *end;
+ MLink head, *end, *link;
Special *special, **specialp, *y;
bool res, sweepgenset;
@@ -926,6 +900,14 @@ runtime·MSpan_Sweep(MSpan *s)
c = g->m->mcache;
sweepgenset = false;
+ // Mark any free objects in this span so we don't collect them.
+ for(link = s->freelist; link != nil; link = link->next) {
+ off = (uintptr*)link - (uintptr*)arena_start;
+ bitp = arena_start - off/wordsPerBitmapByte - 1;
+ shift = (off % wordsPerBitmapByte) * gcBits;
+ *bitp |= bitMarked<<shift;
+ }
+
// Unlink & free special records for any objects we're about to free.
specialp = &s->specials;
special = *specialp;
@@ -933,10 +915,10 @@ runtime·MSpan_Sweep(MSpan *s)
// A finalizer can be set for an inner byte of an object, find object beginning.
p = (byte*)(s->start << PageShift) + special->offset/size*size;
off = (uintptr*)p - (uintptr*)arena_start;
- bitp = (uintptr*)arena_start - off/wordsPerBitmapWord - 1;
- shift = (off % wordsPerBitmapWord) * gcBits;
+ bitp = arena_start - off/wordsPerBitmapByte - 1;
+ shift = (off % wordsPerBitmapByte) * gcBits;
bits = (*bitp>>shift) & bitMask;
- if(bits == bitAllocated) {
+ if((bits&bitMarked) == 0) {
// Find the exact byte for which the special was setup
// (as opposed to object beginning).
p = (byte*)(s->start << PageShift) + special->offset;
@@ -950,10 +932,6 @@ runtime·MSpan_Sweep(MSpan *s)
}
} else {
// object is still live: keep special record
- if(bits != bitMarked) {
- runtime·printf("runtime: bad bits for special object %p: %d\n", p, (int32)bits);
- runtime·throw("runtime: bad bits for special object");
- }
specialp = &special->next;
special = *specialp;
}
@@ -963,29 +941,45 @@ runtime·MSpan_Sweep(MSpan *s)
// This thread owns the span now, so it can manipulate
// the block bitmap without atomic operations.
p = (byte*)(s->start << PageShift);
+ // Find bits for the beginning of the span.
+ off = (uintptr*)p - (uintptr*)arena_start;
+ bitp = arena_start - off/wordsPerBitmapByte - 1;
+ shift = 0;
+ step = size/(PtrSize*wordsPerBitmapByte);
+ // Rewind to the previous quadruple as we move to the next
+ // in the beginning of the loop.
+ bitp += step;
+ if(step == 0) {
+ // 8-byte objects.
+ bitp++;
+ shift = gcBits;
+ }
for(; n > 0; n--, p += size) {
- off = (uintptr*)p - (uintptr*)arena_start;
- bitp = (uintptr*)arena_start - off/wordsPerBitmapWord - 1;
- shift = (off % wordsPerBitmapWord) * gcBits;
+ bitp -= step;
+ if(step == 0) {
+ if(shift != 0)
+ bitp--;
+ shift = gcBits - shift;
+ }
+
xbits = *bitp;
bits = (xbits>>shift) & bitMask;
- // Non-allocated or FlagNoGC object, ignore.
- if(bits == bitBoundary)
- continue;
// Allocated and marked object, reset bits to allocated.
- if(bits == bitMarked) {
- *bitp = (xbits & ~(bitMarked<<shift)) | (bitAllocated<<shift);
+ if((bits&bitMarked) != 0) {
+ *bitp &= ~(bitMarked<<shift);
continue;
}
// At this point we know that we are looking at garbage object
// that needs to be collected.
if(runtime·debug.allocfreetrace)
runtime·tracefree(p, size);
- // Reset to boundary.
- *bitp = (xbits & ~(bitAllocated<<shift)) | (bitBoundary<<shift);
+ // Reset to allocated+noscan.
+ *bitp = (xbits & ~((bitMarked|(BitsMask<<2))<<shift)) | ((uintptr)BitsDead<<(shift+2));
if(cl == 0) {
// Free large span.
+ if(preserve)
+ runtime·throw("can't preserve large span");
runtime·unmarkspan(p, s->npages<<PageShift);
s->needzero = 1;
// important to set sweepgen before returning it to heap
@@ -1047,7 +1041,7 @@ runtime·MSpan_Sweep(MSpan *s)
c->local_nsmallfree[cl] += nfree;
c->local_cachealloc -= nfree * size;
runtime·xadd64(&mstats.next_gc, -(uint64)(nfree * size * (runtime·gcpercent + 100)/100));
- res = runtime·MCentral_FreeSpan(&runtime·mheap.central[cl], s, nfree, head.next, end);
+ res = runtime·MCentral_FreeSpan(&runtime·mheap.central[cl].mcentral, s, nfree, head.next, end, preserve);
// MCentral_FreeSpan updates sweepgen
}
return res;
@@ -1060,9 +1054,7 @@ static struct
G* g;
bool parked;
- MSpan** spans;
- uint32 nspan;
- uint32 spanidx;
+ uint32 spanidx; // background sweeper position
uint32 nbgsweep;
uint32 npausesweep;
@@ -1072,7 +1064,7 @@ static struct
static void
bgsweep(void)
{
- g->issystem = 1;
+ g->issystem = true;
for(;;) {
while(runtime·sweepone() != -1) {
sweep.nbgsweep++;
@@ -1086,9 +1078,7 @@ bgsweep(void)
continue;
}
sweep.parked = true;
- g->isbackground = true;
- runtime·parkunlock(&gclock, "GC sweep wait");
- g->isbackground = false;
+ runtime·parkunlock(&gclock, runtime·gostringnocopy((byte*)"GC sweep wait"));
}
}
@@ -1107,12 +1097,12 @@ runtime·sweepone(void)
sg = runtime·mheap.sweepgen;
for(;;) {
idx = runtime·xadd(&sweep.spanidx, 1) - 1;
- if(idx >= sweep.nspan) {
+ if(idx >= work.nspan) {
runtime·mheap.sweepdone = true;
g->m->locks--;
return -1;
}
- s = sweep.spans[idx];
+ s = work.spans[idx];
if(s->state != MSpanInUse) {
s->sweepgen = sg;
continue;
@@ -1120,7 +1110,7 @@ runtime·sweepone(void)
if(s->sweepgen != sg-2 || !runtime·cas(&s->sweepgen, sg-2, sg-1))
continue;
npages = s->npages;
- if(!runtime·MSpan_Sweep(s))
+ if(!runtime·MSpan_Sweep(s, false))
npages = 0;
g->m->locks--;
return npages;
@@ -1192,6 +1182,7 @@ runtime·updatememstats(GCStats *stats)
int32 i;
uint64 smallfree;
uint64 *src, *dst;
+ void (*fn)(G*);
if(stats)
runtime·memclr((byte*)stats, sizeof(*stats));
@@ -1228,13 +1219,16 @@ runtime·updatememstats(GCStats *stats)
// Flush MCache's to MCentral.
if(g == g->m->g0)
flushallmcaches();
- else
- runtime·mcall(flushallmcaches_m);
+ else {
+ fn = flushallmcaches_m;
+ runtime·mcall(&fn);
+ }
// Aggregate local stats.
cachestats();
// Scan all spans and count number of alive objects.
+ runtime·lock(&runtime·mheap.lock);
for(i = 0; i < runtime·mheap.nspan; i++) {
s = runtime·mheap.allspans[i];
if(s->state != MSpanInUse)
@@ -1248,6 +1242,7 @@ runtime·updatememstats(GCStats *stats)
mstats.alloc += s->ref*s->elemsize;
}
}
+ runtime·unlock(&runtime·mheap.lock);
// Aggregate by size class.
smallfree = 0;
@@ -1290,107 +1285,33 @@ runtime·readgogc(void)
return runtime·atoi(p);
}
-// force = 1 - do GC regardless of current heap usage
-// force = 2 - go GC and eager sweep
void
-runtime·gc(int32 force)
+runtime·gcinit(void)
{
- struct gc_args a;
- int32 i;
-
-//if(thechar == '9') return;
-
if(sizeof(Workbuf) != WorkbufSize)
runtime·throw("runtime: size of Workbuf is suboptimal");
- // The gc is turned off (via enablegc) until
- // the bootstrap has completed.
- // Also, malloc gets called in the guts
- // of a number of libraries that might be
- // holding locks. To avoid priority inversion
- // problems, don't bother trying to run gc
- // while holding a lock. The next mallocgc
- // without a lock will do the gc instead.
- if(!mstats.enablegc || g == g->m->g0 || g->m->locks > 0 || runtime·panicking)
- return;
-
- if(runtime·gcpercent == GcpercentUnknown) { // first time through
- runtime·lock(&runtime·mheap);
- if(runtime·gcpercent == GcpercentUnknown)
- runtime·gcpercent = runtime·readgogc();
- runtime·unlock(&runtime·mheap);
- }
- if(runtime·gcpercent < 0)
- return;
-
- runtime·semacquire(&runtime·worldsema, false);
- if(force==0 && mstats.heap_alloc < mstats.next_gc) {
- // typically threads which lost the race to grab
- // worldsema exit here when gc is done.
- runtime·semrelease(&runtime·worldsema);
- return;
- }
-
- // Ok, we're doing it! Stop everybody else
- a.start_time = runtime·nanotime();
- a.eagersweep = force >= 2;
- g->m->gcing = 1;
- runtime·stoptheworld();
-
- runtime·clearpools();
-
- // Run gc on the g0 stack. We do this so that the g stack
- // we're currently running on will no longer change. Cuts
- // the root set down a bit (g0 stacks are not scanned, and
- // we don't need to scan gc's internal state). Also an
- // enabler for copyable stacks.
- for(i = 0; i < (runtime·debug.gctrace > 1 ? 2 : 1); i++) {
- if(i > 0)
- a.start_time = runtime·nanotime();
- // switch to g0, call gc(&a), then switch back
- g->param = &a;
- g->status = Gwaiting;
- g->waitreason = "garbage collection";
- runtime·mcall(mgc);
- }
-
- // all done
- g->m->gcing = 0;
- g->m->locks++;
- runtime·semrelease(&runtime·worldsema);
- runtime·starttheworld();
- g->m->locks--;
-
- // now that gc is done, kick off finalizer thread if needed
- if(!ConcurrentSweep) {
- // give the queued finalizers, if any, a chance to run
- runtime·gosched();
- }
-}
-static void
-mgc(G *gp)
-{
- gc(gp->param);
- gp->param = nil;
- gp->status = Grunning;
- runtime·gogo(&gp->sched);
+ work.markfor = runtime·parforalloc(MaxGcproc);
+ runtime·gcpercent = runtime·readgogc();
+ runtime·gcdatamask = unrollglobgcprog(runtime·gcdata, runtime·edata - runtime·data);
+ runtime·gcbssmask = unrollglobgcprog(runtime·gcbss, runtime·ebss - runtime·bss);
}
void
-runtime·mgc2(void)
+runtime·gc_m(void)
{
struct gc_args a;
G *gp;
gp = g->m->curg;
- gp->status = Gwaiting;
- gp->waitreason = "garbage collection";
+ runtime·casgstatus(gp, Grunning, Gwaiting);
+ gp->waitreason = runtime·gostringnocopy((byte*)"garbage collection");
- a.start_time = g->m->scalararg[0];
- a.eagersweep = g->m->scalararg[1];
+ a.start_time = (uint64)(g->m->scalararg[0]) | ((uint64)(g->m->scalararg[1]) << 32);
+ a.eagersweep = g->m->scalararg[2];
gc(&a);
- gp->status = Grunning;
+ runtime·casgstatus(gp, Gwaiting, Grunning);
}
static void
@@ -1399,7 +1320,6 @@ gc(struct gc_args *args)
int64 t0, t1, t2, t3, t4;
uint64 heap0, heap1, obj;
GCStats stats;
- uint32 i;
if(runtime·debug.allocfreetrace)
runtime·tracegc();
@@ -1408,14 +1328,6 @@ gc(struct gc_args *args)
t0 = args->start_time;
work.tstart = args->start_time;
- if(work.gcdata == nil) {
- work.gcdata = unrollglobgcprog(gcdata, edata - data);
- work.gcbss = unrollglobgcprog(gcbss, ebss - bss);
- }
-
- if(work.markfor == nil)
- work.markfor = runtime·parforalloc(MaxGcproc);
-
t1 = 0;
if(runtime·debug.gctrace)
t1 = runtime·nanotime();
@@ -1424,6 +1336,24 @@ gc(struct gc_args *args)
while(runtime·sweepone() != -1)
sweep.npausesweep++;
+ // Cache runtime.mheap.allspans in work.spans to avoid conflicts with
+ // resizing/freeing allspans.
+ // New spans can be created while GC progresses, but they are not garbage for
+ // this round:
+ // - new stack spans can be created even while the world is stopped.
+ // - new malloc spans can be created during the concurrent sweep
+
+ // Even if this is stop-the-world, a concurrent exitsyscall can allocate a stack from heap.
+ runtime·lock(&runtime·mheap.lock);
+ // Free the old cached sweep array if necessary.
+ if(work.spans != nil && work.spans != runtime·mheap.allspans)
+ runtime·SysFree(work.spans, work.nspan*sizeof(work.spans[0]), &mstats.other_sys);
+ // Cache the current array for marking.
+ runtime·mheap.gcspans = runtime·mheap.allspans;
+ work.spans = runtime·mheap.allspans;
+ work.nspan = runtime·mheap.nspan;
+ runtime·unlock(&runtime·mheap.lock);
+
work.nwait = 0;
work.ndone = 0;
work.nproc = runtime·gcprocs();
@@ -1457,7 +1387,7 @@ gc(struct gc_args *args)
mstats.next_gc = mstats.heap_alloc+mstats.heap_alloc*runtime·gcpercent/100;
t4 = runtime·nanotime();
- mstats.last_gc = runtime·unixnanotime(); // must be Unix time to make sense to user
+ runtime·atomicstore64(&mstats.last_gc, runtime·unixnanotime()); // must be Unix time to make sense to user
mstats.pause_ns[mstats.numgc%nelem(mstats.pause_ns)] = t4 - t0;
mstats.pause_total_ns += t4 - t0;
mstats.numgc++;
@@ -1483,34 +1413,33 @@ gc(struct gc_args *args)
mstats.numgc, work.nproc, (t1-t0)/1000, (t2-t1)/1000, (t3-t2)/1000, (t4-t3)/1000,
heap0>>20, heap1>>20, obj,
mstats.nmalloc, mstats.nfree,
- sweep.nspan, sweep.nbgsweep, sweep.npausesweep,
+ work.nspan, sweep.nbgsweep, sweep.npausesweep,
stats.nhandoff, stats.nhandoffcnt,
work.markfor->nsteal, work.markfor->nstealcnt,
stats.nprocyield, stats.nosyield, stats.nsleep);
sweep.nbgsweep = sweep.npausesweep = 0;
}
- // We cache current runtime·mheap.allspans array in sweep.spans,
- // because the former can be resized and freed.
- // Otherwise we would need to take heap lock every time
- // we want to convert span index to span pointer.
-
- // Free the old cached array if necessary.
- if(sweep.spans && sweep.spans != runtime·mheap.allspans)
- runtime·SysFree(sweep.spans, sweep.nspan*sizeof(sweep.spans[0]), &mstats.other_sys);
- // Cache the current array.
- runtime·mheap.sweepspans = runtime·mheap.allspans;
+ // See the comment in the beginning of this function as to why we need the following.
+ // Even if this is still stop-the-world, a concurrent exitsyscall can allocate a stack from heap.
+ runtime·lock(&runtime·mheap.lock);
+ // Free the old cached mark array if necessary.
+ if(work.spans != nil && work.spans != runtime·mheap.allspans)
+ runtime·SysFree(work.spans, work.nspan*sizeof(work.spans[0]), &mstats.other_sys);
+ // Cache the current array for sweeping.
+ runtime·mheap.gcspans = runtime·mheap.allspans;
runtime·mheap.sweepgen += 2;
runtime·mheap.sweepdone = false;
- sweep.spans = runtime·mheap.allspans;
- sweep.nspan = runtime·mheap.nspan;
+ work.spans = runtime·mheap.allspans;
+ work.nspan = runtime·mheap.nspan;
sweep.spanidx = 0;
+ runtime·unlock(&runtime·mheap.lock);
// Temporary disable concurrent sweep, because we see failures on builders.
if(ConcurrentSweep && !args->eagersweep) {
runtime·lock(&gclock);
if(sweep.g == nil)
- sweep.g = runtime·newproc1(&bgsweepv, nil, 0, 0, runtime·gc);
+ sweep.g = runtime·newproc1(&bgsweepv, nil, 0, 0, gc);
else if(sweep.parked) {
sweep.parked = false;
runtime·ready(sweep.g);
@@ -1522,12 +1451,7 @@ gc(struct gc_args *args)
sweep.npausesweep++;
}
- // Shrink a stack if not much of it is being used.
- // TODO: do in a parfor
- for(i = 0; i < runtime·allglen; i++)
- runtime·shrinkstack(runtime·allg[i]);
-
- runtime·MProf_GC();
+ runtime·mProf_GC();
g->m->traceback = 0;
}
@@ -1572,7 +1496,7 @@ runtime∕debug·readGCStats(Slice *pauses)
// Pass back: pauses, last gc (absolute time), number of gc, total pause ns.
p = (uint64*)pauses->array;
- runtime·lock(&runtime·mheap);
+ runtime·lock(&runtime·mheap.lock);
n = mstats.numgc;
if(n > nelem(mstats.pause_ns))
n = nelem(mstats.pause_ns);
@@ -1587,23 +1511,25 @@ runtime∕debug·readGCStats(Slice *pauses)
p[n] = mstats.last_gc;
p[n+1] = mstats.numgc;
p[n+2] = mstats.pause_total_ns;
- runtime·unlock(&runtime·mheap);
+ runtime·unlock(&runtime·mheap.lock);
pauses->len = n+3;
}
-int32
-runtime·setgcpercent(int32 in) {
+void
+runtime·setgcpercent_m(void) {
+ int32 in;
int32 out;
- runtime·lock(&runtime·mheap);
- if(runtime·gcpercent == GcpercentUnknown)
- runtime·gcpercent = runtime·readgogc();
+ in = (int32)(intptr)g->m->scalararg[0];
+
+ runtime·lock(&runtime·mheap.lock);
out = runtime·gcpercent;
if(in < 0)
in = -1;
runtime·gcpercent = in;
- runtime·unlock(&runtime·mheap);
- return out;
+ runtime·unlock(&runtime·mheap.lock);
+
+ g->m->scalararg[0] = (uintptr)(intptr)out;
}
static void
@@ -1615,153 +1541,31 @@ gchelperstart(void)
runtime·throw("gchelper not running on g0 stack");
}
-static void
-runfinq(void)
-{
- Finalizer *f;
- FinBlock *fb, *next;
- byte *frame;
- uint32 framesz, framecap, i;
- Eface *ef, ef1;
-
- // This function blocks for long periods of time, and because it is written in C
- // we have no liveness information. Zero everything so that uninitialized pointers
- // do not cause memory leaks.
- f = nil;
- fb = nil;
- next = nil;
- frame = nil;
- framecap = 0;
- framesz = 0;
- i = 0;
- ef = nil;
- ef1.type = nil;
- ef1.data = nil;
-
- // force flush to memory
- USED(&f);
- USED(&fb);
- USED(&next);
- USED(&framesz);
- USED(&i);
- USED(&ef);
- USED(&ef1);
-
- for(;;) {
- runtime·lock(&finlock);
- fb = finq;
- finq = nil;
- if(fb == nil) {
- runtime·fingwait = true;
- g->isbackground = true;
- runtime·parkunlock(&finlock, "finalizer wait");
- g->isbackground = false;
- continue;
- }
- runtime·unlock(&finlock);
- if(raceenabled)
- runtime·racefingo();
- for(; fb; fb=next) {
- next = fb->next;
- for(i=0; i<fb->cnt; i++) {
- f = &fb->fin[i];
- framesz = sizeof(Eface) + f->nret;
- if(framecap < framesz) {
- // The frame does not contain pointers interesting for GC,
- // 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 = runtime·mallocgc(framesz, 0, FlagNoScan|FlagNoInvokeGC);
- framecap = framesz;
- }
- if(f->fint == nil)
- runtime·throw("missing type in runfinq");
- if(f->fint->kind == KindPtr) {
- // direct use of pointer
- *(void**)frame = f->arg;
- } else if(((InterfaceType*)f->fint)->mhdr.len == 0) {
- // convert to empty interface
- ef = (Eface*)frame;
- ef->type = f->ot;
- ef->data = f->arg;
- } else {
- // convert to interface with methods, via empty interface.
- ef1.type = f->ot;
- ef1.data = f->arg;
- if(!runtime·ifaceE2I2((InterfaceType*)f->fint, ef1, (Iface*)frame))
- runtime·throw("invalid type conversion in runfinq");
- }
- reflect·call(f->fn, frame, framesz, framesz);
- f->fn = nil;
- f->arg = nil;
- f->ot = nil;
- }
- fb->cnt = 0;
- runtime·lock(&finlock);
- fb->next = finc;
- finc = fb;
- runtime·unlock(&finlock);
- }
-
- // Zero everything that's dead, to avoid memory leaks.
- // See comment at top of function.
- f = nil;
- fb = nil;
- next = nil;
- i = 0;
- ef = nil;
- ef1.type = nil;
- ef1.data = nil;
- runtime·gc(1); // trigger another gc to clean up the finalized objects, if possible
- }
-}
-
-void
-runtime·createfing(void)
-{
- if(runtime·fing != nil)
- return;
- // Here we use gclock instead of finlock,
- // because newproc1 can allocate, which can cause on-demand span sweep,
- // which can queue finalizers, which would deadlock.
- runtime·lock(&gclock);
- if(runtime·fing == nil)
- runtime·fing = runtime·newproc1(&runfinqv, nil, 0, 0, runtime·gc);
- runtime·unlock(&gclock);
-}
-
-void
-runtime·createfingM(G *gp)
-{
- runtime·createfing();
- runtime·gogo(&gp->sched);
-}
-
G*
runtime·wakefing(void)
{
G *res;
res = nil;
- runtime·lock(&finlock);
+ runtime·lock(&runtime·finlock);
if(runtime·fingwait && runtime·fingwake) {
runtime·fingwait = false;
runtime·fingwake = false;
res = runtime·fing;
}
- runtime·unlock(&finlock);
+ runtime·unlock(&runtime·finlock);
return res;
}
-// Recursively GC program in prog.
+// Recursively unrolls GC program in prog.
// mask is where to store the result.
// ppos is a pointer to position in mask, in bits.
// sparse says to generate 4-bits per word mask for heap (2-bits for data/bss otherwise).
static byte*
unrollgcprog1(byte *mask, byte *prog, uintptr *ppos, bool inplace, bool sparse)
{
- uintptr *b, off, shift, pos, siz, i;
- byte *arena_start, *prog1, v;
+ uintptr pos, siz, i, off;
+ byte *arena_start, *prog1, v, *bitp, shift;
arena_start = runtime·mheap.arena_start;
pos = *ppos;
@@ -1778,11 +1582,11 @@ unrollgcprog1(byte *mask, byte *prog, uintptr *ppos, bool inplace, bool sparse)
if(inplace) {
// Store directly into GC bitmap.
off = (uintptr*)(mask+pos) - (uintptr*)arena_start;
- b = (uintptr*)arena_start - off/wordsPerBitmapWord - 1;
- shift = (off % wordsPerBitmapWord) * gcBits;
- if((shift%8)==0)
- ((byte*)b)[(shift/8)^byteEndian] = 0;
- ((byte*)b)[(shift/8)^byteEndian] |= v<<((shift%8)+2);
+ bitp = arena_start - off/wordsPerBitmapByte - 1;
+ shift = (off % wordsPerBitmapByte) * gcBits;
+ if(shift==0)
+ *bitp = 0;
+ *bitp |= v<<(shift+2);
pos += PtrSize;
} else if(sparse) {
// 4-bits per word
@@ -1822,7 +1626,7 @@ unrollgcprog1(byte *mask, byte *prog, uintptr *ppos, bool inplace, bool sparse)
}
// Unrolls GC program prog for data/bss, returns dense GC mask.
-static byte*
+static BitVector
unrollglobgcprog(byte *prog, uintptr size)
{
byte *mask;
@@ -1842,14 +1646,23 @@ unrollglobgcprog(byte *prog, uintptr size)
runtime·throw("unrollglobgcprog: program does not end with insEnd");
if(mask[masksize] != 0xa1)
runtime·throw("unrollglobgcprog: overflow");
- return mask;
+ return (BitVector){masksize*8, mask};
}
-static void
-unrollgcproginplace(void *v, uintptr size, uintptr size0, Type *typ)
+void
+runtime·unrollgcproginplace_m(void)
{
- uintptr *b, off, shift, pos;
- byte *arena_start, *prog;
+ uintptr size, size0, pos, off;
+ byte *arena_start, *prog, *bitp, shift;
+ Type *typ;
+ void *v;
+
+ v = g->m->ptrarg[0];
+ typ = g->m->ptrarg[1];
+ size = g->m->scalararg[0];
+ size0 = g->m->scalararg[1];
+ g->m->ptrarg[0] = nil;
+ g->m->ptrarg[1] = nil;
pos = 0;
prog = (byte*)typ->gc[1];
@@ -1858,27 +1671,31 @@ unrollgcproginplace(void *v, uintptr size, uintptr size0, Type *typ)
// Mark first word as bitAllocated.
arena_start = runtime·mheap.arena_start;
off = (uintptr*)v - (uintptr*)arena_start;
- b = (uintptr*)arena_start - off/wordsPerBitmapWord - 1;
- shift = (off % wordsPerBitmapWord) * gcBits;
- *b |= bitAllocated<<shift;
+ bitp = arena_start - off/wordsPerBitmapByte - 1;
+ shift = (off % wordsPerBitmapByte) * gcBits;
+ *bitp |= bitBoundary<<shift;
// Mark word after last as BitsDead.
if(size0 < size) {
off = (uintptr*)((byte*)v + size0) - (uintptr*)arena_start;
- b = (uintptr*)arena_start - off/wordsPerBitmapWord - 1;
- shift = (off % wordsPerBitmapWord) * gcBits;
- *b &= ~(bitPtrMask<<shift) | (BitsDead<<(shift+2));
+ bitp = arena_start - off/wordsPerBitmapByte - 1;
+ shift = (off % wordsPerBitmapByte) * gcBits;
+ *bitp &= ~(bitPtrMask<<shift) | ((uintptr)BitsDead<<(shift+2));
}
}
// Unrolls GC program in typ->gc[1] into typ->gc[0]
-static void
-unrollgcprog(Type *typ)
+void
+runtime·unrollgcprog_m(void)
{
- static Lock lock;
+ static Mutex lock;
+ Type *typ;
byte *mask, *prog;
uintptr pos;
uintptr x;
+ typ = g->m->ptrarg[0];
+ g->m->ptrarg[0] = nil;
+
runtime·lock(&lock);
mask = (byte*)typ->gc[0];
if(mask[0] == 0) {
@@ -1901,171 +1718,72 @@ unrollgcprog(Type *typ)
runtime·unlock(&lock);
}
-void
-runtime·markallocated(void *v, uintptr size, uintptr size0, Type *typ, bool scan)
-{
- uintptr *b, off, shift, i, ti, te, nptr, masksize, maskword;
- byte *arena_start, x;
- bool *ptrmask;
-
- arena_start = runtime·mheap.arena_start;
- off = (uintptr*)v - (uintptr*)arena_start;
- b = (uintptr*)arena_start - off/wordsPerBitmapWord - 1;
- shift = (off % wordsPerBitmapWord) * gcBits;
- if(Debug && (((*b)>>shift)&bitMask) != bitBoundary) {
- runtime·printf("runtime: bad bits in markallocated (%p) b=%p[%p] off=%p shift=%d\n", v, b, *b, off, (int32)shift);
- runtime·throw("bad bits in markallocated");
- }
-
- if(!scan) {
- // BitsDead in the first quadruple means don't scan.
- if(size == PtrSize)
- *b = (*b & ~((bitBoundary|bitPtrMask)<<shift)) | ((bitAllocated+(BitsDead<<2))<<shift);
- else
- ((byte*)b)[(shift/8)^byteEndian] = bitAllocated+(BitsDead<<2);
- return;
- }
- if(size == PtrSize) {
- // It's one word and it has pointers, it must be a pointer.
- *b = (*b & ~((bitBoundary|bitPtrMask)<<shift)) | ((bitAllocated | (BitsPointer<<2))<<shift);
- return;
- }
- ti = te = 0;
- ptrmask = nil;
- if(typ != nil && (typ->gc[0]|typ->gc[1]) != 0 && typ->size > PtrSize) {
- if(typ->kind&KindGCProg) {
- nptr = ROUND(typ->size, PtrSize)/PtrSize;
- masksize = nptr;
- if(masksize%2)
- masksize *= 2; // repeated twice
- masksize = masksize*PointersPerByte/8; // 4 bits per word
- masksize++; // unroll flag in the beginning
- if(masksize > MaxGCMask && typ->gc[1] != 0) {
- // If the mask is too large, unroll the program directly
- // into the GC bitmap. It's 7 times slower than copying
- // from the pre-unrolled mask, but saves 1/16 of type size
- // memory for the mask.
- unrollgcproginplace(v, size, size0, typ);
- return;
- }
- ptrmask = (byte*)typ->gc[0];
- // check whether the program is already unrolled
- maskword = (uintptr)runtime·atomicloadp((void*)&typ->gc[0]);
- if(((byte*)&maskword)[0] == 0)
- unrollgcprog(typ);
- ptrmask++; // skip the unroll flag byte
- } else
- ptrmask = (byte*)&typ->gc[0]; // embed mask
- if(size == 2*PtrSize) {
- ((byte*)b)[(shift/8)^byteEndian] = ptrmask[0] | bitAllocated;
- return;
- }
- te = typ->size/PtrSize;
- // if the type occupies odd number of words, its mask is repeated twice
- if((te%2) == 0)
- te /= 2;
- }
- if(size == 2*PtrSize) {
- ((byte*)b)[(shift/8)^byteEndian] = (BitsPointer<<2) | (BitsPointer<<6) | bitAllocated;
- return;
- }
- // Copy pointer bitmask into the bitmap.
- for(i=0; i<size0; i+=2*PtrSize) {
- x = (BitsPointer<<2) | (BitsPointer<<6);
- if(ptrmask != nil) {
- x = ptrmask[ti++];
- if(ti == te)
- ti = 0;
- }
- off = (uintptr*)((byte*)v + i) - (uintptr*)arena_start;
- b = (uintptr*)arena_start - off/wordsPerBitmapWord - 1;
- shift = (off % wordsPerBitmapWord) * gcBits;
- if(i == 0)
- x |= bitAllocated;
- if(i+PtrSize == size0)
- x &= ~(bitPtrMask<<4);
- ((byte*)b)[(shift/8)^byteEndian] = x;
- }
- if(size0 == i && size0 < size) {
- // mark the word after last object's word as BitsDead
- off = (uintptr*)((byte*)v + size0) - (uintptr*)arena_start;
- b = (uintptr*)arena_start - off/wordsPerBitmapWord - 1;
- shift = (off % wordsPerBitmapWord) * gcBits;
- ((byte*)b)[(shift/8)^byteEndian] = 0;
- }
-}
-
-void
-runtime·markallocated_m(void)
-{
- M *mp;
-
- mp = g->m;
- runtime·markallocated(mp->ptrarg[0], mp->scalararg[0], mp->scalararg[1], mp->ptrarg[1], mp->scalararg[2] == 0);
- mp->ptrarg[0] = nil;
- mp->ptrarg[1] = nil;
-}
-
// mark the span of memory at v as having n blocks of the given size.
// if leftover is true, there is left over space at the end of the span.
void
runtime·markspan(void *v, uintptr size, uintptr n, bool leftover)
{
- uintptr *b, *b0, off, shift, x;
- byte *p;
+ uintptr i, off, step;
+ byte *b;
if((byte*)v+size*n > (byte*)runtime·mheap.arena_used || (byte*)v < runtime·mheap.arena_start)
runtime·throw("markspan: bad pointer");
- p = v;
- if(leftover) // mark a boundary just past end of last block too
- n++;
-
- b0 = nil;
- x = 0;
- for(; n-- > 0; p += size) {
- // Okay to use non-atomic ops here, because we control
- // the entire span, and each bitmap word has bits for only
- // one span, so no other goroutines are changing these
- // bitmap words.
- off = (uintptr*)p - (uintptr*)runtime·mheap.arena_start; // word offset
- b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1;
- shift = (off % wordsPerBitmapWord) * gcBits;
- if(b0 != b) {
- if(b0 != nil)
- *b0 = x;
- b0 = b;
- x = 0;
- }
- x |= bitBoundary<<shift;
+ // Find bits of the beginning of the span.
+ off = (uintptr*)v - (uintptr*)runtime·mheap.arena_start; // word offset
+ b = runtime·mheap.arena_start - off/wordsPerBitmapByte - 1;
+ if((off%wordsPerBitmapByte) != 0)
+ runtime·throw("markspan: unaligned length");
+
+ // Okay to use non-atomic ops here, because we control
+ // the entire span, and each bitmap byte has bits for only
+ // one span, so no other goroutines are changing these bitmap words.
+
+ if(size == PtrSize) {
+ // Possible only on 64-bits (minimal size class is 8 bytes).
+ // Poor man's memset(0x11).
+ if(0x11 != ((bitBoundary+BitsDead)<<gcBits) + (bitBoundary+BitsDead))
+ runtime·throw("markspan: bad bits");
+ if((n%(wordsPerBitmapByte*PtrSize)) != 0)
+ runtime·throw("markspan: unaligned length");
+ b = b - n/wordsPerBitmapByte + 1; // find first byte
+ if(((uintptr)b%PtrSize) != 0)
+ runtime·throw("markspan: unaligned pointer");
+ for(i = 0; i != n; i += wordsPerBitmapByte*PtrSize, b += PtrSize)
+ *(uintptr*)b = (uintptr)0x1111111111111111ULL; // bitBoundary+BitsDead
+ return;
}
- *b0 = x;
+
+ if(leftover)
+ n++; // mark a boundary just past end of last block too
+ step = size/(PtrSize*wordsPerBitmapByte);
+ for(i = 0; i != n; i++, b -= step)
+ *b = bitBoundary|(BitsDead<<2);
}
// unmark the span of memory at v of length n bytes.
void
runtime·unmarkspan(void *v, uintptr n)
{
- uintptr *p, *b, off;
+ uintptr off;
+ byte *b;
if((byte*)v+n > (byte*)runtime·mheap.arena_used || (byte*)v < runtime·mheap.arena_start)
runtime·throw("markspan: bad pointer");
- p = v;
- off = p - (uintptr*)runtime·mheap.arena_start; // word offset
- if((off % wordsPerBitmapWord) != 0)
+ off = (uintptr*)v - (uintptr*)runtime·mheap.arena_start; // word offset
+ if((off % (PtrSize*wordsPerBitmapByte)) != 0)
runtime·throw("markspan: unaligned pointer");
- b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1;
+ b = runtime·mheap.arena_start - off/wordsPerBitmapByte - 1;
n /= PtrSize;
- if(n%wordsPerBitmapWord != 0)
+ if(n%(PtrSize*wordsPerBitmapByte) != 0)
runtime·throw("unmarkspan: unaligned length");
// Okay to use non-atomic ops here, because we control
// the entire span, and each bitmap word has bits for only
// one span, so no other goroutines are changing these
// bitmap words.
- n /= wordsPerBitmapWord;
- while(n-- > 0)
- *b-- = 0;
+ n /= wordsPerBitmapByte;
+ runtime·memclr(b - n + 1, n);
}
void
@@ -2079,7 +1797,7 @@ runtime·MHeap_MapBits(MHeap *h)
};
uintptr n;
- n = (h->arena_used - h->arena_start) / wordsPerBitmapWord;
+ n = (h->arena_used - h->arena_start) / (PtrSize*wordsPerBitmapByte);
n = ROUND(n, bitmapChunk);
n = ROUND(n, PhysPageSize);
if(h->bitmap_mapped >= n)
@@ -2095,7 +1813,7 @@ getgcmaskcb(Stkframe *frame, void *ctxt)
Stkframe *frame0;
frame0 = ctxt;
- if(frame0->sp >= (uintptr)frame->varp - frame->sp && frame0->sp < (uintptr)frame->varp) {
+ if(frame->sp <= frame0->sp && frame0->sp < frame->varp) {
*frame0 = *frame;
return false;
}
@@ -2107,32 +1825,33 @@ void
runtime·getgcmask(byte *p, Type *t, byte **mask, uintptr *len)
{
Stkframe frame;
- uintptr i, n, off, bits, shift, *b;
- byte *base;
+ uintptr i, n, off;
+ byte *base, bits, shift, *b;
+ bool (*cb)(Stkframe*, void*);
*mask = nil;
*len = 0;
// data
- if(p >= data && p < edata) {
+ if(p >= runtime·data && p < runtime·edata) {
n = ((PtrType*)t)->elem->size;
*len = n/PtrSize;
*mask = runtime·mallocgc(*len, nil, 0);
for(i = 0; i < n; i += PtrSize) {
- off = (p+i-data)/PtrSize;
- bits = (work.gcdata[off/PointersPerByte] >> ((off%PointersPerByte)*BitsPerPointer))&BitsMask;
+ off = (p+i-runtime·data)/PtrSize;
+ bits = (runtime·gcdatamask.bytedata[off/PointersPerByte] >> ((off%PointersPerByte)*BitsPerPointer))&BitsMask;
(*mask)[i/PtrSize] = bits;
}
return;
}
// bss
- if(p >= bss && p < ebss) {
+ if(p >= runtime·bss && p < runtime·ebss) {
n = ((PtrType*)t)->elem->size;
*len = n/PtrSize;
*mask = runtime·mallocgc(*len, nil, 0);
for(i = 0; i < n; i += PtrSize) {
- off = (p+i-bss)/PtrSize;
- bits = (work.gcbss[off/PointersPerByte] >> ((off%PointersPerByte)*BitsPerPointer))&BitsMask;
+ off = (p+i-runtime·bss)/PtrSize;
+ bits = (runtime·gcbssmask.bytedata[off/PointersPerByte] >> ((off%PointersPerByte)*BitsPerPointer))&BitsMask;
(*mask)[i/PtrSize] = bits;
}
return;
@@ -2143,8 +1862,8 @@ runtime·getgcmask(byte *p, Type *t, byte **mask, uintptr *len)
*mask = runtime·mallocgc(*len, nil, 0);
for(i = 0; i < n; i += PtrSize) {
off = (uintptr*)(base+i) - (uintptr*)runtime·mheap.arena_start;
- b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1;
- shift = (off % wordsPerBitmapWord) * gcBits;
+ b = runtime·mheap.arena_start - off/wordsPerBitmapByte - 1;
+ shift = (off % wordsPerBitmapByte) * gcBits;
bits = (*b >> (shift+2))&BitsMask;
(*mask)[i/PtrSize] = bits;
}
@@ -2153,7 +1872,8 @@ runtime·getgcmask(byte *p, Type *t, byte **mask, uintptr *len)
// stack
frame.fn = nil;
frame.sp = (uintptr)p;
- runtime·gentraceback((uintptr)runtime·getcallerpc(&p), (uintptr)runtime·getcallersp(&p), 0, g, 0, nil, 1000, getgcmaskcb, &frame, false);
+ cb = getgcmaskcb;
+ runtime·gentraceback(g->m->curg->sched.pc, g->m->curg->sched.sp, 0, g->m->curg, 0, nil, 1000, &cb, &frame, false);
if(frame.fn != nil) {
Func *f;
StackMap *stackmap;
@@ -2180,9 +1900,19 @@ runtime·getgcmask(byte *p, Type *t, byte **mask, uintptr *len)
*len = n/PtrSize;
*mask = runtime·mallocgc(*len, nil, 0);
for(i = 0; i < n; i += PtrSize) {
- off = (p+i-frame.varp+size)/PtrSize;
+ off = (p+i-(byte*)frame.varp+size)/PtrSize;
bits = (bv.bytedata[off/PointersPerByte] >> ((off%PointersPerByte)*BitsPerPointer))&BitsMask;
(*mask)[i/PtrSize] = bits;
}
}
}
+
+void runtime·gc_unixnanotime(int64 *now);
+
+int64 runtime·unixnanotime(void)
+{
+ int64 now;
+
+ runtime·gc_unixnanotime(&now);
+ return now;
+}
diff --git a/src/pkg/runtime/mgc0.go b/src/pkg/runtime/mgc0.go
index 624485d18b..2d9d76a474 100644
--- a/src/pkg/runtime/mgc0.go
+++ b/src/pkg/runtime/mgc0.go
@@ -19,9 +19,52 @@ func gc_itab_ptr(ret *interface{}) {
*ret = (*itab)(nil)
}
+// Type used for "conservative" allocations in C code.
+type notype [8]*byte
+
+// Called from C. Returns the Go type used for C allocations w/o type.
+func gc_notype_ptr(ret *interface{}) {
+ var x notype
+ *ret = x
+}
+
func timenow() (sec int64, nsec int32)
func gc_unixnanotime(now *int64) {
sec, nsec := timenow()
*now = sec*1e9 + int64(nsec)
}
+
+func freeOSMemory() {
+ gogc(2) // force GC and do eager sweep
+ onM(scavenge_m)
+}
+
+var poolcleanup func()
+
+func registerPoolCleanup(f func()) {
+ poolcleanup = f
+}
+
+func clearpools() {
+ // clear sync.Pools
+ if poolcleanup != nil {
+ poolcleanup()
+ }
+
+ for _, p := range &allp {
+ if p == nil {
+ break
+ }
+ // clear tinyalloc pool
+ if c := p.mcache; c != nil {
+ c.tiny = nil
+ c.tinysize = 0
+ c.sudogcache = nil
+ }
+ // clear defer pools
+ for i := range p.deferpool {
+ p.deferpool[i] = nil
+ }
+ }
+}
diff --git a/src/pkg/runtime/mgc0.h b/src/pkg/runtime/mgc0.h
index 99271a532b..cc7acfbe1f 100644
--- a/src/pkg/runtime/mgc0.h
+++ b/src/pkg/runtime/mgc0.h
@@ -15,8 +15,8 @@ enum {
// Should we just make the bitmap a byte array?
// Four bits per word (see #defines below).
- wordsPerBitmapWord = sizeof(void*)*8/4,
gcBits = 4,
+ wordsPerBitmapByte = 8/gcBits,
// GC type info programs.
// The programs allow to store type info required for GC in a compact form.
@@ -57,12 +57,13 @@ enum {
BitsMultiWord = 3,
// BitsMultiWord will be set for the first word of a multi-word item.
// When it is set, one of the following will be set for the second word.
- BitsString = 0,
- BitsSlice = 1,
+ // NOT USED ANYMORE: BitsString = 0,
+ // NOT USED ANYMORE: BitsSlice = 1,
BitsIface = 2,
BitsEface = 3,
- MaxGCMask = 0, // disabled because wastes several bytes of memory
+ // 64 bytes cover objects of size 1024/512 on 64/32 bits, respectively.
+ MaxGCMask = 64,
};
// Bits in per-word bitmap.
@@ -77,10 +78,8 @@ enum {
// the off/16+1'th word before mheap.arena_start. (On a 32-bit system,
// the only difference is that the divisor is 8.)
-#define bitMiddle ((uintptr)0) // middle of an object
-#define bitBoundary ((uintptr)1) // boundary on a non-allocated object
-#define bitAllocated ((uintptr)2) // boundary on an allocated object
-#define bitMarked ((uintptr)3) // boundary on an allocated and marked object
+#define bitBoundary ((uintptr)1) // boundary of an object
+#define bitMarked ((uintptr)2) // marked object
-#define bitMask ((uintptr)bitMiddle|bitBoundary|bitAllocated|bitMarked)
+#define bitMask ((uintptr)bitBoundary|bitMarked)
#define bitPtrMask ((uintptr)BitsMask<<2)
diff --git a/src/pkg/runtime/mheap.c b/src/pkg/runtime/mheap.c
index c7043bb143..902a5c71a2 100644
--- a/src/pkg/runtime/mheap.c
+++ b/src/pkg/runtime/mheap.c
@@ -17,7 +17,7 @@
#include "malloc.h"
static MSpan *MHeap_AllocSpanLocked(MHeap*, uintptr);
-static void MHeap_FreeSpanLocked(MHeap*, MSpan*);
+static void MHeap_FreeSpanLocked(MHeap*, MSpan*, bool, bool);
static bool MHeap_Grow(MHeap*, uintptr);
static MSpan *MHeap_AllocLarge(MHeap*, uintptr);
static MSpan *BestFit(MSpan*, uintptr, MSpan*);
@@ -36,14 +36,14 @@ RecordSpan(void *vh, byte *p)
cap = 64*1024/sizeof(all[0]);
if(cap < h->nspancap*3/2)
cap = h->nspancap*3/2;
- all = (MSpan**)runtime·SysAlloc(cap*sizeof(all[0]), &mstats.other_sys);
+ all = (MSpan**)runtime·sysAlloc(cap*sizeof(all[0]), &mstats.other_sys);
if(all == nil)
runtime·throw("runtime: cannot allocate memory");
if(h->allspans) {
runtime·memmove(all, h->allspans, h->nspancap*sizeof(all[0]));
// Don't free the old array if it's referenced by sweep.
// See the comment in mgc0.c.
- if(h->allspans != runtime·mheap.sweepspans)
+ if(h->allspans != runtime·mheap.gcspans)
runtime·SysFree(h->allspans, h->nspancap*sizeof(all[0]), &mstats.other_sys);
}
h->allspans = all;
@@ -70,7 +70,7 @@ runtime·MHeap_Init(MHeap *h)
runtime·MSpanList_Init(&h->freelarge);
runtime·MSpanList_Init(&h->busylarge);
for(i=0; i<nelem(h->central); i++)
- runtime·MCentral_Init(&h->central[i], i);
+ runtime·MCentral_Init(&h->central[i].mcentral, i);
}
void
@@ -106,9 +106,9 @@ retry:
runtime·MSpanList_Remove(s);
// swept spans are at the end of the list
runtime·MSpanList_InsertBack(list, s);
- runtime·unlock(h);
- n += runtime·MSpan_Sweep(s);
- runtime·lock(h);
+ runtime·unlock(&h->lock);
+ n += runtime·MSpan_Sweep(s, false);
+ runtime·lock(&h->lock);
if(n >= npages)
return n;
// the span could have been moved elsewhere
@@ -153,7 +153,7 @@ MHeap_Reclaim(MHeap *h, uintptr npage)
}
// Now sweep everything that is not yet swept.
- runtime·unlock(h);
+ runtime·unlock(&h->lock);
for(;;) {
n = runtime·sweepone();
if(n == -1) // all spans are swept
@@ -162,7 +162,7 @@ MHeap_Reclaim(MHeap *h, uintptr npage)
if(reclaimed >= npage)
break;
}
- runtime·lock(h);
+ runtime·lock(&h->lock);
}
// Allocate a new span of npage pages from the heap for GC'd memory
@@ -174,7 +174,7 @@ mheap_alloc(MHeap *h, uintptr npage, int32 sizeclass, bool large)
if(g != g->m->g0)
runtime·throw("mheap_alloc not on M stack");
- runtime·lock(h);
+ runtime·lock(&h->lock);
// To prevent excessive heap growth, before allocating n pages
// we need to sweep and reclaim at least n pages.
@@ -207,11 +207,11 @@ mheap_alloc(MHeap *h, uintptr npage, int32 sizeclass, bool large)
runtime·MSpanList_InsertBack(&h->busylarge, s);
}
}
- runtime·unlock(h);
+ runtime·unlock(&h->lock);
return s;
}
-void
+static void
mheap_alloc_m(G *gp)
{
MHeap *h;
@@ -229,6 +229,7 @@ MSpan*
runtime·MHeap_Alloc(MHeap *h, uintptr npage, int32 sizeclass, bool large, bool needzero)
{
MSpan *s;
+ void (*fn)(G*);
// Don't do any operations that lock the heap on the G stack.
// It might trigger stack growth, and the stack growth code needs
@@ -240,7 +241,8 @@ runtime·MHeap_Alloc(MHeap *h, uintptr npage, int32 sizeclass, bool large, bool
g->m->scalararg[0] = npage;
g->m->scalararg[1] = sizeclass;
g->m->scalararg[2] = large;
- runtime·mcall(mheap_alloc_m);
+ fn = mheap_alloc_m;
+ runtime·mcall(&fn);
s = g->m->ptrarg[0];
g->m->ptrarg[0] = nil;
}
@@ -259,7 +261,7 @@ runtime·MHeap_AllocStack(MHeap *h, uintptr npage)
if(g != g->m->g0)
runtime·throw("mheap_allocstack not on M stack");
- runtime·lock(h);
+ runtime·lock(&h->lock);
s = MHeap_AllocSpanLocked(h, npage);
if(s != nil) {
s->state = MSpanStack;
@@ -267,7 +269,7 @@ runtime·MHeap_AllocStack(MHeap *h, uintptr npage)
s->ref = 0;
mstats.stacks_inuse += s->npages<<PageShift;
}
- runtime·unlock(h);
+ runtime·unlock(&h->lock);
return s;
}
@@ -279,7 +281,7 @@ MHeap_AllocSpanLocked(MHeap *h, uintptr npage)
{
uintptr n;
MSpan *s, *t;
- PageID p;
+ pageID p;
// Try in fixed-size lists up to max.
for(n=npage; n < nelem(h->free); n++) {
@@ -326,7 +328,7 @@ HaveSpan:
t->needzero = s->needzero;
s->state = MSpanStack; // prevent coalescing with s
t->state = MSpanStack;
- MHeap_FreeSpanLocked(h, t);
+ MHeap_FreeSpanLocked(h, t, false, false);
t->unusedsince = s->unusedsince; // preserve age (TODO: wrong: t is possibly merged and/or deallocated at this point)
s->state = MSpanFree;
}
@@ -380,7 +382,7 @@ MHeap_Grow(MHeap *h, uintptr npage)
uintptr ask;
void *v;
MSpan *s;
- PageID p;
+ pageID p;
// Ask for a big chunk, to reduce the number of mappings
// the operating system needs to track; also amortizes
@@ -413,7 +415,7 @@ MHeap_Grow(MHeap *h, uintptr npage)
h->spans[p + s->npages - 1] = s;
runtime·atomicstore(&s->sweepgen, h->sweepgen);
s->state = MSpanInUse;
- MHeap_FreeSpanLocked(h, s);
+ MHeap_FreeSpanLocked(h, s, false, true);
return true;
}
@@ -441,7 +443,7 @@ MSpan*
runtime·MHeap_LookupMaybe(MHeap *h, void *v)
{
MSpan *s;
- PageID p, q;
+ pageID p, q;
if((byte*)v < h->arena_start || (byte*)v >= h->arena_used)
return nil;
@@ -460,15 +462,15 @@ mheap_free(MHeap *h, MSpan *s, int32 acct)
{
if(g != g->m->g0)
runtime·throw("mheap_free not on M stack");
- runtime·lock(h);
+ runtime·lock(&h->lock);
mstats.heap_alloc += g->m->mcache->local_cachealloc;
g->m->mcache->local_cachealloc = 0;
if(acct) {
mstats.heap_alloc -= s->npages<<PageShift;
mstats.heap_objects--;
}
- MHeap_FreeSpanLocked(h, s);
- runtime·unlock(h);
+ MHeap_FreeSpanLocked(h, s, true, true);
+ runtime·unlock(&h->lock);
}
static void
@@ -488,13 +490,16 @@ mheap_free_m(G *gp)
void
runtime·MHeap_Free(MHeap *h, MSpan *s, int32 acct)
{
+ void (*fn)(G*);
+
if(g == g->m->g0) {
mheap_free(h, s, acct);
} else {
g->m->ptrarg[0] = h;
g->m->ptrarg[1] = s;
g->m->scalararg[0] = acct;
- runtime·mcall(mheap_free_m);
+ fn = mheap_free_m;
+ runtime·mcall(&fn);
}
}
@@ -504,17 +509,17 @@ runtime·MHeap_FreeStack(MHeap *h, MSpan *s)
if(g != g->m->g0)
runtime·throw("mheap_freestack not on M stack");
s->needzero = 1;
- runtime·lock(h);
+ runtime·lock(&h->lock);
mstats.stacks_inuse -= s->npages<<PageShift;
- MHeap_FreeSpanLocked(h, s);
- runtime·unlock(h);
+ MHeap_FreeSpanLocked(h, s, true, true);
+ runtime·unlock(&h->lock);
}
static void
-MHeap_FreeSpanLocked(MHeap *h, MSpan *s)
+MHeap_FreeSpanLocked(MHeap *h, MSpan *s, bool acctinuse, bool acctidle)
{
MSpan *t;
- PageID p;
+ pageID p;
switch(s->state) {
case MSpanStack:
@@ -532,8 +537,10 @@ MHeap_FreeSpanLocked(MHeap *h, MSpan *s)
runtime·throw("MHeap_FreeSpanLocked - invalid span state");
break;
}
- mstats.heap_inuse -= s->npages<<PageShift;
- mstats.heap_idle += s->npages<<PageShift;
+ if(acctinuse)
+ mstats.heap_inuse -= s->npages<<PageShift;
+ if(acctidle)
+ mstats.heap_idle += s->npages<<PageShift;
s->state = MSpanFree;
runtime·MSpanList_Remove(s);
// Stamp newly unused spans. The scavenger will use that
@@ -572,13 +579,6 @@ MHeap_FreeSpanLocked(MHeap *h, MSpan *s)
runtime·MSpanList_Insert(&h->freelarge, s);
}
-static void
-forcegchelper(Note *note)
-{
- runtime·gc(1);
- runtime·notewakeup(note);
-}
-
static uintptr
scavengelist(MSpan *list, uint64 now, uint64 limit)
{
@@ -601,103 +601,41 @@ scavengelist(MSpan *list, uint64 now, uint64 limit)
return sumreleased;
}
-static void
-scavenge(int32 k, uint64 now, uint64 limit)
+void
+runtime·MHeap_Scavenge(int32 k, uint64 now, uint64 limit)
{
uint32 i;
uintptr sumreleased;
MHeap *h;
h = &runtime·mheap;
+ runtime·lock(&h->lock);
sumreleased = 0;
for(i=0; i < nelem(h->free); i++)
sumreleased += scavengelist(&h->free[i], now, limit);
sumreleased += scavengelist(&h->freelarge, now, limit);
+ runtime·unlock(&h->lock);
if(runtime·debug.gctrace > 0) {
if(sumreleased > 0)
runtime·printf("scvg%d: %D MB released\n", k, (uint64)sumreleased>>20);
+ // TODO(dvyukov): these stats are incorrect as we don't subtract stack usage from heap.
+ // But we can't call ReadMemStats on g0 holding locks.
runtime·printf("scvg%d: inuse: %D, idle: %D, sys: %D, released: %D, consumed: %D (MB)\n",
k, mstats.heap_inuse>>20, mstats.heap_idle>>20, mstats.heap_sys>>20,
mstats.heap_released>>20, (mstats.heap_sys - mstats.heap_released)>>20);
}
}
-static void
-scavenge_m(G *gp)
-{
- runtime·lock(&runtime·mheap);
- scavenge(g->m->scalararg[0], g->m->scalararg[1], g->m->scalararg[2]);
- runtime·unlock(&runtime·mheap);
- runtime·gogo(&gp->sched);
-}
-
-static FuncVal forcegchelperv = {(void(*)(void))forcegchelper};
-
-// Release (part of) unused memory to OS.
-// Goroutine created at startup.
-// Loop forever.
-void
-runtime·MHeap_Scavenger(void)
-{
- uint64 tick, forcegc, limit;
- int64 unixnow;
- int32 k;
- Note note, *notep;
-
- g->issystem = true;
- g->isbackground = true;
-
- // If we go two minutes without a garbage collection, force one to run.
- forcegc = 2*60*1e9;
- // If a span goes unused for 5 minutes after a garbage collection,
- // we hand it back to the operating system.
- limit = 5*60*1e9;
- // Make wake-up period small enough for the sampling to be correct.
- if(forcegc < limit)
- tick = forcegc/2;
- else
- tick = limit/2;
-
- for(k=0;; k++) {
- runtime·noteclear(&note);
- runtime·notetsleepg(&note, tick);
-
- unixnow = runtime·unixnanotime();
- if(unixnow - mstats.last_gc > forcegc) {
- // The scavenger can not block other goroutines,
- // otherwise deadlock detector can fire spuriously.
- // GC blocks other goroutines via the runtime·worldsema.
- runtime·noteclear(&note);
- notep = &note;
- runtime·newproc1(&forcegchelperv, (byte*)&notep, sizeof(notep), 0, runtime·MHeap_Scavenger);
- runtime·notetsleepg(&note, -1);
- if(runtime·debug.gctrace > 0)
- runtime·printf("scvg%d: GC forced\n", k);
- }
- g->m->locks++; // ensure that we are on the same m while filling arguments
- g->m->scalararg[0] = k;
- g->m->scalararg[1] = runtime·nanotime();
- g->m->scalararg[2] = limit;
- runtime·mcall(scavenge_m);
- g->m->locks--;
- }
-}
-
void
-runtime∕debug·freeOSMemory(void)
+runtime·scavenge_m(void)
{
- runtime·gc(2); // force GC and do eager sweep
-
- g->m->scalararg[0] = -1;
- g->m->scalararg[1] = ~(uintptr)0;
- g->m->scalararg[2] = 0;
- runtime·mcall(scavenge_m);
+ runtime·MHeap_Scavenge(-1, ~(uintptr)0, 0);
}
// Initialize a new span with the given start and npages.
void
-runtime·MSpan_Init(MSpan *span, PageID start, uintptr npages)
+runtime·MSpan_Init(MSpan *span, pageID start, uintptr npages)
{
span->next = nil;
span->prev = nil;
@@ -865,12 +803,12 @@ runtime·addfinalizer(void *p, FuncVal *f, uintptr nret, Type *fint, PtrType *ot
runtime·lock(&runtime·mheap.speciallock);
s = runtime·FixAlloc_Alloc(&runtime·mheap.specialfinalizeralloc);
runtime·unlock(&runtime·mheap.speciallock);
- s->kind = KindSpecialFinalizer;
+ s->special.kind = KindSpecialFinalizer;
s->fn = f;
s->nret = nret;
s->fint = fint;
s->ot = ot;
- if(addspecial(p, s))
+ if(addspecial(p, &s->special))
return true;
// There was an old finalizer
@@ -896,16 +834,23 @@ runtime·removefinalizer(void *p)
// Set the heap profile bucket associated with addr to b.
void
-runtime·setprofilebucket(void *p, Bucket *b)
-{
+runtime·setprofilebucket_m(void)
+{
+ void *p;
+ Bucket *b;
SpecialProfile *s;
+
+ p = g->m->ptrarg[0];
+ b = g->m->ptrarg[1];
+ g->m->ptrarg[0] = nil;
+ g->m->ptrarg[1] = nil;
runtime·lock(&runtime·mheap.speciallock);
s = runtime·FixAlloc_Alloc(&runtime·mheap.specialprofilealloc);
runtime·unlock(&runtime·mheap.speciallock);
- s->kind = KindSpecialProfile;
+ s->special.kind = KindSpecialProfile;
s->b = b;
- if(!addspecial(p, s))
+ if(!addspecial(p, &s->special))
runtime·throw("setprofilebucket: profile already set");
}
@@ -928,7 +873,7 @@ runtime·freespecial(Special *s, void *p, uintptr size, bool freed)
return false; // don't free p until finalizer is done
case KindSpecialProfile:
sp = (SpecialProfile*)s;
- runtime·MProf_Free(sp->b, size, freed);
+ runtime·mProf_Free(sp->b, size, freed);
runtime·lock(&runtime·mheap.speciallock);
runtime·FixAlloc_Free(&runtime·mheap.specialprofilealloc, sp);
runtime·unlock(&runtime·mheap.speciallock);
diff --git a/src/pkg/runtime/mprof.go b/src/pkg/runtime/mprof.go
new file mode 100644
index 0000000000..7177c84592
--- /dev/null
+++ b/src/pkg/runtime/mprof.go
@@ -0,0 +1,660 @@
+// 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.
+
+// Malloc profiling.
+// Patterned after tcmalloc's algorithms; shorter code.
+
+package runtime
+
+import (
+ "unsafe"
+)
+
+// NOTE(rsc): Everything here could use cas if contention became an issue.
+var proflock mutex
+
+// All memory allocations are local and do not escape outside of the profiler.
+// The profiler is forbidden from referring to garbage-collected memory.
+
+const (
+ // profile types
+ memProfile bucketType = 1 + iota
+ blockProfile
+
+ // size of bucket hash table
+ buckHashSize = 179999
+
+ // max depth of stack to record in bucket
+ maxStack = 32
+)
+
+type bucketType int
+
+// A bucket holds per-call-stack profiling information.
+// The representation is a bit sleazy, inherited from C.
+// This struct defines the bucket header. It is followed in
+// memory by the stack words and then the actual record
+// data, either a memRecord or a blockRecord.
+//
+// Per-call-stack profiling information.
+// Lookup by hashing call stack into a linked-list hash table.
+type bucket struct {
+ next *bucket
+ allnext *bucket
+ typ bucketType // memBucket or blockBucket
+ hash uintptr
+ size uintptr
+ nstk uintptr
+}
+
+// A memRecord is the bucket data for a bucket of type memProfile,
+// part of the memory profile.
+type memRecord struct {
+ // The following complex 3-stage scheme of stats accumulation
+ // is required to obtain a consistent picture of mallocs and frees
+ // for some point in time.
+ // The problem is that mallocs come in real time, while frees
+ // come only after a GC during concurrent sweeping. So if we would
+ // naively count them, we would get a skew toward mallocs.
+ //
+ // Mallocs are accounted in recent stats.
+ // Explicit frees are accounted in recent stats.
+ // GC frees are accounted in prev stats.
+ // After GC prev stats are added to final stats and
+ // recent stats are moved into prev stats.
+ allocs uintptr
+ frees uintptr
+ alloc_bytes uintptr
+ free_bytes uintptr
+
+ // changes between next-to-last GC and last GC
+ prev_allocs uintptr
+ prev_frees uintptr
+ prev_alloc_bytes uintptr
+ prev_free_bytes uintptr
+
+ // changes since last GC
+ recent_allocs uintptr
+ recent_frees uintptr
+ recent_alloc_bytes uintptr
+ recent_free_bytes uintptr
+}
+
+// A blockRecord is the bucket data for a bucket of type blockProfile,
+// part of the blocking profile.
+type blockRecord struct {
+ count int64
+ cycles int64
+}
+
+var (
+ mbuckets *bucket // memory profile buckets
+ bbuckets *bucket // blocking profile buckets
+ buckhash *[179999]*bucket
+ bucketmem uintptr
+)
+
+// newBucket allocates a bucket with the given type and number of stack entries.
+func newBucket(typ bucketType, nstk int) *bucket {
+ size := unsafe.Sizeof(bucket{}) + uintptr(nstk)*unsafe.Sizeof(uintptr(0))
+ switch typ {
+ default:
+ gothrow("invalid profile bucket type")
+ case memProfile:
+ size += unsafe.Sizeof(memRecord{})
+ case blockProfile:
+ size += unsafe.Sizeof(blockRecord{})
+ }
+
+ b := (*bucket)(persistentalloc(size, 0, &memstats.buckhash_sys))
+ bucketmem += size
+ b.typ = typ
+ b.nstk = uintptr(nstk)
+ return b
+}
+
+// stk returns the slice in b holding the stack.
+func (b *bucket) stk() []uintptr {
+ stk := (*[maxStack]uintptr)(add(unsafe.Pointer(b), unsafe.Sizeof(*b)))
+ return stk[:b.nstk:b.nstk]
+}
+
+// mp returns the memRecord associated with the memProfile bucket b.
+func (b *bucket) mp() *memRecord {
+ if b.typ != memProfile {
+ gothrow("bad use of bucket.mp")
+ }
+ data := add(unsafe.Pointer(b), unsafe.Sizeof(*b)+b.nstk*unsafe.Sizeof(uintptr(0)))
+ return (*memRecord)(data)
+}
+
+// bp returns the blockRecord associated with the blockProfile bucket b.
+func (b *bucket) bp() *blockRecord {
+ if b.typ != blockProfile {
+ gothrow("bad use of bucket.bp")
+ }
+ data := add(unsafe.Pointer(b), unsafe.Sizeof(*b)+b.nstk*unsafe.Sizeof(uintptr(0)))
+ return (*blockRecord)(data)
+}
+
+// Return the bucket for stk[0:nstk], allocating new bucket if needed.
+func stkbucket(typ bucketType, size uintptr, stk []uintptr, alloc bool) *bucket {
+ if buckhash == nil {
+ buckhash = (*[buckHashSize]*bucket)(sysAlloc(unsafe.Sizeof(*buckhash), &memstats.buckhash_sys))
+ if buckhash == nil {
+ gothrow("runtime: cannot allocate memory")
+ }
+ }
+
+ // Hash stack.
+ var h uintptr
+ for _, pc := range stk {
+ h += pc
+ h += h << 10
+ h ^= h >> 6
+ }
+ // hash in size
+ h += size
+ h += h << 10
+ h ^= h >> 6
+ // finalize
+ h += h << 3
+ h ^= h >> 11
+
+ i := int(h % buckHashSize)
+ for b := buckhash[i]; b != nil; b = b.next {
+ if b.typ == typ && b.hash == h && b.size == size && eqslice(b.stk(), stk) {
+ return b
+ }
+ }
+
+ if !alloc {
+ return nil
+ }
+
+ // Create new bucket.
+ b := newBucket(typ, len(stk))
+ copy(b.stk(), stk)
+ b.hash = h
+ b.size = size
+ b.next = buckhash[i]
+ buckhash[i] = b
+ if typ == memProfile {
+ b.allnext = mbuckets
+ mbuckets = b
+ } else {
+ b.allnext = bbuckets
+ bbuckets = b
+ }
+ return b
+}
+
+func sysAlloc(n uintptr, stat *uint64) unsafe.Pointer
+
+func eqslice(x, y []uintptr) bool {
+ if len(x) != len(y) {
+ return false
+ }
+ for i, xi := range x {
+ if xi != y[i] {
+ return false
+ }
+ }
+ return true
+}
+
+func mprof_GC() {
+ for b := mbuckets; b != nil; b = b.allnext {
+ mp := b.mp()
+ mp.allocs += mp.prev_allocs
+ mp.frees += mp.prev_frees
+ mp.alloc_bytes += mp.prev_alloc_bytes
+ mp.free_bytes += mp.prev_free_bytes
+
+ mp.prev_allocs = mp.recent_allocs
+ mp.prev_frees = mp.recent_frees
+ mp.prev_alloc_bytes = mp.recent_alloc_bytes
+ mp.prev_free_bytes = mp.recent_free_bytes
+
+ mp.recent_allocs = 0
+ mp.recent_frees = 0
+ mp.recent_alloc_bytes = 0
+ mp.recent_free_bytes = 0
+ }
+}
+
+// Record that a gc just happened: all the 'recent' statistics are now real.
+func mProf_GC() {
+ lock(&proflock)
+ mprof_GC()
+ unlock(&proflock)
+}
+
+// Called by malloc to record a profiled block.
+func mProf_Malloc(p unsafe.Pointer, size uintptr) {
+ var stk [maxStack]uintptr
+ nstk := callers(1, &stk[0], len(stk))
+ lock(&proflock)
+ b := stkbucket(memProfile, size, stk[:nstk], true)
+ mp := b.mp()
+ mp.recent_allocs++
+ mp.recent_alloc_bytes += size
+ unlock(&proflock)
+
+ // Setprofilebucket locks a bunch of other mutexes, so we call it outside of proflock.
+ // This reduces potential contention and chances of deadlocks.
+ // Since the object must be alive during call to mProf_Malloc,
+ // it's fine to do this non-atomically.
+ setprofilebucket(p, b)
+}
+
+func setprofilebucket_m() // mheap.c
+
+func setprofilebucket(p unsafe.Pointer, b *bucket) {
+ g := getg()
+ g.m.ptrarg[0] = p
+ g.m.ptrarg[1] = unsafe.Pointer(b)
+ onM(setprofilebucket_m)
+}
+
+// Called when freeing a profiled block.
+func mProf_Free(b *bucket, size uintptr, freed bool) {
+ lock(&proflock)
+ mp := b.mp()
+ if freed {
+ mp.recent_frees++
+ mp.recent_free_bytes += size
+ } else {
+ mp.prev_frees++
+ mp.prev_free_bytes += size
+ }
+ unlock(&proflock)
+}
+
+var blockprofilerate uint64 // in CPU ticks
+
+// SetBlockProfileRate controls the fraction of goroutine blocking events
+// that are reported in the blocking profile. The profiler aims to sample
+// an average of one blocking event per rate nanoseconds spent blocked.
+//
+// To include every blocking event in the profile, pass rate = 1.
+// To turn off profiling entirely, pass rate <= 0.
+func SetBlockProfileRate(rate int) {
+ var r int64
+ if rate <= 0 {
+ r = 0 // disable profiling
+ } else {
+ // convert ns to cycles, use float64 to prevent overflow during multiplication
+ r = int64(float64(rate) * float64(tickspersecond()) / (1000 * 1000 * 1000))
+ if r == 0 {
+ r = 1
+ }
+ }
+
+ atomicstore64(&blockprofilerate, uint64(r))
+}
+
+func fastrand1() uint32 // assembly
+func readgstatus(*g) uint32 // proc.c
+
+func blockevent(cycles int64, skip int) {
+ if cycles <= 0 {
+ return
+ }
+ rate := int64(atomicload64(&blockprofilerate))
+ if rate <= 0 || (rate > cycles && int64(fastrand1())%rate > cycles) {
+ return
+ }
+ gp := getg()
+ var nstk int
+ var stk [maxStack]uintptr
+ if gp.m.curg == nil || gp.m.curg == gp {
+ nstk = callers(skip, &stk[0], len(stk))
+ } else {
+ nstk = gcallers(gp.m.curg, skip, &stk[0], len(stk))
+ }
+ lock(&proflock)
+ b := stkbucket(blockProfile, 0, stk[:nstk], true)
+ b.bp().count++
+ b.bp().cycles += cycles
+ unlock(&proflock)
+}
+
+// Go interface to profile data.
+
+// A StackRecord describes a single execution stack.
+type StackRecord struct {
+ Stack0 [32]uintptr // stack trace for this record; ends at first 0 entry
+}
+
+// Stack returns the stack trace associated with the record,
+// a prefix of r.Stack0.
+func (r *StackRecord) Stack() []uintptr {
+ for i, v := range r.Stack0 {
+ if v == 0 {
+ return r.Stack0[0:i]
+ }
+ }
+ return r.Stack0[0:]
+}
+
+// MemProfileRate controls the fraction of memory allocations
+// that are recorded and reported in the memory profile.
+// The profiler aims to sample an average of
+// one allocation per MemProfileRate bytes allocated.
+//
+// To include every allocated block in the profile, set MemProfileRate to 1.
+// To turn off profiling entirely, set MemProfileRate to 0.
+//
+// The tools that process the memory profiles assume that the
+// profile rate is constant across the lifetime of the program
+// and equal to the current value. Programs that change the
+// memory profiling rate should do so just once, as early as
+// possible in the execution of the program (for example,
+// at the beginning of main).
+var MemProfileRate int = 512 * 1024
+
+// A MemProfileRecord describes the live objects allocated
+// by a particular call sequence (stack trace).
+type MemProfileRecord struct {
+ AllocBytes, FreeBytes int64 // number of bytes allocated, freed
+ AllocObjects, FreeObjects int64 // number of objects allocated, freed
+ Stack0 [32]uintptr // stack trace for this record; ends at first 0 entry
+}
+
+// InUseBytes returns the number of bytes in use (AllocBytes - FreeBytes).
+func (r *MemProfileRecord) InUseBytes() int64 { return r.AllocBytes - r.FreeBytes }
+
+// InUseObjects returns the number of objects in use (AllocObjects - FreeObjects).
+func (r *MemProfileRecord) InUseObjects() int64 {
+ return r.AllocObjects - r.FreeObjects
+}
+
+// Stack returns the stack trace associated with the record,
+// a prefix of r.Stack0.
+func (r *MemProfileRecord) Stack() []uintptr {
+ for i, v := range r.Stack0 {
+ if v == 0 {
+ return r.Stack0[0:i]
+ }
+ }
+ return r.Stack0[0:]
+}
+
+// MemProfile returns n, the number of records in the current memory profile.
+// If len(p) >= n, MemProfile copies the profile into p and returns n, true.
+// If len(p) < n, MemProfile does not change p and returns n, false.
+//
+// If inuseZero is true, the profile includes allocation records
+// where r.AllocBytes > 0 but r.AllocBytes == r.FreeBytes.
+// These are sites where memory was allocated, but it has all
+// been released back to the runtime.
+//
+// Most clients should use the runtime/pprof package or
+// the testing package's -test.memprofile flag instead
+// of calling MemProfile directly.
+func MemProfile(p []MemProfileRecord, inuseZero bool) (n int, ok bool) {
+ lock(&proflock)
+ clear := true
+ for b := mbuckets; b != nil; b = b.allnext {
+ mp := b.mp()
+ if inuseZero || mp.alloc_bytes != mp.free_bytes {
+ n++
+ }
+ if mp.allocs != 0 || mp.frees != 0 {
+ clear = false
+ }
+ }
+ if clear {
+ // Absolutely no data, suggesting that a garbage collection
+ // has not yet happened. In order to allow profiling when
+ // garbage collection is disabled from the beginning of execution,
+ // accumulate stats as if a GC just happened, and recount buckets.
+ mprof_GC()
+ mprof_GC()
+ n = 0
+ for b := mbuckets; b != nil; b = b.allnext {
+ mp := b.mp()
+ if inuseZero || mp.alloc_bytes != mp.free_bytes {
+ n++
+ }
+ }
+ }
+ if n <= len(p) {
+ ok = true
+ idx := 0
+ for b := mbuckets; b != nil; b = b.allnext {
+ mp := b.mp()
+ if inuseZero || mp.alloc_bytes != mp.free_bytes {
+ record(&p[idx], b)
+ idx++
+ }
+ }
+ }
+ unlock(&proflock)
+ return
+}
+
+// Write b's data to r.
+func record(r *MemProfileRecord, b *bucket) {
+ mp := b.mp()
+ r.AllocBytes = int64(mp.alloc_bytes)
+ r.FreeBytes = int64(mp.free_bytes)
+ r.AllocObjects = int64(mp.allocs)
+ r.FreeObjects = int64(mp.frees)
+ copy(r.Stack0[:], b.stk())
+ for i := int(b.nstk); i < len(r.Stack0); i++ {
+ r.Stack0[i] = 0
+ }
+}
+
+func iterate_memprof(fn func(*bucket, uintptr, *uintptr, uintptr, uintptr, uintptr)) {
+ lock(&proflock)
+ for b := mbuckets; b != nil; b = b.allnext {
+ mp := b.mp()
+ fn(b, uintptr(b.nstk), &b.stk()[0], b.size, mp.allocs, mp.frees)
+ }
+ unlock(&proflock)
+}
+
+// BlockProfileRecord describes blocking events originated
+// at a particular call sequence (stack trace).
+type BlockProfileRecord struct {
+ Count int64
+ Cycles int64
+ StackRecord
+}
+
+// BlockProfile returns n, the number of records in the current blocking profile.
+// If len(p) >= n, BlockProfile copies the profile into p and returns n, true.
+// If len(p) < n, BlockProfile does not change p and returns n, false.
+//
+// Most clients should use the runtime/pprof package or
+// the testing package's -test.blockprofile flag instead
+// of calling BlockProfile directly.
+func BlockProfile(p []BlockProfileRecord) (n int, ok bool) {
+ lock(&proflock)
+ for b := bbuckets; b != nil; b = b.allnext {
+ n++
+ }
+ if n <= len(p) {
+ ok = true
+ for b := bbuckets; b != nil; b = b.allnext {
+ bp := b.bp()
+ r := &p[0]
+ r.Count = int64(bp.count)
+ r.Cycles = int64(bp.cycles)
+ i := copy(r.Stack0[:], b.stk())
+ for ; i < len(r.Stack0); i++ {
+ r.Stack0[i] = 0
+ }
+ p = p[1:]
+ }
+ }
+ unlock(&proflock)
+ return
+}
+
+// ThreadCreateProfile returns n, the number of records in the thread creation profile.
+// If len(p) >= n, ThreadCreateProfile copies the profile into p and returns n, true.
+// If len(p) < n, ThreadCreateProfile does not change p and returns n, false.
+//
+// Most clients should use the runtime/pprof package instead
+// of calling ThreadCreateProfile directly.
+func ThreadCreateProfile(p []StackRecord) (n int, ok bool) {
+ first := (*m)(atomicloadp(unsafe.Pointer(&allm)))
+ for mp := first; mp != nil; mp = mp.alllink {
+ n++
+ }
+ if n <= len(p) {
+ ok = true
+ i := 0
+ for mp := first; mp != nil; mp = mp.alllink {
+ for s := range mp.createstack {
+ p[i].Stack0[s] = uintptr(mp.createstack[s])
+ }
+ i++
+ }
+ }
+ return
+}
+
+var allgs []*g // proc.c
+
+// GoroutineProfile returns n, the number of records in the active goroutine stack profile.
+// If len(p) >= n, GoroutineProfile copies the profile into p and returns n, true.
+// If len(p) < n, GoroutineProfile does not change p and returns n, false.
+//
+// Most clients should use the runtime/pprof package instead
+// of calling GoroutineProfile directly.
+func GoroutineProfile(p []StackRecord) (n int, ok bool) {
+ sp := getcallersp(unsafe.Pointer(&p))
+ pc := getcallerpc(unsafe.Pointer(&p))
+
+ n = NumGoroutine()
+ if n <= len(p) {
+ gp := getg()
+ semacquire(&worldsema, false)
+ gp.m.gcing = 1
+ onM(stoptheworld)
+
+ n = NumGoroutine()
+ if n <= len(p) {
+ ok = true
+ r := p
+ saveg(pc, sp, gp, &r[0])
+ r = r[1:]
+ for _, gp1 := range allgs {
+ if gp1 == gp || readgstatus(gp1) == _Gdead {
+ continue
+ }
+ saveg(^uintptr(0), ^uintptr(0), gp1, &r[0])
+ r = r[1:]
+ }
+ }
+
+ gp.m.gcing = 0
+ semrelease(&worldsema)
+ onM(starttheworld)
+ }
+
+ return n, ok
+}
+
+func saveg(pc, sp uintptr, gp *g, r *StackRecord) {
+ n := gentraceback(pc, sp, 0, gp, 0, &r.Stack0[0], len(r.Stack0), nil, nil, false)
+ if n < len(r.Stack0) {
+ r.Stack0[n] = 0
+ }
+}
+
+// Stack formats a stack trace of the calling goroutine into buf
+// and returns the number of bytes written to buf.
+// If all is true, Stack formats stack traces of all other goroutines
+// into buf after the trace for the current goroutine.
+func Stack(buf []byte, all bool) int {
+ sp := getcallersp(unsafe.Pointer(&buf))
+ pc := getcallerpc(unsafe.Pointer(&buf))
+ mp := acquirem()
+ gp := mp.curg
+ if all {
+ semacquire(&worldsema, false)
+ mp.gcing = 1
+ releasem(mp)
+ onM(stoptheworld)
+ if mp != acquirem() {
+ gothrow("Stack: rescheduled")
+ }
+ }
+
+ n := 0
+ if len(buf) > 0 {
+ gp.writebuf = buf[0:0:len(buf)]
+ goroutineheader(gp)
+ traceback(pc, sp, 0, gp)
+ if all {
+ tracebackothers(gp)
+ }
+ n = len(gp.writebuf)
+ gp.writebuf = nil
+ }
+
+ if all {
+ mp.gcing = 0
+ semrelease(&worldsema)
+ onM(starttheworld)
+ }
+ releasem(mp)
+ return n
+}
+
+// Tracing of alloc/free/gc.
+
+var tracelock mutex
+
+func tracealloc(p unsafe.Pointer, size uintptr, typ *_type) {
+ lock(&tracelock)
+ gp := getg()
+ gp.m.traceback = 2
+ if typ == nil {
+ print("tracealloc(", p, ", ", hex(size), ")\n")
+ } else {
+ print("tracealloc(", p, ", ", hex(size), ", ", *typ._string, ")\n")
+ }
+ if gp.m.curg == nil || gp == gp.m.curg {
+ goroutineheader(gp)
+ traceback(getcallerpc(unsafe.Pointer(&p)), getcallersp(unsafe.Pointer(&p)), 0, gp)
+ } else {
+ goroutineheader(gp.m.curg)
+ traceback(^uintptr(0), ^uintptr(0), 0, gp.m.curg)
+ }
+ print("\n")
+ gp.m.traceback = 0
+ unlock(&tracelock)
+}
+
+func tracefree(p unsafe.Pointer, size uintptr) {
+ lock(&tracelock)
+ gp := getg()
+ gp.m.traceback = 2
+ print("tracefree(", p, ", ", hex(size), ")\n")
+ goroutineheader(gp)
+ traceback(getcallerpc(unsafe.Pointer(&p)), getcallersp(unsafe.Pointer(&p)), 0, gp)
+ print("\n")
+ gp.m.traceback = 0
+ unlock(&tracelock)
+}
+
+func tracegc() {
+ lock(&tracelock)
+ gp := getg()
+ gp.m.traceback = 2
+ print("tracegc()\n")
+ // running on m->g0 stack; show all non-g0 goroutines
+ tracebackothers(gp)
+ print("end tracegc\n")
+ print("\n")
+ gp.m.traceback = 0
+ unlock(&tracelock)
+}
diff --git a/src/pkg/runtime/mprof.goc b/src/pkg/runtime/mprof.goc
deleted file mode 100644
index 69187f2a74..0000000000
--- a/src/pkg/runtime/mprof.goc
+++ /dev/null
@@ -1,489 +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.
-
-// Malloc profiling.
-// Patterned after tcmalloc's algorithms; shorter code.
-
-package runtime
-#include "runtime.h"
-#include "arch_GOARCH.h"
-#include "malloc.h"
-#include "mprof.h"
-#include "defs_GOOS_GOARCH.h"
-#include "type.h"
-
-// NOTE(rsc): Everything here could use cas if contention became an issue.
-static Lock proflock;
-
-// All memory allocations are local and do not escape outside of the profiler.
-// The profiler is forbidden from referring to garbage-collected memory.
-
-enum { MProf, BProf }; // profile types
-
-enum {
- BuckHashSize = 179999,
-};
-static Bucket **buckhash;
-static Bucket *mbuckets; // memory profile buckets
-static Bucket *bbuckets; // blocking profile buckets
-static uintptr bucketmem;
-
-// Return the bucket for stk[0:nstk], allocating new bucket if needed.
-static Bucket*
-stkbucket(int32 typ, uintptr size, uintptr *stk, int32 nstk, bool alloc)
-{
- int32 i;
- uintptr h;
- Bucket *b;
-
- if(buckhash == nil) {
- buckhash = runtime·SysAlloc(BuckHashSize*sizeof buckhash[0], &mstats.buckhash_sys);
- if(buckhash == nil)
- runtime·throw("runtime: cannot allocate memory");
- }
-
- // Hash stack.
- h = 0;
- for(i=0; i<nstk; i++) {
- h += stk[i];
- h += h<<10;
- h ^= h>>6;
- }
- // hash in size
- h += size;
- h += h<<10;
- h ^= h>>6;
- // finalize
- h += h<<3;
- h ^= h>>11;
-
- i = h%BuckHashSize;
- for(b = buckhash[i]; b; b=b->next)
- if(b->typ == typ && b->hash == h && b->size == size && b->nstk == nstk &&
- runtime·mcmp((byte*)b->stk, (byte*)stk, nstk*sizeof stk[0]) == 0)
- return b;
-
- if(!alloc)
- return nil;
-
- b = runtime·persistentalloc(sizeof *b + nstk*sizeof stk[0], 0, &mstats.buckhash_sys);
- bucketmem += sizeof *b + nstk*sizeof stk[0];
- runtime·memmove(b->stk, stk, nstk*sizeof stk[0]);
- b->typ = typ;
- b->hash = h;
- b->size = size;
- b->nstk = nstk;
- b->next = buckhash[i];
- buckhash[i] = b;
- if(typ == MProf) {
- b->allnext = mbuckets;
- mbuckets = b;
- } else {
- b->allnext = bbuckets;
- bbuckets = b;
- }
- return b;
-}
-
-static void
-MProf_GC(void)
-{
- Bucket *b;
-
- for(b=mbuckets; b; b=b->allnext) {
- b->allocs += b->prev_allocs;
- b->frees += b->prev_frees;
- b->alloc_bytes += b->prev_alloc_bytes;
- b->free_bytes += b->prev_free_bytes;
-
- b->prev_allocs = b->recent_allocs;
- b->prev_frees = b->recent_frees;
- b->prev_alloc_bytes = b->recent_alloc_bytes;
- b->prev_free_bytes = b->recent_free_bytes;
-
- b->recent_allocs = 0;
- b->recent_frees = 0;
- b->recent_alloc_bytes = 0;
- b->recent_free_bytes = 0;
- }
-}
-
-// Record that a gc just happened: all the 'recent' statistics are now real.
-void
-runtime·MProf_GC(void)
-{
- runtime·lock(&proflock);
- MProf_GC();
- runtime·unlock(&proflock);
-}
-
-// Called by malloc to record a profiled block.
-void
-runtime·MProf_Malloc(void *p, uintptr size)
-{
- uintptr stk[32];
- Bucket *b;
- int32 nstk;
-
- nstk = runtime·callers(1, stk, nelem(stk));
- runtime·lock(&proflock);
- b = stkbucket(MProf, size, stk, nstk, true);
- b->recent_allocs++;
- b->recent_alloc_bytes += size;
- runtime·unlock(&proflock);
-
- // Setprofilebucket locks a bunch of other mutexes, so we call it outside of proflock.
- // This reduces potential contention and chances of deadlocks.
- // Since the object must be alive during call to MProf_Malloc,
- // it's fine to do this non-atomically.
- runtime·setprofilebucket(p, b);
-}
-
-// Called by malloc to record a profiled block.
-void
-runtime·mprofMalloc(void)
-{
- uintptr stk[32];
- Bucket *b;
- int32 nstk;
- uintptr size;
- void *p;
-
- size = g->m->scalararg[0];
- p = g->m->ptrarg[0];
- g->m->ptrarg[0] = nil;
-
- if(g->m->curg == nil)
- nstk = runtime·callers(1, stk, nelem(stk));
- else
- nstk = runtime·gcallers(g->m->curg, 1, stk, nelem(stk));
- runtime·lock(&proflock);
- b = stkbucket(MProf, size, stk, nstk, true);
- b->recent_allocs++;
- b->recent_alloc_bytes += size;
- runtime·unlock(&proflock);
-
- // Setprofilebucket locks a bunch of other mutexes, so we call it outside of proflock.
- // This reduces potential contention and chances of deadlocks.
- // Since the object must be alive during call to MProf_Malloc,
- // it's fine to do this non-atomically.
- runtime·setprofilebucket(p, b);
-}
-
-// Called when freeing a profiled block.
-void
-runtime·MProf_Free(Bucket *b, uintptr size, bool freed)
-{
- runtime·lock(&proflock);
- if(freed) {
- b->recent_frees++;
- b->recent_free_bytes += size;
- } else {
- b->prev_frees++;
- b->prev_free_bytes += size;
- }
- runtime·unlock(&proflock);
-}
-
-int64 runtime·blockprofilerate; // in CPU ticks
-
-void
-runtime·SetBlockProfileRate(intgo rate)
-{
- int64 r;
-
- if(rate <= 0)
- r = 0; // disable profiling
- else {
- // convert ns to cycles, use float64 to prevent overflow during multiplication
- r = (float64)rate*runtime·tickspersecond()/(1000*1000*1000);
- if(r == 0)
- r = 1;
- }
- runtime·atomicstore64((uint64*)&runtime·blockprofilerate, r);
-}
-
-void
-runtime·blockevent(int64 cycles, int32 skip)
-{
- int32 nstk;
- int64 rate;
- uintptr stk[32];
- Bucket *b;
-
- if(cycles <= 0)
- return;
- rate = runtime·atomicload64((uint64*)&runtime·blockprofilerate);
- if(rate <= 0 || (rate > cycles && runtime·fastrand1()%rate > cycles))
- return;
-
- nstk = runtime·callers(skip, stk, nelem(stk));
- runtime·lock(&proflock);
- b = stkbucket(BProf, 0, stk, nstk, true);
- b->count++;
- b->cycles += cycles;
- runtime·unlock(&proflock);
-}
-
-// Go interface to profile data. (Declared in debug.go)
-
-// Must match MemProfileRecord in debug.go.
-typedef struct Record Record;
-struct Record {
- int64 alloc_bytes, free_bytes;
- int64 alloc_objects, free_objects;
- uintptr stk[32];
-};
-
-// Write b's data to r.
-static void
-record(Record *r, Bucket *b)
-{
- int32 i;
-
- r->alloc_bytes = b->alloc_bytes;
- r->free_bytes = b->free_bytes;
- r->alloc_objects = b->allocs;
- r->free_objects = b->frees;
- for(i=0; i<b->nstk && i<nelem(r->stk); i++)
- r->stk[i] = b->stk[i];
- for(; i<nelem(r->stk); i++)
- r->stk[i] = 0;
-}
-
-func MemProfile(p Slice, include_inuse_zero bool) (n int, ok bool) {
- Bucket *b;
- Record *r;
- bool clear;
-
- runtime·lock(&proflock);
- n = 0;
- clear = true;
- for(b=mbuckets; b; b=b->allnext) {
- if(include_inuse_zero || b->alloc_bytes != b->free_bytes)
- n++;
- if(b->allocs != 0 || b->frees != 0)
- clear = false;
- }
- if(clear) {
- // Absolutely no data, suggesting that a garbage collection
- // has not yet happened. In order to allow profiling when
- // garbage collection is disabled from the beginning of execution,
- // accumulate stats as if a GC just happened, and recount buckets.
- MProf_GC();
- MProf_GC();
- n = 0;
- for(b=mbuckets; b; b=b->allnext)
- if(include_inuse_zero || b->alloc_bytes != b->free_bytes)
- n++;
- }
- ok = false;
- if(n <= p.len) {
- ok = true;
- r = (Record*)p.array;
- for(b=mbuckets; b; b=b->allnext)
- if(include_inuse_zero || b->alloc_bytes != b->free_bytes)
- record(r++, b);
- }
- runtime·unlock(&proflock);
-}
-
-void
-runtime·iterate_memprof(void (*callback)(Bucket*, uintptr, uintptr*, uintptr, uintptr, uintptr))
-{
- Bucket *b;
-
- runtime·lock(&proflock);
- for(b=mbuckets; b; b=b->allnext) {
- callback(b, b->nstk, b->stk, b->size, b->allocs, b->frees);
- }
- runtime·unlock(&proflock);
-}
-
-// Must match BlockProfileRecord in debug.go.
-typedef struct BRecord BRecord;
-struct BRecord {
- int64 count;
- int64 cycles;
- uintptr stk[32];
-};
-
-func BlockProfile(p Slice) (n int, ok bool) {
- Bucket *b;
- BRecord *r;
- int32 i;
-
- runtime·lock(&proflock);
- n = 0;
- for(b=bbuckets; b; b=b->allnext)
- n++;
- ok = false;
- if(n <= p.len) {
- ok = true;
- r = (BRecord*)p.array;
- for(b=bbuckets; b; b=b->allnext, r++) {
- r->count = b->count;
- r->cycles = b->cycles;
- for(i=0; i<b->nstk && i<nelem(r->stk); i++)
- r->stk[i] = b->stk[i];
- for(; i<nelem(r->stk); i++)
- r->stk[i] = 0;
- }
- }
- runtime·unlock(&proflock);
-}
-
-// Must match StackRecord in debug.go.
-typedef struct TRecord TRecord;
-struct TRecord {
- uintptr stk[32];
-};
-
-func ThreadCreateProfile(p Slice) (n int, ok bool) {
- TRecord *r;
- M *first, *mp;
-
- first = runtime·atomicloadp(&runtime·allm);
- n = 0;
- for(mp=first; mp; mp=mp->alllink)
- n++;
- ok = false;
- if(n <= p.len) {
- ok = true;
- r = (TRecord*)p.array;
- for(mp=first; mp; mp=mp->alllink) {
- runtime·memmove(r->stk, mp->createstack, sizeof r->stk);
- r++;
- }
- }
-}
-
-func Stack(b Slice, all bool) (n int) {
- uintptr pc, sp;
-
- sp = runtime·getcallersp(&b);
- pc = (uintptr)runtime·getcallerpc(&b);
-
- if(all) {
- runtime·semacquire(&runtime·worldsema, false);
- g->m->gcing = 1;
- runtime·stoptheworld();
- }
-
- if(b.len == 0)
- n = 0;
- else{
- g->writebuf = (byte*)b.array;
- g->writenbuf = b.len;
- runtime·goroutineheader(g);
- runtime·traceback(pc, sp, 0, g);
- if(all)
- runtime·tracebackothers(g);
- n = b.len - g->writenbuf;
- g->writebuf = nil;
- g->writenbuf = 0;
- }
-
- if(all) {
- g->m->gcing = 0;
- runtime·semrelease(&runtime·worldsema);
- runtime·starttheworld();
- }
-}
-
-static void
-saveg(uintptr pc, uintptr sp, G *gp, TRecord *r)
-{
- int32 n;
-
- n = runtime·gentraceback(pc, sp, 0, gp, 0, r->stk, nelem(r->stk), nil, nil, false);
- if(n < nelem(r->stk))
- r->stk[n] = 0;
-}
-
-func GoroutineProfile(b Slice) (n int, ok bool) {
- uintptr pc, sp, i;
- TRecord *r;
- G *gp;
-
- sp = runtime·getcallersp(&b);
- pc = (uintptr)runtime·getcallerpc(&b);
-
- ok = false;
- n = runtime·gcount();
- if(n <= b.len) {
- runtime·semacquire(&runtime·worldsema, false);
- g->m->gcing = 1;
- runtime·stoptheworld();
-
- n = runtime·gcount();
- if(n <= b.len) {
- ok = true;
- r = (TRecord*)b.array;
- saveg(pc, sp, g, r++);
- for(i = 0; i < runtime·allglen; i++) {
- gp = runtime·allg[i];
- if(gp == g || gp->status == Gdead)
- continue;
- saveg(~(uintptr)0, ~(uintptr)0, gp, r++);
- }
- }
-
- g->m->gcing = 0;
- runtime·semrelease(&runtime·worldsema);
- runtime·starttheworld();
- }
-}
-
-// Tracing of alloc/free/gc.
-
-static Lock tracelock;
-
-void
-runtime·tracealloc(void *p, uintptr size, Type *type)
-{
- runtime·lock(&tracelock);
- g->m->traceback = 2;
- if(type == nil)
- runtime·printf("tracealloc(%p, %p)\n", p, size);
- else
- runtime·printf("tracealloc(%p, %p, %S)\n", p, size, *type->string);
- if(g->m->curg == nil || g == g->m->curg) {
- runtime·goroutineheader(g);
- runtime·traceback((uintptr)runtime·getcallerpc(&p), (uintptr)runtime·getcallersp(&p), 0, g);
- } else {
- runtime·goroutineheader(g->m->curg);
- runtime·traceback(~(uintptr)0, ~(uintptr)0, 0, g->m->curg);
- }
- runtime·printf("\n");
- g->m->traceback = 0;
- runtime·unlock(&tracelock);
-}
-
-void
-runtime·tracefree(void *p, uintptr size)
-{
- runtime·lock(&tracelock);
- g->m->traceback = 2;
- runtime·printf("tracefree(%p, %p)\n", p, size);
- runtime·goroutineheader(g);
- runtime·traceback((uintptr)runtime·getcallerpc(&p), (uintptr)runtime·getcallersp(&p), 0, g);
- runtime·printf("\n");
- g->m->traceback = 0;
- runtime·unlock(&tracelock);
-}
-
-void
-runtime·tracegc(void)
-{
- runtime·lock(&tracelock);
- g->m->traceback = 2;
- runtime·printf("tracegc()\n");
- // running on m->g0 stack; show all non-g0 goroutines
- runtime·tracebackothers(g);
- runtime·printf("end tracegc\n");
- runtime·printf("\n");
- g->m->traceback = 0;
- runtime·unlock(&tracelock);
-}
diff --git a/src/pkg/runtime/mprof.h b/src/pkg/runtime/mprof.h
deleted file mode 100644
index de5e707d60..0000000000
--- a/src/pkg/runtime/mprof.h
+++ /dev/null
@@ -1,56 +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.
-
-// Per-call-stack profiling information.
-// Lookup by hashing call stack into a linked-list hash table.
-struct Bucket
-{
- Bucket *next; // next in hash list
- Bucket *allnext; // next in list of all mbuckets/bbuckets
- int32 typ;
- // Generally unions can break precise GC,
- // this one is fine because it does not contain pointers.
- union
- {
- struct // typ == MProf
- {
- // The following complex 3-stage scheme of stats accumulation
- // is required to obtain a consistent picture of mallocs and frees
- // for some point in time.
- // The problem is that mallocs come in real time, while frees
- // come only after a GC during concurrent sweeping. So if we would
- // naively count them, we would get a skew toward mallocs.
- //
- // Mallocs are accounted in recent stats.
- // Explicit frees are accounted in recent stats.
- // GC frees are accounted in prev stats.
- // After GC prev stats are added to final stats and
- // recent stats are moved into prev stats.
- uintptr allocs;
- uintptr frees;
- uintptr alloc_bytes;
- uintptr free_bytes;
-
- uintptr prev_allocs; // since last but one till last gc
- uintptr prev_frees;
- uintptr prev_alloc_bytes;
- uintptr prev_free_bytes;
-
- uintptr recent_allocs; // since last gc till now
- uintptr recent_frees;
- uintptr recent_alloc_bytes;
- uintptr recent_free_bytes;
-
- };
- struct // typ == BProf
- {
- int64 count;
- int64 cycles;
- };
- };
- uintptr hash; // hash of size + stk
- uintptr size;
- uintptr nstk;
- uintptr stk[1];
-};
diff --git a/src/pkg/runtime/msize.c b/src/pkg/runtime/msize.c
index 2fbd5e1042..7cb65dad0d 100644
--- a/src/pkg/runtime/msize.c
+++ b/src/pkg/runtime/msize.c
@@ -28,7 +28,7 @@
#include "runtime.h"
#include "arch_GOARCH.h"
#include "malloc.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
#pragma dataflag NOPTR
int32 runtime·class_to_size[NumSizeClasses];
diff --git a/src/pkg/runtime/netpoll.go b/src/pkg/runtime/netpoll.go
new file mode 100644
index 0000000000..3456e02081
--- /dev/null
+++ b/src/pkg/runtime/netpoll.go
@@ -0,0 +1,455 @@
+// 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.
+
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
+
+package runtime
+
+import "unsafe"
+
+// Integrated network poller (platform-independent part).
+// A particular implementation (epoll/kqueue) must define the following functions:
+// func netpollinit() // to initialize the poller
+// func netpollopen(fd uintptr, pd *pollDesc) int32 // to arm edge-triggered notifications
+// and associate fd with pd.
+// An implementation must call the following function to denote that the pd is ready.
+// func netpollready(gpp **g, pd *pollDesc, mode int32)
+
+// pollDesc contains 2 binary semaphores, rg and wg, to park reader and writer
+// goroutines respectively. The semaphore can be in the following states:
+// pdReady - io readiness notification is pending;
+// a goroutine consumes the notification by changing the state to nil.
+// pdWait - a goroutine prepares to park on the semaphore, but not yet parked;
+// the goroutine commits to park by changing the state to G pointer,
+// or, alternatively, concurrent io notification changes the state to READY,
+// or, alternatively, concurrent timeout/close changes the state to nil.
+// G pointer - the goroutine is blocked on the semaphore;
+// io notification or timeout/close changes the state to READY or nil respectively
+// and unparks the goroutine.
+// nil - nothing of the above.
+const (
+ pdReady uintptr = 1
+ pdWait uintptr = 2
+)
+
+const pollBlockSize = 4 * 1024
+
+// Network poller descriptor.
+type pollDesc struct {
+ link *pollDesc // in pollcache, protected by pollcache.lock
+
+ // The lock protects pollOpen, pollSetDeadline, pollUnblock and deadlineimpl operations.
+ // This fully covers seq, rt and wt variables. fd is constant throughout the PollDesc lifetime.
+ // pollReset, pollWait, pollWaitCanceled and runtime·netpollready (IO readiness notification)
+ // proceed w/o taking the lock. So closing, rg, rd, wg and wd are manipulated
+ // in a lock-free way by all operations.
+ // NOTE(dvyukov): the following code uses uintptr to store *g (rg/wg),
+ // that will blow up when GC starts moving objects.
+ lock mutex // protectes the following fields
+ fd uintptr
+ closing bool
+ seq uintptr // protects from stale timers and ready notifications
+ rg uintptr // pdReady, pdWait, G waiting for read or nil
+ rt timer // read deadline timer (set if rt.f != nil)
+ rd int64 // read deadline
+ wg uintptr // pdReady, pdWait, G waiting for write or nil
+ wt timer // write deadline timer
+ wd int64 // write deadline
+ user unsafe.Pointer // user settable cookie
+}
+
+type pollCache struct {
+ lock mutex
+ first *pollDesc
+ // PollDesc objects must be type-stable,
+ // because we can get ready notification from epoll/kqueue
+ // after the descriptor is closed/reused.
+ // Stale notifications are detected using seq variable,
+ // seq is incremented when deadlines are changed or descriptor is reused.
+}
+
+var pollcache pollCache
+
+func netpollServerInit() {
+ onM(netpollinit)
+}
+
+func netpollOpen(fd uintptr) (*pollDesc, int) {
+ pd := pollcache.alloc()
+ lock(&pd.lock)
+ if pd.wg != 0 && pd.wg != pdReady {
+ gothrow("netpollOpen: blocked write on free descriptor")
+ }
+ if pd.rg != 0 && pd.rg != pdReady {
+ gothrow("netpollOpen: blocked read on free descriptor")
+ }
+ pd.fd = fd
+ pd.closing = false
+ pd.seq++
+ pd.rg = 0
+ pd.rd = 0
+ pd.wg = 0
+ pd.wd = 0
+ unlock(&pd.lock)
+
+ var errno int32
+ onM(func() {
+ errno = netpollopen(fd, pd)
+ })
+ return pd, int(errno)
+}
+
+func netpollClose(pd *pollDesc) {
+ if !pd.closing {
+ gothrow("netpollClose: close w/o unblock")
+ }
+ if pd.wg != 0 && pd.wg != pdReady {
+ gothrow("netpollClose: blocked write on closing descriptor")
+ }
+ if pd.rg != 0 && pd.rg != pdReady {
+ gothrow("netpollClose: blocked read on closing descriptor")
+ }
+ onM(func() {
+ netpollclose(uintptr(pd.fd))
+ })
+ pollcache.free(pd)
+}
+
+func (c *pollCache) free(pd *pollDesc) {
+ lock(&c.lock)
+ pd.link = c.first
+ c.first = pd
+ unlock(&c.lock)
+}
+
+func netpollReset(pd *pollDesc, mode int) int {
+ err := netpollcheckerr(pd, int32(mode))
+ if err != 0 {
+ return err
+ }
+ if mode == 'r' {
+ pd.rg = 0
+ } else if mode == 'w' {
+ pd.wg = 0
+ }
+ return 0
+}
+
+func netpollWait(pd *pollDesc, mode int) int {
+ err := netpollcheckerr(pd, int32(mode))
+ if err != 0 {
+ return err
+ }
+ // As for now only Solaris uses level-triggered IO.
+ if GOOS == "solaris" {
+ onM(func() {
+ netpollarm(pd, mode)
+ })
+ }
+ for !netpollblock(pd, int32(mode), false) {
+ err = netpollcheckerr(pd, int32(mode))
+ if err != 0 {
+ return err
+ }
+ // Can happen if timeout has fired and unblocked us,
+ // but before we had a chance to run, timeout has been reset.
+ // Pretend it has not happened and retry.
+ }
+ return 0
+}
+
+func netpollWaitCanceled(pd *pollDesc, mode int) {
+ // This function is used only on windows after a failed attempt to cancel
+ // a pending async IO operation. Wait for ioready, ignore closing or timeouts.
+ for !netpollblock(pd, int32(mode), true) {
+ }
+}
+
+func netpollSetDeadline(pd *pollDesc, d int64, mode int) {
+ lock(&pd.lock)
+ if pd.closing {
+ unlock(&pd.lock)
+ return
+ }
+ pd.seq++ // invalidate current timers
+ // Reset current timers.
+ if pd.rt.f != nil {
+ deltimer(&pd.rt)
+ pd.rt.f = nil
+ }
+ if pd.wt.f != nil {
+ deltimer(&pd.wt)
+ pd.wt.f = nil
+ }
+ // Setup new timers.
+ if d != 0 && d <= nanotime() {
+ d = -1
+ }
+ if mode == 'r' || mode == 'r'+'w' {
+ pd.rd = d
+ }
+ if mode == 'w' || mode == 'r'+'w' {
+ pd.wd = d
+ }
+ if pd.rd > 0 && pd.rd == pd.wd {
+ pd.rt.f = netpollDeadline
+ pd.rt.when = pd.rd
+ // Copy current seq into the timer arg.
+ // Timer func will check the seq against current descriptor seq,
+ // if they differ the descriptor was reused or timers were reset.
+ pd.rt.arg = pd
+ pd.rt.seq = pd.seq
+ addtimer(&pd.rt)
+ } else {
+ if pd.rd > 0 {
+ pd.rt.f = netpollReadDeadline
+ pd.rt.when = pd.rd
+ pd.rt.arg = pd
+ pd.rt.seq = pd.seq
+ addtimer(&pd.rt)
+ }
+ if pd.wd > 0 {
+ pd.wt.f = netpollWriteDeadline
+ pd.wt.when = pd.wd
+ pd.wt.arg = pd
+ pd.wt.seq = pd.seq
+ addtimer(&pd.wt)
+ }
+ }
+ // If we set the new deadline in the past, unblock currently pending IO if any.
+ var rg, wg *g
+ atomicstorep(unsafe.Pointer(&wg), nil) // full memory barrier between stores to rd/wd and load of rg/wg in netpollunblock
+ if pd.rd < 0 {
+ rg = netpollunblock(pd, 'r', false)
+ }
+ if pd.wd < 0 {
+ wg = netpollunblock(pd, 'w', false)
+ }
+ unlock(&pd.lock)
+ if rg != nil {
+ goready(rg)
+ }
+ if wg != nil {
+ goready(wg)
+ }
+}
+
+func netpollUnblock(pd *pollDesc) {
+ lock(&pd.lock)
+ if pd.closing {
+ gothrow("netpollUnblock: already closing")
+ }
+ pd.closing = true
+ pd.seq++
+ var rg, wg *g
+ atomicstorep(unsafe.Pointer(&rg), nil) // full memory barrier between store to closing and read of rg/wg in netpollunblock
+ rg = netpollunblock(pd, 'r', false)
+ wg = netpollunblock(pd, 'w', false)
+ if pd.rt.f != nil {
+ deltimer(&pd.rt)
+ pd.rt.f = nil
+ }
+ if pd.wt.f != nil {
+ deltimer(&pd.wt)
+ pd.wt.f = nil
+ }
+ unlock(&pd.lock)
+ if rg != nil {
+ goready(rg)
+ }
+ if wg != nil {
+ goready(wg)
+ }
+}
+
+func netpollfd(pd *pollDesc) uintptr {
+ return pd.fd
+}
+
+func netpolluser(pd *pollDesc) *unsafe.Pointer {
+ return &pd.user
+}
+
+func netpollclosing(pd *pollDesc) bool {
+ return pd.closing
+}
+
+func netpolllock(pd *pollDesc) {
+ lock(&pd.lock)
+}
+
+func netpollunlock(pd *pollDesc) {
+ unlock(&pd.lock)
+}
+
+// make pd ready, newly runnable goroutines (if any) are returned in rg/wg
+func netpollready(gpp **g, pd *pollDesc, mode int32) {
+ var rg, wg *g
+ if mode == 'r' || mode == 'r'+'w' {
+ rg = netpollunblock(pd, 'r', true)
+ }
+ if mode == 'w' || mode == 'r'+'w' {
+ wg = netpollunblock(pd, 'w', true)
+ }
+ if rg != nil {
+ rg.schedlink = *gpp
+ *gpp = rg
+ }
+ if wg != nil {
+ wg.schedlink = *gpp
+ *gpp = wg
+ }
+}
+
+func netpollcheckerr(pd *pollDesc, mode int32) int {
+ if pd.closing {
+ return 1 // errClosing
+ }
+ if (mode == 'r' && pd.rd < 0) || (mode == 'w' && pd.wd < 0) {
+ return 2 // errTimeout
+ }
+ return 0
+}
+
+func netpollblockcommit(gp *g, gpp unsafe.Pointer) bool {
+ return casuintptr((*uintptr)(gpp), pdWait, uintptr(unsafe.Pointer(gp)))
+}
+
+// returns true if IO is ready, or false if timedout or closed
+// waitio - wait only for completed IO, ignore errors
+func netpollblock(pd *pollDesc, mode int32, waitio bool) bool {
+ gpp := &pd.rg
+ if mode == 'w' {
+ gpp = &pd.wg
+ }
+
+ // set the gpp semaphore to WAIT
+ for {
+ old := *gpp
+ if old == pdReady {
+ *gpp = 0
+ return true
+ }
+ if old != 0 {
+ gothrow("netpollblock: double wait")
+ }
+ if casuintptr(gpp, 0, pdWait) {
+ break
+ }
+ }
+
+ // need to recheck error states after setting gpp to WAIT
+ // this is necessary because runtime_pollUnblock/runtime_pollSetDeadline/deadlineimpl
+ // do the opposite: store to closing/rd/wd, membarrier, load of rg/wg
+ if waitio || netpollcheckerr(pd, mode) == 0 {
+ f := netpollblockcommit
+ gopark(**(**unsafe.Pointer)(unsafe.Pointer(&f)), unsafe.Pointer(gpp), "IO wait")
+ }
+ // be careful to not lose concurrent READY notification
+ old := xchguintptr(gpp, 0)
+ if old > pdWait {
+ gothrow("netpollblock: corrupted state")
+ }
+ return old == pdReady
+}
+
+func netpollunblock(pd *pollDesc, mode int32, ioready bool) *g {
+ gpp := &pd.rg
+ if mode == 'w' {
+ gpp = &pd.wg
+ }
+
+ for {
+ old := *gpp
+ if old == pdReady {
+ return nil
+ }
+ if old == 0 && !ioready {
+ // Only set READY for ioready. runtime_pollWait
+ // will check for timeout/cancel before waiting.
+ return nil
+ }
+ var new uintptr
+ if ioready {
+ new = pdReady
+ }
+ if casuintptr(gpp, old, new) {
+ if old == pdReady || old == pdWait {
+ old = 0
+ }
+ return (*g)(unsafe.Pointer(old))
+ }
+ }
+}
+
+func netpolldeadlineimpl(pd *pollDesc, seq uintptr, read, write bool) {
+ lock(&pd.lock)
+ // Seq arg is seq when the timer was set.
+ // If it's stale, ignore the timer event.
+ if seq != pd.seq {
+ // The descriptor was reused or timers were reset.
+ unlock(&pd.lock)
+ return
+ }
+ var rg *g
+ if read {
+ if pd.rd <= 0 || pd.rt.f == nil {
+ gothrow("netpolldeadlineimpl: inconsistent read deadline")
+ }
+ pd.rd = -1
+ atomicstorep(unsafe.Pointer(&pd.rt.f), nil) // full memory barrier between store to rd and load of rg in netpollunblock
+ rg = netpollunblock(pd, 'r', false)
+ }
+ var wg *g
+ if write {
+ if pd.wd <= 0 || pd.wt.f == nil && !read {
+ gothrow("netpolldeadlineimpl: inconsistent write deadline")
+ }
+ pd.wd = -1
+ atomicstorep(unsafe.Pointer(&pd.wt.f), nil) // full memory barrier between store to wd and load of wg in netpollunblock
+ wg = netpollunblock(pd, 'w', false)
+ }
+ unlock(&pd.lock)
+ if rg != nil {
+ goready(rg)
+ }
+ if wg != nil {
+ goready(wg)
+ }
+}
+
+func netpollDeadline(arg interface{}, seq uintptr) {
+ netpolldeadlineimpl(arg.(*pollDesc), seq, true, true)
+}
+
+func netpollReadDeadline(arg interface{}, seq uintptr) {
+ netpolldeadlineimpl(arg.(*pollDesc), seq, true, false)
+}
+
+func netpollWriteDeadline(arg interface{}, seq uintptr) {
+ netpolldeadlineimpl(arg.(*pollDesc), seq, false, true)
+}
+
+func (c *pollCache) alloc() *pollDesc {
+ lock(&c.lock)
+ if c.first == nil {
+ const pdSize = unsafe.Sizeof(pollDesc{})
+ n := pollBlockSize / pdSize
+ if n == 0 {
+ n = 1
+ }
+ // Must be in non-GC memory because can be referenced
+ // only from epoll/kqueue internals.
+ mem := persistentalloc(n*pdSize, 0, &memstats.other_sys)
+ for i := uintptr(0); i < n; i++ {
+ pd := (*pollDesc)(add(mem, i*pdSize))
+ pd.link = c.first
+ c.first = pd
+ }
+ }
+ pd := c.first
+ c.first = pd.link
+ unlock(&c.lock)
+ return pd
+}
diff --git a/src/pkg/runtime/netpoll.goc b/src/pkg/runtime/netpoll.goc
deleted file mode 100644
index 7b3d16d02d..0000000000
--- a/src/pkg/runtime/netpoll.goc
+++ /dev/null
@@ -1,467 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
-
-package net
-
-#include "runtime.h"
-#include "defs_GOOS_GOARCH.h"
-#include "arch_GOARCH.h"
-#include "malloc.h"
-
-// Integrated network poller (platform-independent part).
-// A particular implementation (epoll/kqueue) must define the following functions:
-// void runtime·netpollinit(void); // to initialize the poller
-// int32 runtime·netpollopen(uintptr fd, PollDesc *pd); // to arm edge-triggered notifications
- // and associate fd with pd.
-// An implementation must call the following function to denote that the pd is ready.
-// void runtime·netpollready(G **gpp, PollDesc *pd, int32 mode);
-
-// PollDesc contains 2 binary semaphores, rg and wg, to park reader and writer
-// goroutines respectively. The semaphore can be in the following states:
-// READY - io readiness notification is pending;
-// a goroutine consumes the notification by changing the state to nil.
-// WAIT - a goroutine prepares to park on the semaphore, but not yet parked;
-// the goroutine commits to park by changing the state to G pointer,
-// or, alternatively, concurrent io notification changes the state to READY,
-// or, alternatively, concurrent timeout/close changes the state to nil.
-// G pointer - the goroutine is blocked on the semaphore;
-// io notification or timeout/close changes the state to READY or nil respectively
-// and unparks the goroutine.
-// nil - nothing of the above.
-#define READY ((G*)1)
-#define WAIT ((G*)2)
-
-enum
-{
- PollBlockSize = 4*1024,
-};
-
-struct PollDesc
-{
- PollDesc* link; // in pollcache, protected by pollcache.Lock
-
- // The lock protects pollOpen, pollSetDeadline, pollUnblock and deadlineimpl operations.
- // This fully covers seq, rt and wt variables. fd is constant throughout the PollDesc lifetime.
- // pollReset, pollWait, pollWaitCanceled and runtime·netpollready (IO rediness notification)
- // proceed w/o taking the lock. So closing, rg, rd, wg and wd are manipulated
- // in a lock-free way by all operations.
- Lock; // protectes the following fields
- uintptr fd;
- bool closing;
- uintptr seq; // protects from stale timers and ready notifications
- G* rg; // READY, WAIT, G waiting for read or nil
- Timer rt; // read deadline timer (set if rt.fv != nil)
- int64 rd; // read deadline
- G* wg; // READY, WAIT, G waiting for write or nil
- Timer wt; // write deadline timer
- int64 wd; // write deadline
- void* user; // user settable cookie
-};
-
-static struct
-{
- Lock;
- PollDesc* first;
- // PollDesc objects must be type-stable,
- // because we can get ready notification from epoll/kqueue
- // after the descriptor is closed/reused.
- // Stale notifications are detected using seq variable,
- // seq is incremented when deadlines are changed or descriptor is reused.
-} pollcache;
-
-static bool netpollblock(PollDesc*, int32, bool);
-static G* netpollunblock(PollDesc*, int32, bool);
-static void deadline(int64, Eface);
-static void readDeadline(int64, Eface);
-static void writeDeadline(int64, Eface);
-static PollDesc* allocPollDesc(void);
-static intgo checkerr(PollDesc *pd, int32 mode);
-
-static FuncVal deadlineFn = {(void(*)(void))deadline};
-static FuncVal readDeadlineFn = {(void(*)(void))readDeadline};
-static FuncVal writeDeadlineFn = {(void(*)(void))writeDeadline};
-
-// runtimeNano returns the current value of the runtime clock in nanoseconds.
-func runtimeNano() (ns int64) {
- ns = runtime·nanotime();
-}
-
-func runtime_pollServerInit() {
- runtime·netpollinit();
-}
-
-func runtime_pollOpen(fd uintptr) (pd *PollDesc, errno int) {
- pd = allocPollDesc();
- runtime·lock(pd);
- if(pd->wg != nil && pd->wg != READY)
- runtime·throw("runtime_pollOpen: blocked write on free descriptor");
- if(pd->rg != nil && pd->rg != READY)
- runtime·throw("runtime_pollOpen: blocked read on free descriptor");
- pd->fd = fd;
- pd->closing = false;
- pd->seq++;
- pd->rg = nil;
- pd->rd = 0;
- pd->wg = nil;
- pd->wd = 0;
- runtime·unlock(pd);
-
- errno = runtime·netpollopen(fd, pd);
-}
-
-func runtime_pollClose(pd *PollDesc) {
- if(!pd->closing)
- runtime·throw("runtime_pollClose: close w/o unblock");
- if(pd->wg != nil && pd->wg != READY)
- runtime·throw("runtime_pollClose: blocked write on closing descriptor");
- if(pd->rg != nil && pd->rg != READY)
- runtime·throw("runtime_pollClose: blocked read on closing descriptor");
- runtime·netpollclose(pd->fd);
- runtime·lock(&pollcache);
- pd->link = pollcache.first;
- pollcache.first = pd;
- runtime·unlock(&pollcache);
-}
-
-func runtime_pollReset(pd *PollDesc, mode int) (err int) {
- err = checkerr(pd, mode);
- if(err)
- goto ret;
- if(mode == 'r')
- pd->rg = nil;
- else if(mode == 'w')
- pd->wg = nil;
-ret:
-}
-
-func runtime_pollWait(pd *PollDesc, mode int) (err int) {
- err = checkerr(pd, mode);
- if(err == 0) {
- // As for now only Solaris uses level-triggered IO.
- if(Solaris)
- runtime·netpollarm(pd, mode);
- while(!netpollblock(pd, mode, false)) {
- err = checkerr(pd, mode);
- if(err != 0)
- break;
- // Can happen if timeout has fired and unblocked us,
- // but before we had a chance to run, timeout has been reset.
- // Pretend it has not happened and retry.
- }
- }
-}
-
-func runtime_pollWaitCanceled(pd *PollDesc, mode int) {
- // This function is used only on windows after a failed attempt to cancel
- // a pending async IO operation. Wait for ioready, ignore closing or timeouts.
- while(!netpollblock(pd, mode, true))
- ;
-}
-
-func runtime_pollSetDeadline(pd *PollDesc, d int64, mode int) {
- G *rg, *wg;
-
- runtime·lock(pd);
- if(pd->closing) {
- runtime·unlock(pd);
- return;
- }
- pd->seq++; // invalidate current timers
- // Reset current timers.
- if(pd->rt.fv) {
- runtime·deltimer(&pd->rt);
- pd->rt.fv = nil;
- }
- if(pd->wt.fv) {
- runtime·deltimer(&pd->wt);
- pd->wt.fv = nil;
- }
- // Setup new timers.
- if(d != 0 && d <= runtime·nanotime())
- d = -1;
- if(mode == 'r' || mode == 'r'+'w')
- pd->rd = d;
- if(mode == 'w' || mode == 'r'+'w')
- pd->wd = d;
- if(pd->rd > 0 && pd->rd == pd->wd) {
- pd->rt.fv = &deadlineFn;
- pd->rt.when = pd->rd;
- // Copy current seq into the timer arg.
- // Timer func will check the seq against current descriptor seq,
- // if they differ the descriptor was reused or timers were reset.
- pd->rt.arg.type = (Type*)pd->seq;
- pd->rt.arg.data = pd;
- runtime·addtimer(&pd->rt);
- } else {
- if(pd->rd > 0) {
- pd->rt.fv = &readDeadlineFn;
- pd->rt.when = pd->rd;
- pd->rt.arg.type = (Type*)pd->seq;
- pd->rt.arg.data = pd;
- runtime·addtimer(&pd->rt);
- }
- if(pd->wd > 0) {
- pd->wt.fv = &writeDeadlineFn;
- pd->wt.when = pd->wd;
- pd->wt.arg.type = (Type*)pd->seq;
- pd->wt.arg.data = pd;
- runtime·addtimer(&pd->wt);
- }
- }
- // If we set the new deadline in the past, unblock currently pending IO if any.
- rg = nil;
- runtime·atomicstorep(&wg, nil); // full memory barrier between stores to rd/wd and load of rg/wg in netpollunblock
- if(pd->rd < 0)
- rg = netpollunblock(pd, 'r', false);
- if(pd->wd < 0)
- wg = netpollunblock(pd, 'w', false);
- runtime·unlock(pd);
- if(rg)
- runtime·ready(rg);
- if(wg)
- runtime·ready(wg);
-}
-
-func runtime_pollUnblock(pd *PollDesc) {
- G *rg, *wg;
-
- runtime·lock(pd);
- if(pd->closing)
- runtime·throw("runtime_pollUnblock: already closing");
- pd->closing = true;
- pd->seq++;
- runtime·atomicstorep(&rg, nil); // full memory barrier between store to closing and read of rg/wg in netpollunblock
- rg = netpollunblock(pd, 'r', false);
- wg = netpollunblock(pd, 'w', false);
- if(pd->rt.fv) {
- runtime·deltimer(&pd->rt);
- pd->rt.fv = nil;
- }
- if(pd->wt.fv) {
- runtime·deltimer(&pd->wt);
- pd->wt.fv = nil;
- }
- runtime·unlock(pd);
- if(rg)
- runtime·ready(rg);
- if(wg)
- runtime·ready(wg);
-}
-
-uintptr
-runtime·netpollfd(PollDesc *pd)
-{
- return pd->fd;
-}
-
-void**
-runtime·netpolluser(PollDesc *pd)
-{
- return &pd->user;
-}
-
-bool
-runtime·netpollclosing(PollDesc *pd)
-{
- return pd->closing;
-}
-
-void
-runtime·netpolllock(PollDesc *pd)
-{
- runtime·lock(pd);
-}
-
-void
-runtime·netpollunlock(PollDesc *pd)
-{
- runtime·unlock(pd);
-}
-
-// make pd ready, newly runnable goroutines (if any) are enqueued info gpp list
-void
-runtime·netpollready(G **gpp, PollDesc *pd, int32 mode)
-{
- G *rg, *wg;
-
- rg = wg = nil;
- if(mode == 'r' || mode == 'r'+'w')
- rg = netpollunblock(pd, 'r', true);
- if(mode == 'w' || mode == 'r'+'w')
- wg = netpollunblock(pd, 'w', true);
- if(rg) {
- rg->schedlink = *gpp;
- *gpp = rg;
- }
- if(wg) {
- wg->schedlink = *gpp;
- *gpp = wg;
- }
-}
-
-static intgo
-checkerr(PollDesc *pd, int32 mode)
-{
- if(pd->closing)
- return 1; // errClosing
- if((mode == 'r' && pd->rd < 0) || (mode == 'w' && pd->wd < 0))
- return 2; // errTimeout
- return 0;
-}
-
-static bool
-blockcommit(G *gp, G **gpp)
-{
- return runtime·casp(gpp, WAIT, gp);
-}
-
-// returns true if IO is ready, or false if timedout or closed
-// waitio - wait only for completed IO, ignore errors
-static bool
-netpollblock(PollDesc *pd, int32 mode, bool waitio)
-{
- G **gpp, *old;
-
- gpp = &pd->rg;
- if(mode == 'w')
- gpp = &pd->wg;
-
- // set the gpp semaphore to WAIT
- for(;;) {
- old = *gpp;
- if(old == READY) {
- *gpp = nil;
- return true;
- }
- if(old != nil)
- runtime·throw("netpollblock: double wait");
- if(runtime·casp(gpp, nil, WAIT))
- break;
- }
-
- // need to recheck error states after setting gpp to WAIT
- // this is necessary because runtime_pollUnblock/runtime_pollSetDeadline/deadlineimpl
- // do the opposite: store to closing/rd/wd, membarrier, load of rg/wg
- if(waitio || checkerr(pd, mode) == 0)
- runtime·park((bool(*)(G*, void*))blockcommit, gpp, "IO wait");
- // be careful to not lose concurrent READY notification
- old = runtime·xchgp(gpp, nil);
- if(old > WAIT)
- runtime·throw("netpollblock: corrupted state");
- return old == READY;
-}
-
-static G*
-netpollunblock(PollDesc *pd, int32 mode, bool ioready)
-{
- G **gpp, *old, *new;
-
- gpp = &pd->rg;
- if(mode == 'w')
- gpp = &pd->wg;
-
- for(;;) {
- old = *gpp;
- if(old == READY)
- return nil;
- if(old == nil && !ioready) {
- // Only set READY for ioready. runtime_pollWait
- // will check for timeout/cancel before waiting.
- return nil;
- }
- new = nil;
- if(ioready)
- new = READY;
- if(runtime·casp(gpp, old, new))
- break;
- }
- if(old > WAIT)
- return old; // must be G*
- return nil;
-}
-
-static void
-deadlineimpl(int64 now, Eface arg, bool read, bool write)
-{
- PollDesc *pd;
- uint32 seq;
- G *rg, *wg;
-
- USED(now);
- pd = (PollDesc*)arg.data;
- // This is the seq when the timer was set.
- // If it's stale, ignore the timer event.
- seq = (uintptr)arg.type;
- rg = wg = nil;
- runtime·lock(pd);
- if(seq != pd->seq) {
- // The descriptor was reused or timers were reset.
- runtime·unlock(pd);
- return;
- }
- if(read) {
- if(pd->rd <= 0 || pd->rt.fv == nil)
- runtime·throw("deadlineimpl: inconsistent read deadline");
- pd->rd = -1;
- runtime·atomicstorep(&pd->rt.fv, nil); // full memory barrier between store to rd and load of rg in netpollunblock
- rg = netpollunblock(pd, 'r', false);
- }
- if(write) {
- if(pd->wd <= 0 || (pd->wt.fv == nil && !read))
- runtime·throw("deadlineimpl: inconsistent write deadline");
- pd->wd = -1;
- runtime·atomicstorep(&pd->wt.fv, nil); // full memory barrier between store to wd and load of wg in netpollunblock
- wg = netpollunblock(pd, 'w', false);
- }
- runtime·unlock(pd);
- if(rg)
- runtime·ready(rg);
- if(wg)
- runtime·ready(wg);
-}
-
-static void
-deadline(int64 now, Eface arg)
-{
- deadlineimpl(now, arg, true, true);
-}
-
-static void
-readDeadline(int64 now, Eface arg)
-{
- deadlineimpl(now, arg, true, false);
-}
-
-static void
-writeDeadline(int64 now, Eface arg)
-{
- deadlineimpl(now, arg, false, true);
-}
-
-static PollDesc*
-allocPollDesc(void)
-{
- PollDesc *pd;
- uint32 i, n;
-
- runtime·lock(&pollcache);
- if(pollcache.first == nil) {
- n = PollBlockSize/sizeof(*pd);
- if(n == 0)
- n = 1;
- // Must be in non-GC memory because can be referenced
- // only from epoll/kqueue internals.
- pd = runtime·persistentalloc(n*sizeof(*pd), 0, &mstats.other_sys);
- for(i = 0; i < n; i++) {
- pd[i].link = pollcache.first;
- pollcache.first = &pd[i];
- }
- }
- pd = pollcache.first;
- pollcache.first = pd->link;
- runtime·unlock(&pollcache);
- return pd;
-}
diff --git a/src/pkg/runtime/netpoll_epoll.c b/src/pkg/runtime/netpoll_epoll.c
deleted file mode 100644
index a0ae7df310..0000000000
--- a/src/pkg/runtime/netpoll_epoll.c
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build linux
-
-#include "runtime.h"
-#include "defs_GOOS_GOARCH.h"
-
-int32 runtime·epollcreate(int32 size);
-int32 runtime·epollcreate1(int32 flags);
-int32 runtime·epollctl(int32 epfd, int32 op, int32 fd, EpollEvent *ev);
-int32 runtime·epollwait(int32 epfd, EpollEvent *ev, int32 nev, int32 timeout);
-void runtime·closeonexec(int32 fd);
-
-static int32 epfd = -1; // epoll descriptor
-
-void
-runtime·netpollinit(void)
-{
- epfd = runtime·epollcreate1(EPOLL_CLOEXEC);
- if(epfd >= 0)
- return;
- epfd = runtime·epollcreate(1024);
- if(epfd >= 0) {
- runtime·closeonexec(epfd);
- return;
- }
- runtime·printf("netpollinit: failed to create descriptor (%d)\n", -epfd);
- runtime·throw("netpollinit: failed to create descriptor");
-}
-
-int32
-runtime·netpollopen(uintptr fd, PollDesc *pd)
-{
- EpollEvent ev;
- int32 res;
-
- ev.events = EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET;
- ev.data = (uint64)pd;
- res = runtime·epollctl(epfd, EPOLL_CTL_ADD, (int32)fd, &ev);
- return -res;
-}
-
-int32
-runtime·netpollclose(uintptr fd)
-{
- EpollEvent ev;
- int32 res;
-
- res = runtime·epollctl(epfd, EPOLL_CTL_DEL, (int32)fd, &ev);
- return -res;
-}
-
-void
-runtime·netpollarm(PollDesc* pd, int32 mode)
-{
- USED(pd);
- USED(mode);
- runtime·throw("unused");
-}
-
-// polls for ready network connections
-// returns list of goroutines that become runnable
-G*
-runtime·netpoll(bool block)
-{
- static int32 lasterr;
- EpollEvent events[128], *ev;
- int32 n, i, waitms, mode;
- G *gp;
-
- if(epfd == -1)
- return nil;
- waitms = -1;
- if(!block)
- waitms = 0;
-retry:
- n = runtime·epollwait(epfd, events, nelem(events), waitms);
- if(n < 0) {
- if(n != -EINTR && n != lasterr) {
- lasterr = n;
- runtime·printf("runtime: epollwait on fd %d failed with %d\n", epfd, -n);
- }
- goto retry;
- }
- gp = nil;
- for(i = 0; i < n; i++) {
- ev = &events[i];
- if(ev->events == 0)
- continue;
- mode = 0;
- if(ev->events & (EPOLLIN|EPOLLRDHUP|EPOLLHUP|EPOLLERR))
- mode += 'r';
- if(ev->events & (EPOLLOUT|EPOLLHUP|EPOLLERR))
- mode += 'w';
- if(mode)
- runtime·netpollready(&gp, (void*)ev->data, mode);
- }
- if(block && gp == nil)
- goto retry;
- return gp;
-}
diff --git a/src/pkg/runtime/netpoll_epoll.go b/src/pkg/runtime/netpoll_epoll.go
new file mode 100644
index 0000000000..ecfc9cdde8
--- /dev/null
+++ b/src/pkg/runtime/netpoll_epoll.go
@@ -0,0 +1,97 @@
+// 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.
+
+// +build linux
+
+package runtime
+
+import "unsafe"
+
+func epollcreate(size int32) int32
+func epollcreate1(flags int32) int32
+
+//go:noescape
+func epollctl(epfd, op, fd int32, ev *epollevent) int32
+
+//go:noescape
+func epollwait(epfd int32, ev *epollevent, nev, timeout int32) int32
+func closeonexec(fd int32)
+
+var (
+ epfd int32 = -1 // epoll descriptor
+ netpolllasterr int32
+)
+
+func netpollinit() {
+ epfd = epollcreate1(_EPOLL_CLOEXEC)
+ if epfd >= 0 {
+ return
+ }
+ epfd = epollcreate(1024)
+ if epfd >= 0 {
+ closeonexec(epfd)
+ return
+ }
+ println("netpollinit: failed to create epoll descriptor", -epfd)
+ gothrow("netpollinit: failed to create descriptor")
+}
+
+func netpollopen(fd uintptr, pd *pollDesc) int32 {
+ var ev epollevent
+ ev.events = _EPOLLIN | _EPOLLOUT | _EPOLLRDHUP | _EPOLLET
+ *(**pollDesc)(unsafe.Pointer(&ev.data)) = pd
+ return -epollctl(epfd, _EPOLL_CTL_ADD, int32(fd), &ev)
+}
+
+func netpollclose(fd uintptr) int32 {
+ var ev epollevent
+ return -epollctl(epfd, _EPOLL_CTL_DEL, int32(fd), &ev)
+}
+
+func netpollarm(pd *pollDesc, mode int) {
+ gothrow("unused")
+}
+
+// polls for ready network connections
+// returns list of goroutines that become runnable
+func netpoll(block bool) (gp *g) {
+ if epfd == -1 {
+ return
+ }
+ waitms := int32(-1)
+ if !block {
+ waitms = 0
+ }
+ var events [128]epollevent
+retry:
+ n := epollwait(epfd, &events[0], int32(len(events)), waitms)
+ if n < 0 {
+ if n != -_EINTR && n != netpolllasterr {
+ netpolllasterr = n
+ println("runtime: epollwait on fd", epfd, "failed with", -n)
+ }
+ goto retry
+ }
+ for i := int32(0); i < n; i++ {
+ ev := &events[i]
+ if ev.events == 0 {
+ continue
+ }
+ var mode int32
+ if ev.events&(_EPOLLIN|_EPOLLRDHUP|_EPOLLHUP|_EPOLLERR) != 0 {
+ mode += 'r'
+ }
+ if ev.events&(_EPOLLOUT|_EPOLLHUP|_EPOLLERR) != 0 {
+ mode += 'w'
+ }
+ if mode != 0 {
+ pd := *(**pollDesc)(unsafe.Pointer(&ev.data))
+ netpollready((**g)(noescape(unsafe.Pointer(&gp))), pd, mode)
+ }
+ }
+ if block && gp == nil {
+ goto retry
+ }
+ return gp
+}
diff --git a/src/pkg/runtime/netpoll_kqueue.c b/src/pkg/runtime/netpoll_kqueue.c
deleted file mode 100644
index 171346cce2..0000000000
--- a/src/pkg/runtime/netpoll_kqueue.c
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build darwin dragonfly freebsd netbsd openbsd
-
-#include "runtime.h"
-#include "defs_GOOS_GOARCH.h"
-#include "os_GOOS.h"
-
-// Integrated network poller (kqueue-based implementation).
-
-int32 runtime·kqueue(void);
-int32 runtime·kevent(int32, Kevent*, int32, Kevent*, int32, Timespec*);
-void runtime·closeonexec(int32);
-
-static int32 kq = -1;
-
-void
-runtime·netpollinit(void)
-{
- kq = runtime·kqueue();
- if(kq < 0) {
- runtime·printf("netpollinit: kqueue failed with %d\n", -kq);
- runtime·throw("netpollinit: kqueue failed");
- }
- runtime·closeonexec(kq);
-}
-
-int32
-runtime·netpollopen(uintptr fd, PollDesc *pd)
-{
- Kevent ev[2];
- int32 n;
-
- // Arm both EVFILT_READ and EVFILT_WRITE in edge-triggered mode (EV_CLEAR)
- // for the whole fd lifetime. The notifications are automatically unregistered
- // when fd is closed.
- ev[0].ident = (uint32)fd;
- ev[0].filter = EVFILT_READ;
- ev[0].flags = EV_ADD|EV_CLEAR;
- ev[0].fflags = 0;
- ev[0].data = 0;
- ev[0].udata = (kevent_udata)pd;
- ev[1] = ev[0];
- ev[1].filter = EVFILT_WRITE;
- n = runtime·kevent(kq, ev, 2, nil, 0, nil);
- if(n < 0)
- return -n;
- return 0;
-}
-
-int32
-runtime·netpollclose(uintptr fd)
-{
- // Don't need to unregister because calling close()
- // on fd will remove any kevents that reference the descriptor.
- USED(fd);
- return 0;
-}
-
-void
-runtime·netpollarm(PollDesc* pd, int32 mode)
-{
- USED(pd, mode);
- runtime·throw("unused");
-}
-
-// Polls for ready network connections.
-// Returns list of goroutines that become runnable.
-G*
-runtime·netpoll(bool block)
-{
- static int32 lasterr;
- Kevent events[64], *ev;
- Timespec ts, *tp;
- int32 n, i, mode;
- G *gp;
-
- if(kq == -1)
- return nil;
- tp = nil;
- if(!block) {
- ts.tv_sec = 0;
- ts.tv_nsec = 0;
- tp = &ts;
- }
- gp = nil;
-retry:
- n = runtime·kevent(kq, nil, 0, events, nelem(events), tp);
- if(n < 0) {
- if(n != -EINTR && n != lasterr) {
- lasterr = n;
- runtime·printf("runtime: kevent on fd %d failed with %d\n", kq, -n);
- }
- goto retry;
- }
- for(i = 0; i < n; i++) {
- ev = &events[i];
- mode = 0;
- if(ev->filter == EVFILT_READ)
- mode += 'r';
- if(ev->filter == EVFILT_WRITE)
- mode += 'w';
- if(mode)
- runtime·netpollready(&gp, (PollDesc*)ev->udata, mode);
- }
- if(block && gp == nil)
- goto retry;
- return gp;
-}
diff --git a/src/pkg/runtime/netpoll_kqueue.go b/src/pkg/runtime/netpoll_kqueue.go
new file mode 100644
index 0000000000..d6d55b97b8
--- /dev/null
+++ b/src/pkg/runtime/netpoll_kqueue.go
@@ -0,0 +1,101 @@
+// 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.
+
+// +build darwin dragonfly freebsd netbsd openbsd
+
+package runtime
+
+// Integrated network poller (kqueue-based implementation).
+
+import "unsafe"
+
+func kqueue() int32
+
+//go:noescape
+func kevent(kq int32, ch *keventt, nch int32, ev *keventt, nev int32, ts *timespec) int32
+func closeonexec(fd int32)
+
+var (
+ kq int32 = -1
+ netpolllasterr int32
+)
+
+func netpollinit() {
+ kq = kqueue()
+ if kq < 0 {
+ println("netpollinit: kqueue failed with", -kq)
+ gothrow("netpollinit: kqueue failed")
+ }
+ closeonexec(kq)
+}
+
+func netpollopen(fd uintptr, pd *pollDesc) int32 {
+ // Arm both EVFILT_READ and EVFILT_WRITE in edge-triggered mode (EV_CLEAR)
+ // for the whole fd lifetime. The notifications are automatically unregistered
+ // when fd is closed.
+ var ev [2]keventt
+ *(*uintptr)(unsafe.Pointer(&ev[0].ident)) = fd
+ ev[0].filter = _EVFILT_READ
+ ev[0].flags = _EV_ADD | _EV_CLEAR
+ ev[0].fflags = 0
+ ev[0].data = 0
+ ev[0].udata = (*byte)(unsafe.Pointer(pd))
+ ev[1] = ev[0]
+ ev[1].filter = _EVFILT_WRITE
+ n := kevent(kq, &ev[0], 2, nil, 0, nil)
+ if n < 0 {
+ return -n
+ }
+ return 0
+}
+
+func netpollclose(fd uintptr) int32 {
+ // Don't need to unregister because calling close()
+ // on fd will remove any kevents that reference the descriptor.
+ return 0
+}
+
+func netpollarm(pd *pollDesc, mode int) {
+ gothrow("unused")
+}
+
+// Polls for ready network connections.
+// Returns list of goroutines that become runnable.
+func netpoll(block bool) (gp *g) {
+ if kq == -1 {
+ return
+ }
+ var tp *timespec
+ var ts timespec
+ if !block {
+ tp = &ts
+ }
+ var events [64]keventt
+retry:
+ n := kevent(kq, nil, 0, &events[0], int32(len(events)), tp)
+ if n < 0 {
+ if n != -_EINTR && n != netpolllasterr {
+ netpolllasterr = n
+ println("runtime: kevent on fd", kq, "failed with", -n)
+ }
+ goto retry
+ }
+ for i := 0; i < int(n); i++ {
+ ev := &events[i]
+ var mode int32
+ if ev.filter == _EVFILT_READ {
+ mode += 'r'
+ }
+ if ev.filter == _EVFILT_WRITE {
+ mode += 'w'
+ }
+ if mode != 0 {
+ netpollready((**g)(noescape(unsafe.Pointer(&gp))), (*pollDesc)(unsafe.Pointer(ev.udata)), mode)
+ }
+ }
+ if block && gp == nil {
+ goto retry
+ }
+ return gp
+}
diff --git a/src/pkg/runtime/netpoll_nacl.c b/src/pkg/runtime/netpoll_nacl.c
deleted file mode 100644
index b75753a23b..0000000000
--- a/src/pkg/runtime/netpoll_nacl.c
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-#include "runtime.h"
-#include "defs_GOOS_GOARCH.h"
-#include "os_GOOS.h"
-
-// Fake network poller for NaCl.
-// Should never be used, because NaCl network connections do not honor "SetNonblock".
-
-void
-runtime·netpollinit(void)
-{
-}
-
-int32
-runtime·netpollopen(uintptr fd, PollDesc *pd)
-{
- USED(fd);
- USED(pd);
- return 0;
-}
-
-int32
-runtime·netpollclose(uintptr fd)
-{
- USED(fd);
- return 0;
-}
-
-G*
-runtime·netpoll(bool block)
-{
- USED(block);
- return nil;
-}
diff --git a/src/pkg/runtime/netpoll_nacl.go b/src/pkg/runtime/netpoll_nacl.go
new file mode 100644
index 0000000000..5cbc300321
--- /dev/null
+++ b/src/pkg/runtime/netpoll_nacl.go
@@ -0,0 +1,26 @@
+// 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.
+
+// Fake network poller for NaCl.
+// Should never be used, because NaCl network connections do not honor "SetNonblock".
+
+package runtime
+
+func netpollinit() {
+}
+
+func netpollopen(fd uintptr, pd *pollDesc) int32 {
+ return 0
+}
+
+func netpollclose(fd uintptr) int32 {
+ return 0
+}
+
+func netpollarm(pd *pollDesc, mode int) {
+}
+
+func netpoll(block bool) *g {
+ return nil
+}
diff --git a/src/pkg/runtime/netpoll_solaris.c b/src/pkg/runtime/netpoll_solaris.c
index 569aee52d4..d422719cf1 100644
--- a/src/pkg/runtime/netpoll_solaris.c
+++ b/src/pkg/runtime/netpoll_solaris.c
@@ -84,35 +84,31 @@ extern uintptr libc·port_getn;
int32
runtime·fcntl(int32 fd, int32 cmd, uintptr arg)
{
- return runtime·sysvicall6(libc·fcntl, 3,
- (uintptr)fd, (uintptr)cmd, (uintptr)arg);
+ return runtime·sysvicall3(libc·fcntl, (uintptr)fd, (uintptr)cmd, (uintptr)arg);
}
int32
runtime·port_create(void)
{
- return runtime·sysvicall6(libc·port_create, 0);
+ return runtime·sysvicall0(libc·port_create);
}
int32
runtime·port_associate(int32 port, int32 source, uintptr object, int32 events, uintptr user)
{
- return runtime·sysvicall6(libc·port_associate,
- 5, (uintptr)port, (uintptr)source, object, (uintptr)events, user);
+ return runtime·sysvicall5(libc·port_associate, (uintptr)port, (uintptr)source, object, (uintptr)events, user);
}
int32
runtime·port_dissociate(int32 port, int32 source, uintptr object)
{
- return runtime·sysvicall6(libc·port_dissociate,
- 3, (uintptr)port, (uintptr)source, object);
+ return runtime·sysvicall3(libc·port_dissociate, (uintptr)port, (uintptr)source, object);
}
int32
runtime·port_getn(int32 port, PortEvent *evs, uint32 max, uint32 *nget, Timespec *timeout)
{
- return runtime·sysvicall6(libc·port_getn, 5, (uintptr)port,
- (uintptr)evs, (uintptr)max, (uintptr)nget, (uintptr)timeout);
+ return runtime·sysvicall5(libc·port_getn, (uintptr)port, (uintptr)evs, (uintptr)max, (uintptr)nget, (uintptr)timeout);
}
static int32 portfd = -1;
diff --git a/src/pkg/runtime/netpoll_windows.c b/src/pkg/runtime/netpoll_windows.c
index 4528292125..64da41ad90 100644
--- a/src/pkg/runtime/netpoll_windows.c
+++ b/src/pkg/runtime/netpoll_windows.c
@@ -47,7 +47,7 @@ static uintptr iocphandle = INVALID_HANDLE_VALUE; // completion port io handle
void
runtime·netpollinit(void)
{
- iocphandle = (uintptr)runtime·stdcall(runtime·CreateIoCompletionPort, 4, INVALID_HANDLE_VALUE, (uintptr)0, (uintptr)0, (uintptr)DWORD_MAX);
+ iocphandle = (uintptr)runtime·stdcall4(runtime·CreateIoCompletionPort, INVALID_HANDLE_VALUE, 0, 0, DWORD_MAX);
if(iocphandle == 0) {
runtime·printf("netpoll: failed to create iocp handle (errno=%d)\n", runtime·getlasterror());
runtime·throw("netpoll: failed to create iocp handle");
@@ -59,7 +59,7 @@ int32
runtime·netpollopen(uintptr fd, PollDesc *pd)
{
USED(pd);
- if(runtime·stdcall(runtime·CreateIoCompletionPort, 4, fd, iocphandle, (uintptr)0, (uintptr)0) == 0)
+ if(runtime·stdcall4(runtime·CreateIoCompletionPort, fd, iocphandle, 0, 0) == 0)
return -runtime·getlasterror();
return 0;
}
@@ -103,7 +103,7 @@ retry:
n = 8;
if(block)
g->m->blocked = true;
- if(runtime·stdcall(runtime·GetQueuedCompletionStatusEx, 6, iocphandle, entries, (uintptr)n, &n, (uintptr)wait, (uintptr)0) == 0) {
+ if(runtime·stdcall6(runtime·GetQueuedCompletionStatusEx, iocphandle, (uintptr)entries, n, (uintptr)&n, wait, 0) == 0) {
g->m->blocked = false;
errno = runtime·getlasterror();
if(!block && errno == WAIT_TIMEOUT)
@@ -116,7 +116,7 @@ retry:
op = entries[i].op;
errno = 0;
qty = 0;
- if(runtime·stdcall(runtime·WSAGetOverlappedResult, 5, runtime·netpollfd(op->pd), op, &qty, (uintptr)0, (uintptr)&flags) == 0)
+ if(runtime·stdcall5(runtime·WSAGetOverlappedResult, runtime·netpollfd(op->pd), (uintptr)op, (uintptr)&qty, 0, (uintptr)&flags) == 0)
errno = runtime·getlasterror();
handlecompletion(&gp, op, errno, qty);
}
@@ -126,7 +126,7 @@ retry:
qty = 0;
if(block)
g->m->blocked = true;
- if(runtime·stdcall(runtime·GetQueuedCompletionStatus, 5, iocphandle, &qty, &key, &op, (uintptr)wait) == 0) {
+ if(runtime·stdcall5(runtime·GetQueuedCompletionStatus, iocphandle, (uintptr)&qty, (uintptr)&key, (uintptr)&op, wait) == 0) {
g->m->blocked = false;
errno = runtime·getlasterror();
if(!block && errno == WAIT_TIMEOUT)
diff --git a/src/pkg/runtime/noasm_arm.go b/src/pkg/runtime/noasm_arm.go
new file mode 100644
index 0000000000..dd3ef82676
--- /dev/null
+++ b/src/pkg/runtime/noasm_arm.go
@@ -0,0 +1,54 @@
+// 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.
+
+// Routines that are implemented in assembly in asm_{amd64,386}.s
+// but are implemented in Go for arm.
+
+package runtime
+
+func cmpstring(s1, s2 string) int {
+ l := len(s1)
+ if len(s2) < l {
+ l = len(s2)
+ }
+ for i := 0; i < l; i++ {
+ c1, c2 := s1[i], s2[i]
+ if c1 < c2 {
+ return -1
+ }
+ if c1 > c2 {
+ return +1
+ }
+ }
+ if len(s1) < len(s2) {
+ return -1
+ }
+ if len(s1) > len(s2) {
+ return +1
+ }
+ return 0
+}
+
+func cmpbytes(s1, s2 []byte) int {
+ l := len(s1)
+ if len(s2) < l {
+ l = len(s2)
+ }
+ for i := 0; i < l; i++ {
+ c1, c2 := s1[i], s2[i]
+ if c1 < c2 {
+ return -1
+ }
+ if c1 > c2 {
+ return +1
+ }
+ }
+ if len(s1) < len(s2) {
+ return -1
+ }
+ if len(s1) > len(s2) {
+ return +1
+ }
+ return 0
+}
diff --git a/src/pkg/runtime/os_darwin.c b/src/pkg/runtime/os_darwin.c
index 84b69b047a..536f688cf1 100644
--- a/src/pkg/runtime/os_darwin.c
+++ b/src/pkg/runtime/os_darwin.c
@@ -7,7 +7,7 @@
#include "os_GOOS.h"
#include "signal_unix.h"
#include "stack.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
extern SigTab runtime·sigtab[];
@@ -22,16 +22,31 @@ unimplemented(int8 *name)
*(int32*)1231 = 1231;
}
+#pragma textflag NOSPLIT
void
runtime·semawakeup(M *mp)
{
runtime·mach_semrelease(mp->waitsema);
}
+static void
+semacreate(void)
+{
+ g->m->scalararg[0] = runtime·mach_semcreate();
+}
+
+#pragma textflag NOSPLIT
uintptr
runtime·semacreate(void)
{
- return runtime·mach_semcreate();
+ uintptr x;
+ void (*fn)(void);
+
+ fn = semacreate;
+ runtime·onM(&fn);
+ x = g->m->scalararg[0];
+ g->m->scalararg[0] = 0;
+ return x;
}
// BSD interface for threading.
@@ -56,6 +71,7 @@ runtime·osinit(void)
runtime·ncpu = out;
}
+#pragma textflag NOSPLIT
void
runtime·get_random_data(byte **rnd, int32 *rnd_len)
{
@@ -143,14 +159,13 @@ runtime·unminit(void)
// Mach IPC, to get at semaphores
// Definitions are in /usr/include/mach on a Mac.
-#pragma textflag NOSPLIT
static void
macherror(int32 r, int8 *fn)
{
runtime·prints("mach error ");
runtime·prints(fn);
runtime·prints(": ");
- runtime·printint_c(r);
+ runtime·printint(r);
runtime·prints("\n");
runtime·throw("mach error");
}
@@ -218,7 +233,7 @@ machcall(MachHeader *h, int32 maxsize, int32 rxsize)
runtime·prints("send:\t");
for(i=0; i<h->msgh_size/sizeof(p[0]); i++){
runtime·prints(" ");
- runtime·printpointer_c((void*)p[i]);
+ runtime·printpointer((void*)p[i]);
if(i%8 == 7)
runtime·prints("\n\t");
}
@@ -231,7 +246,7 @@ machcall(MachHeader *h, int32 maxsize, int32 rxsize)
if(ret != 0){
if(DebugMach){
runtime·prints("mach_msg error ");
- runtime·printint_c(ret);
+ runtime·printint(ret);
runtime·prints("\n");
}
return ret;
@@ -242,7 +257,7 @@ machcall(MachHeader *h, int32 maxsize, int32 rxsize)
runtime·prints("recv:\t");
for(i=0; i<h->msgh_size/sizeof(p[0]); i++){
runtime·prints(" ");
- runtime·printpointer_c((void*)p[i]);
+ runtime·printpointer((void*)p[i]);
if(i%8 == 7)
runtime·prints("\n\t");
}
@@ -253,9 +268,9 @@ machcall(MachHeader *h, int32 maxsize, int32 rxsize)
if(h->msgh_id != id+Reply){
if(DebugMach){
runtime·prints("mach_msg reply id mismatch ");
- runtime·printint_c(h->msgh_id);
+ runtime·printint(h->msgh_id);
runtime·prints(" != ");
- runtime·printint_c(id+Reply);
+ runtime·printint(id+Reply);
runtime·prints("\n");
}
return -303; // MIG_REPLY_MISMATCH
@@ -272,7 +287,7 @@ machcall(MachHeader *h, int32 maxsize, int32 rxsize)
&& !(h->msgh_bits & MACH_MSGH_BITS_COMPLEX)){
if(DebugMach){
runtime·prints("mig result ");
- runtime·printint_c(c->code);
+ runtime·printint(c->code);
runtime·prints("\n");
}
return c->code;
@@ -281,9 +296,9 @@ machcall(MachHeader *h, int32 maxsize, int32 rxsize)
if(h->msgh_size != rxsize){
if(DebugMach){
runtime·prints("mach_msg reply size mismatch ");
- runtime·printint_c(h->msgh_size);
+ runtime·printint(h->msgh_size);
runtime·prints(" != ");
- runtime·printint_c(rxsize);
+ runtime·printint(rxsize);
runtime·prints("\n");
}
return -307; // MIG_ARRAY_TOO_LARGE
@@ -398,38 +413,88 @@ int32 runtime·mach_semaphore_timedwait(uint32 sema, uint32 sec, uint32 nsec);
int32 runtime·mach_semaphore_signal(uint32 sema);
int32 runtime·mach_semaphore_signal_all(uint32 sema);
-#pragma textflag NOSPLIT
-int32
-runtime·semasleep(int64 ns)
+static void
+semasleep(void)
{
int32 r, secs, nsecs;
+ int64 ns;
+
+ ns = (int64)(uint32)g->m->scalararg[0] | (int64)(uint32)g->m->scalararg[1]<<32;
+ g->m->scalararg[0] = 0;
+ g->m->scalararg[1] = 0;
if(ns >= 0) {
secs = runtime·timediv(ns, 1000000000, &nsecs);
r = runtime·mach_semaphore_timedwait(g->m->waitsema, secs, nsecs);
- if(r == KERN_ABORTED || r == KERN_OPERATION_TIMED_OUT)
- return -1;
+ if(r == KERN_ABORTED || r == KERN_OPERATION_TIMED_OUT) {
+ g->m->scalararg[0] = -1;
+ return;
+ }
if(r != 0)
macherror(r, "semaphore_wait");
- return 0;
+ g->m->scalararg[0] = 0;
+ return;
}
while((r = runtime·mach_semaphore_wait(g->m->waitsema)) != 0) {
if(r == KERN_ABORTED) // interrupted
continue;
macherror(r, "semaphore_wait");
}
- return 0;
+ g->m->scalararg[0] = 0;
+ return;
}
+#pragma textflag NOSPLIT
+int32
+runtime·semasleep(int64 ns)
+{
+ int32 r;
+ void (*fn)(void);
+
+ g->m->scalararg[0] = (uint32)ns;
+ g->m->scalararg[1] = (uint32)(ns>>32);
+ fn = semasleep;
+ runtime·onM(&fn);
+ r = g->m->scalararg[0];
+ g->m->scalararg[0] = 0;
+ return r;
+}
+
+static int32 mach_semrelease_errno;
+
+static void
+mach_semrelease_fail(void)
+{
+ macherror(mach_semrelease_errno, "semaphore_signal");
+}
+
+#pragma textflag NOSPLIT
void
runtime·mach_semrelease(uint32 sem)
{
int32 r;
+ void (*fn)(void);
while((r = runtime·mach_semaphore_signal(sem)) != 0) {
if(r == KERN_ABORTED) // interrupted
continue;
- macherror(r, "semaphore_signal");
+
+ // mach_semrelease must be completely nosplit,
+ // because it is called from Go code.
+ // If we're going to die, start that process on the m stack
+ // to avoid a Go stack split.
+ // Only do that if we're actually running on the g stack.
+ // We might be on the gsignal stack, and if so, onM will abort.
+ // We use the global variable instead of scalararg because
+ // we might be on the gsignal stack, having interrupted a
+ // normal call to onM. It doesn't quite matter, since the
+ // program is about to die, but better to be clean.
+ mach_semrelease_errno = r;
+ fn = mach_semrelease_fail;
+ if(g == g->m->curg)
+ runtime·onM(&fn);
+ else
+ fn();
}
}
@@ -488,7 +553,7 @@ runtime·memlimit(void)
void
runtime·setsig(int32 i, GoSighandler *fn, bool restart)
{
- Sigaction sa;
+ SigactionT sa;
runtime·memclr((byte*)&sa, sizeof sa);
sa.sa_flags = SA_SIGINFO|SA_ONSTACK;
@@ -503,7 +568,7 @@ runtime·setsig(int32 i, GoSighandler *fn, bool restart)
GoSighandler*
runtime·getsig(int32 i)
{
- Sigaction sa;
+ SigactionT sa;
runtime·memclr((byte*)&sa, sizeof sa);
runtime·sigaction(i, nil, &sa);
diff --git a/src/pkg/runtime/os_darwin.go b/src/pkg/runtime/os_darwin.go
new file mode 100644
index 0000000000..e0f63ddb90
--- /dev/null
+++ b/src/pkg/runtime/os_darwin.go
@@ -0,0 +1,26 @@
+// 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"
+
+func bsdthread_create(stk, mm, gg, fn unsafe.Pointer) int32
+func bsdthread_register() int32
+func mach_msg_trap(h unsafe.Pointer, op int32, send_size, rcv_size, rcv_name, timeout, notify uint32) int32
+func mach_reply_port() uint32
+func mach_task_self() uint32
+func mach_thread_self() uint32
+func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32
+func sigprocmask(sig int32, new, old unsafe.Pointer)
+func sigaction(mode uint32, new, old unsafe.Pointer)
+func sigaltstack(new, old unsafe.Pointer)
+func sigtramp()
+func setitimer(mode int32, new, old unsafe.Pointer)
+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
+
+const stackSystem = 0
diff --git a/src/pkg/runtime/os_darwin.h b/src/pkg/runtime/os_darwin.h
index 91a405f214..e8bb45dafc 100644
--- a/src/pkg/runtime/os_darwin.h
+++ b/src/pkg/runtime/os_darwin.h
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#define SS_DISABLE 4
-
typedef byte* kevent_udata;
int32 runtime·bsdthread_create(void*, M*, G*, void(*)(void));
@@ -25,8 +23,8 @@ typedef uint32 Sigset;
void runtime·sigprocmask(int32, Sigset*, Sigset*);
void runtime·unblocksignals(void);
-struct Sigaction;
-void runtime·sigaction(uintptr, struct Sigaction*, struct Sigaction*);
+struct SigactionT;
+void runtime·sigaction(uintptr, struct SigactionT*, struct SigactionT*);
struct StackT;
void runtime·sigaltstack(struct StackT*, struct StackT*);
@@ -35,8 +33,11 @@ void runtime·sigpanic(void);
void runtime·setitimer(int32, Itimerval*, Itimerval*);
-#define NSIG 32
-#define SI_USER 0 /* empirically true, but not what headers say */
-#define SIG_BLOCK 1
-#define SIG_UNBLOCK 2
-#define SIG_SETMASK 3
+enum {
+ 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/pkg/runtime/os_dragonfly.c b/src/pkg/runtime/os_dragonfly.c
index ce5307af07..d470f7c78c 100644
--- a/src/pkg/runtime/os_dragonfly.c
+++ b/src/pkg/runtime/os_dragonfly.c
@@ -7,7 +7,7 @@
#include "os_GOOS.h"
#include "signal_unix.h"
#include "stack.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
extern SigTab runtime·sigtab[];
extern int32 runtime·sys_umtx_sleep(uint32*, int32, int32);
@@ -40,13 +40,38 @@ getncpu(void)
return 1;
}
+static void futexsleep(void);
+
#pragma textflag NOSPLIT
void
runtime·futexsleep(uint32 *addr, uint32 val, int64 ns)
{
+ void (*fn)(void);
+
+ g->m->ptrarg[0] = addr;
+ g->m->scalararg[0] = val;
+ g->m->ptrarg[1] = &ns;
+
+ fn = futexsleep;
+ runtime·onM(&fn);
+}
+
+static void
+futexsleep(void)
+{
+ uint32 *addr;
+ uint32 val;
+ int64 ns;
int32 timeout = 0;
int32 ret;
+ addr = g->m->ptrarg[0];
+ val = g->m->scalararg[0];
+ ns = *(int64*)g->m->ptrarg[1];
+ g->m->ptrarg[0] = nil;
+ g->m->scalararg[0] = 0;
+ g->m->ptrarg[1] = nil;
+
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
@@ -63,28 +88,49 @@ runtime·futexsleep(uint32 *addr, uint32 val, int64 ns)
return;
runtime·prints("umtx_wait addr=");
- runtime·printpointer_c(addr);
+ runtime·printpointer(addr);
runtime·prints(" val=");
- runtime·printint_c(val);
+ runtime·printint(val);
runtime·prints(" ret=");
- runtime·printint_c(ret);
+ runtime·printint(ret);
runtime·prints("\n");
*(int32*)0x1005 = 0x1005;
}
+static void badfutexwakeup(void);
+
+#pragma textflag NOSPLIT
void
runtime·futexwakeup(uint32 *addr, uint32 cnt)
{
int32 ret;
+ void (*fn)(void);
ret = runtime·sys_umtx_wakeup(addr, cnt);
if(ret >= 0)
return;
- runtime·printf("umtx_wake addr=%p ret=%d\n", addr, ret);
+ g->m->ptrarg[0] = addr;
+ g->m->scalararg[0] = ret;
+ fn = badfutexwakeup;
+ if(g == g->m->gsignal)
+ fn();
+ else
+ runtime·onM(&fn);
*(int32*)0x1006 = 0x1006;
}
+static void
+badfutexwakeup(void)
+{
+ void *addr;
+ int32 ret;
+
+ addr = g->m->ptrarg[0];
+ ret = g->m->scalararg[0];
+ runtime·printf("umtx_wake addr=%p ret=%d\n", addr, ret);
+}
+
void runtime·lwp_start(void*);
void
@@ -119,6 +165,7 @@ runtime·osinit(void)
runtime·ncpu = getncpu();
}
+#pragma textflag NOSPLIT
void
runtime·get_random_data(byte **rnd, int32 *rnd_len)
{
@@ -207,7 +254,7 @@ uintptr
runtime·memlimit(void)
{
Rlimit rl;
- extern byte text[], end[];
+ extern byte runtime·text[], runtime·end[];
uintptr used;
if(runtime·getrlimit(RLIMIT_AS, &rl) != 0)
@@ -218,7 +265,7 @@ runtime·memlimit(void)
// Estimate our VM footprint excluding the heap.
// Not an exact science: use size of binary plus
// some room for thread stacks.
- used = end - text + (64<<20);
+ used = runtime·end - runtime·text + (64<<20);
if(used >= rl.rlim_cur)
return 0;
@@ -240,12 +287,12 @@ typedef struct sigaction {
} __sigaction_u; /* signal handler */
int32 sa_flags; /* see signal options below */
Sigset sa_mask; /* signal mask to apply */
-} Sigaction;
+} SigactionT;
void
runtime·setsig(int32 i, GoSighandler *fn, bool restart)
{
- Sigaction sa;
+ SigactionT sa;
runtime·memclr((byte*)&sa, sizeof sa);
sa.sa_flags = SA_SIGINFO|SA_ONSTACK;
@@ -264,7 +311,7 @@ runtime·setsig(int32 i, GoSighandler *fn, bool restart)
GoSighandler*
runtime·getsig(int32 i)
{
- Sigaction sa;
+ SigactionT sa;
runtime·memclr((byte*)&sa, sizeof sa);
runtime·sigaction(i, nil, &sa);
diff --git a/src/pkg/runtime/os_dragonfly.go b/src/pkg/runtime/os_dragonfly.go
new file mode 100644
index 0000000000..cdaa06986e
--- /dev/null
+++ b/src/pkg/runtime/os_dragonfly.go
@@ -0,0 +1,20 @@
+// 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"
+
+func lwp_create(param unsafe.Pointer) int32
+func sigaltstack(new, old unsafe.Pointer)
+func sigaction(sig int32, new, old unsafe.Pointer)
+func sigprocmask(new, old unsafe.Pointer)
+func setitimer(mode int32, new, old unsafe.Pointer)
+func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32
+func getrlimit(kind int32, limit unsafe.Pointer) int32
+func raise(sig int32)
+func sys_umtx_sleep(addr unsafe.Pointer, val, timeout int32) int32
+func sys_umtx_wakeup(addr unsafe.Pointer, val int32) int32
+
+const stackSystem = 0
diff --git a/src/pkg/runtime/os_dragonfly.h b/src/pkg/runtime/os_dragonfly.h
index fddeede85d..389736a323 100644
--- a/src/pkg/runtime/os_dragonfly.h
+++ b/src/pkg/runtime/os_dragonfly.h
@@ -2,13 +2,12 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#define SS_DISABLE 4
typedef byte* kevent_udata;
int32 runtime·lwp_create(Lwpparams*);
void runtime·sigpanic(void);
-void runtime·sigaltstack(Sigaltstack*, Sigaltstack*);
+void runtime·sigaltstack(SigaltstackT*, SigaltstackT*);
struct sigaction;
void runtime·sigaction(int32, struct sigaction*, struct sigaction*);
void runtime·sigprocmask(Sigset *, Sigset *);
@@ -16,11 +15,13 @@ void runtime·unblocksignals(void);
void runtime·setitimer(int32, Itimerval*, Itimerval*);
int32 runtime·sysctl(uint32*, uint32, byte*, uintptr*, byte*, uintptr);
+enum {
+ NSIG = 33,
+ SI_USER = 0x10001,
+ SS_DISABLE = 4,
+ RLIMIT_AS = 10,
+};
-#define NSIG 33
-#define SI_USER 0x10001
-
-#define RLIMIT_AS 10
typedef struct Rlimit Rlimit;
struct Rlimit {
int64 rlim_cur;
diff --git a/src/pkg/runtime/os_freebsd.c b/src/pkg/runtime/os_freebsd.c
index 794578c208..aae944ea11 100644
--- a/src/pkg/runtime/os_freebsd.c
+++ b/src/pkg/runtime/os_freebsd.c
@@ -7,7 +7,7 @@
#include "os_GOOS.h"
#include "signal_unix.h"
#include "stack.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
extern SigTab runtime·sigtab[];
extern int32 runtime·sys_umtx_op(uint32*, int32, uint32, void*, void*);
@@ -42,12 +42,37 @@ getncpu(void)
// FreeBSD's umtx_op syscall is effectively the same as Linux's futex, and
// thus the code is largely similar. See linux/thread.c and lock_futex.c for comments.
+static void futexsleep(void);
+
#pragma textflag NOSPLIT
void
runtime·futexsleep(uint32 *addr, uint32 val, int64 ns)
{
+ void (*fn)(void);
+
+ g->m->ptrarg[0] = addr;
+ g->m->scalararg[0] = val;
+ g->m->ptrarg[1] = &ns;
+
+ fn = futexsleep;
+ runtime·onM(&fn);
+}
+
+static void
+futexsleep(void)
+{
+ uint32 *addr;
+ uint32 val;
+ int64 ns;
int32 ret;
Timespec ts;
+
+ addr = g->m->ptrarg[0];
+ val = g->m->scalararg[0];
+ ns = *(int64*)g->m->ptrarg[1];
+ g->m->ptrarg[0] = nil;
+ g->m->scalararg[0] = 0;
+ g->m->ptrarg[1] = nil;
if(ns < 0) {
ret = runtime·sys_umtx_op(addr, UMTX_OP_WAIT_UINT_PRIVATE, val, nil, nil);
@@ -64,28 +89,49 @@ runtime·futexsleep(uint32 *addr, uint32 val, int64 ns)
fail:
runtime·prints("umtx_wait addr=");
- runtime·printpointer_c(addr);
+ runtime·printpointer(addr);
runtime·prints(" val=");
- runtime·printint_c(val);
+ runtime·printint(val);
runtime·prints(" ret=");
- runtime·printint_c(ret);
+ runtime·printint(ret);
runtime·prints("\n");
*(int32*)0x1005 = 0x1005;
}
+static void badfutexwakeup(void);
+
+#pragma textflag NOSPLIT
void
runtime·futexwakeup(uint32 *addr, uint32 cnt)
{
int32 ret;
+ void (*fn)(void);
ret = runtime·sys_umtx_op(addr, UMTX_OP_WAKE_PRIVATE, cnt, nil, nil);
if(ret >= 0)
return;
- runtime·printf("umtx_wake addr=%p ret=%d\n", addr, ret);
+ g->m->ptrarg[0] = addr;
+ g->m->scalararg[0] = ret;
+ fn = badfutexwakeup;
+ if(g == g->m->gsignal)
+ fn();
+ else
+ runtime·onM(&fn);
*(int32*)0x1006 = 0x1006;
}
+static void
+badfutexwakeup(void)
+{
+ void *addr;
+ int32 ret;
+
+ addr = g->m->ptrarg[0];
+ ret = g->m->scalararg[0];
+ runtime·printf("umtx_wake addr=%p ret=%d\n", addr, ret);
+}
+
void runtime·thr_start(void*);
void
@@ -127,6 +173,7 @@ runtime·osinit(void)
runtime·ncpu = getncpu();
}
+#pragma textflag NOSPLIT
void
runtime·get_random_data(byte **rnd, int32 *rnd_len)
{
@@ -215,7 +262,7 @@ uintptr
runtime·memlimit(void)
{
Rlimit rl;
- extern byte text[], end[];
+ extern byte runtime·text[], runtime·end[];
uintptr used;
if(runtime·getrlimit(RLIMIT_AS, &rl) != 0)
@@ -226,7 +273,7 @@ runtime·memlimit(void)
// Estimate our VM footprint excluding the heap.
// Not an exact science: use size of binary plus
// some room for thread stacks.
- used = end - text + (64<<20);
+ used = runtime·end - runtime·text + (64<<20);
if(used >= rl.rlim_cur)
return 0;
@@ -248,12 +295,12 @@ typedef struct sigaction {
} __sigaction_u; /* signal handler */
int32 sa_flags; /* see signal options below */
Sigset sa_mask; /* signal mask to apply */
-} Sigaction;
+} SigactionT;
void
runtime·setsig(int32 i, GoSighandler *fn, bool restart)
{
- Sigaction sa;
+ SigactionT sa;
runtime·memclr((byte*)&sa, sizeof sa);
sa.sa_flags = SA_SIGINFO|SA_ONSTACK;
@@ -272,7 +319,7 @@ runtime·setsig(int32 i, GoSighandler *fn, bool restart)
GoSighandler*
runtime·getsig(int32 i)
{
- Sigaction sa;
+ SigactionT sa;
runtime·memclr((byte*)&sa, sizeof sa);
runtime·sigaction(i, nil, &sa);
diff --git a/src/pkg/runtime/os_freebsd.go b/src/pkg/runtime/os_freebsd.go
new file mode 100644
index 0000000000..96964f1e1e
--- /dev/null
+++ b/src/pkg/runtime/os_freebsd.go
@@ -0,0 +1,19 @@
+// 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"
+
+func thr_new(param unsafe.Pointer, size int32)
+func sigaltstack(new, old unsafe.Pointer)
+func sigaction(sig int32, new, old unsafe.Pointer)
+func sigprocmask(new, old unsafe.Pointer)
+func setitimer(mode int32, new, old unsafe.Pointer)
+func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32
+func getrlimit(kind int32, limit unsafe.Pointer) int32
+func raise(sig int32)
+func sys_umtx_op(addr unsafe.Pointer, mode int32, val uint32, ptr2, ts unsafe.Pointer) int32
+
+const stackSystem = 0
diff --git a/src/pkg/runtime/os_freebsd.h b/src/pkg/runtime/os_freebsd.h
index 4b2c253308..b86bb393c2 100644
--- a/src/pkg/runtime/os_freebsd.h
+++ b/src/pkg/runtime/os_freebsd.h
@@ -2,13 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#define SS_DISABLE 4
-
typedef byte* kevent_udata;
int32 runtime·thr_new(ThrParam*, int32);
void runtime·sigpanic(void);
-void runtime·sigaltstack(Sigaltstack*, Sigaltstack*);
+void runtime·sigaltstack(SigaltstackT*, SigaltstackT*);
struct sigaction;
void runtime·sigaction(int32, struct sigaction*, struct sigaction*);
void runtime·sigprocmask(Sigset *, Sigset *);
@@ -16,11 +14,13 @@ void runtime·unblocksignals(void);
void runtime·setitimer(int32, Itimerval*, Itimerval*);
int32 runtime·sysctl(uint32*, uint32, byte*, uintptr*, byte*, uintptr);
+enum {
+ SS_DISABLE = 4,
+ NSIG = 33,
+ SI_USER = 0x10001,
+ RLIMIT_AS = 10,
+};
-#define NSIG 33
-#define SI_USER 0x10001
-
-#define RLIMIT_AS 10
typedef struct Rlimit Rlimit;
struct Rlimit {
int64 rlim_cur;
diff --git a/src/pkg/runtime/os_freebsd_arm.c b/src/pkg/runtime/os_freebsd_arm.c
index 1fa235b01d..2f2d7767f2 100644
--- a/src/pkg/runtime/os_freebsd_arm.c
+++ b/src/pkg/runtime/os_freebsd_arm.c
@@ -5,7 +5,7 @@
#include "runtime.h"
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
void
runtime·checkgoarm(void)
diff --git a/src/pkg/runtime/os_linux.c b/src/pkg/runtime/os_linux.c
index 8aadee7215..2f67ca746f 100644
--- a/src/pkg/runtime/os_linux.c
+++ b/src/pkg/runtime/os_linux.c
@@ -7,7 +7,7 @@
#include "os_GOOS.h"
#include "signal_unix.h"
#include "stack.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
extern SigTab runtime·sigtab[];
@@ -68,24 +68,44 @@ runtime·futexsleep(uint32 *addr, uint32 val, int64 ns)
runtime·futex(addr, FUTEX_WAIT, val, &ts, nil, 0);
}
+static void badfutexwakeup(void);
+
// If any procs are sleeping on addr, wake up at most cnt.
+#pragma textflag NOSPLIT
void
runtime·futexwakeup(uint32 *addr, uint32 cnt)
{
int64 ret;
+ void (*fn)(void);
ret = runtime·futex(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.
- runtime·printf("futexwakeup addr=%p returned %D\n", addr, ret);
+ g->m->ptrarg[0] = addr;
+ g->m->scalararg[0] = (int32)ret; // truncated but fine
+ fn = badfutexwakeup;
+ if(g == g->m->gsignal)
+ fn();
+ else
+ runtime·onM(&fn);
*(int32*)0x1006 = 0x1006;
}
+static void
+badfutexwakeup(void)
+{
+ void *addr;
+ int64 ret;
+
+ addr = g->m->ptrarg[0];
+ ret = (int32)g->m->scalararg[0];
+ runtime·printf("futexwakeup addr=%p returned %D\n", addr, ret);
+}
+
extern runtime·sched_getaffinity(uintptr pid, uintptr len, uintptr *buf);
static int32
getproccount(void)
@@ -178,6 +198,7 @@ runtime·osinit(void)
byte* runtime·startup_random_data;
uint32 runtime·startup_random_data_len;
+#pragma textflag NOSPLIT
void
runtime·get_random_data(byte **rnd, int32 *rnd_len)
{
@@ -271,7 +292,7 @@ uintptr
runtime·memlimit(void)
{
Rlimit rl;
- extern byte text[], end[];
+ extern byte runtime·text[], runtime·end[];
uintptr used;
if(runtime·getrlimit(RLIMIT_AS, &rl) != 0)
@@ -282,7 +303,7 @@ runtime·memlimit(void)
// Estimate our VM footprint excluding the heap.
// Not an exact science: use size of binary plus
// some room for thread stacks.
- used = end - text + (64<<20);
+ used = runtime·end - runtime·text + (64<<20);
if(used >= rl.rlim_cur)
return 0;
@@ -309,7 +330,7 @@ extern void runtime·sigreturn(void); // calls rt_sigreturn, only used with SA_R
void
runtime·setsig(int32 i, GoSighandler *fn, bool restart)
{
- Sigaction sa;
+ SigactionT sa;
runtime·memclr((byte*)&sa, sizeof sa);
sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESTORER;
@@ -336,7 +357,7 @@ runtime·setsig(int32 i, GoSighandler *fn, bool restart)
GoSighandler*
runtime·getsig(int32 i)
{
- Sigaction sa;
+ SigactionT sa;
runtime·memclr((byte*)&sa, sizeof sa);
if(runtime·rt_sigaction(i, nil, &sa, sizeof(sa.sa_mask)) != 0)
@@ -349,7 +370,7 @@ runtime·getsig(int32 i)
void
runtime·signalstack(byte *p, int32 n)
{
- Sigaltstack st;
+ SigaltstackT st;
st.ss_sp = p;
st.ss_size = n;
diff --git a/src/pkg/runtime/os_linux.go b/src/pkg/runtime/os_linux.go
new file mode 100644
index 0000000000..a6799cd414
--- /dev/null
+++ b/src/pkg/runtime/os_linux.go
@@ -0,0 +1,19 @@
+// 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"
+
+func futex(addr unsafe.Pointer, op int32, val uint32, ts, addr2 unsafe.Pointer, val3 uint32) int32
+func clone(flags int32, stk, mm, gg, fn unsafe.Pointer) int32
+func rt_sigaction(sig uintptr, new, old unsafe.Pointer, size uintptr) int32
+func sigaltstack(new, old unsafe.Pointer)
+func setitimer(mode int32, new, old unsafe.Pointer)
+func rtsigprocmask(sig int32, new, old unsafe.Pointer, size int32)
+func getrlimit(kind int32, limit unsafe.Pointer) int32
+func raise(sig int32)
+func sched_getaffinity(pid, len uintptr, buf *uintptr) int32
+
+const stackSystem = 0
diff --git a/src/pkg/runtime/os_linux.h b/src/pkg/runtime/os_linux.h
index d4b1902c3a..75606d6152 100644
--- a/src/pkg/runtime/os_linux.h
+++ b/src/pkg/runtime/os_linux.h
@@ -2,22 +2,25 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#define SS_DISABLE 2
// Linux-specific system calls
int32 runtime·futex(uint32*, int32, uint32, Timespec*, uint32*, uint32);
int32 runtime·clone(int32, void*, M*, G*, void(*)(void));
-struct Sigaction;
-int32 runtime·rt_sigaction(uintptr, struct Sigaction*, void*, uintptr);
+struct SigactionT;
+int32 runtime·rt_sigaction(uintptr, struct SigactionT*, void*, uintptr);
-void runtime·sigaltstack(Sigaltstack*, Sigaltstack*);
+void runtime·sigaltstack(SigaltstackT*, SigaltstackT*);
void runtime·sigpanic(void);
void runtime·setitimer(int32, Itimerval*, Itimerval*);
-
-#define NSIG 65
-#define SI_USER 0
+enum {
+ SS_DISABLE = 2,
+ NSIG = 65,
+ SI_USER = 0,
+ SIG_SETMASK = 2,
+ RLIMIT_AS = 9,
+};
// It's hard to tease out exactly how big a Sigset is, but
// rt_sigprocmask crashes if we get it wrong, so if binaries
@@ -29,9 +32,7 @@ struct Sigset
};
void runtime·rtsigprocmask(int32, Sigset*, Sigset*, int32);
void runtime·unblocksignals(void);
-#define SIG_SETMASK 2
-#define RLIMIT_AS 9
typedef struct Rlimit Rlimit;
struct Rlimit {
uintptr rlim_cur;
diff --git a/src/pkg/runtime/os_linux_386.c b/src/pkg/runtime/os_linux_386.c
index ad72814649..dc89d04e28 100644
--- a/src/pkg/runtime/os_linux_386.c
+++ b/src/pkg/runtime/os_linux_386.c
@@ -5,7 +5,7 @@
#include "runtime.h"
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
#define AT_NULL 0
#define AT_RANDOM 25
diff --git a/src/pkg/runtime/os_linux_arm.c b/src/pkg/runtime/os_linux_arm.c
index aad08b9890..e3eda7c2d1 100644
--- a/src/pkg/runtime/os_linux_arm.c
+++ b/src/pkg/runtime/os_linux_arm.c
@@ -5,7 +5,7 @@
#include "runtime.h"
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
#define AT_NULL 0
#define AT_PLATFORM 15 // introduced in at least 2.6.11
diff --git a/src/pkg/runtime/os_nacl.c b/src/pkg/runtime/os_nacl.c
index b3e0fc6636..37a1fcca0d 100644
--- a/src/pkg/runtime/os_nacl.c
+++ b/src/pkg/runtime/os_nacl.c
@@ -6,7 +6,7 @@
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
#include "arch_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
#include "stack.h"
int8 *goos = "nacl";
@@ -65,6 +65,7 @@ runtime·crash(void)
*(int32*)0 = 0;
}
+#pragma textflag NOSPLIT
void
runtime·get_random_data(byte **rnd, int32 *rnd_len)
{
@@ -112,8 +113,8 @@ runtime·newosproc(M *mp, void *stk)
}
}
-uintptr
-runtime·semacreate(void)
+static void
+semacreate(void)
{
int32 mu, cond;
@@ -128,14 +129,32 @@ runtime·semacreate(void)
runtime·throw("semacreate");
}
g->m->waitsemalock = mu;
- return cond; // assigned to m->waitsema
+ g->m->scalararg[0] = cond; // assigned to m->waitsema
}
#pragma textflag NOSPLIT
-int32
-runtime·semasleep(int64 ns)
+uint32
+runtime·semacreate(void)
+{
+ void (*fn)(void);
+ uint32 x;
+
+ fn = semacreate;
+ runtime·onM(&fn);
+ x = g->m->scalararg[0];
+ g->m->scalararg[0] = 0;
+ return x;
+}
+
+static void
+semasleep(void)
{
int32 ret;
+ int64 ns;
+
+ ns = (int64)(uint32)g->m->scalararg[0] | (int64)(uint32)g->m->scalararg[1]<<32;
+ g->m->scalararg[0] = 0;
+ g->m->scalararg[1] = 0;
ret = runtime·nacl_mutex_lock(g->m->waitsemalock);
if(ret < 0) {
@@ -145,7 +164,8 @@ runtime·semasleep(int64 ns)
if(g->m->waitsemacount > 0) {
g->m->waitsemacount = 0;
runtime·nacl_mutex_unlock(g->m->waitsemalock);
- return 0;
+ g->m->scalararg[0] = 0;
+ return;
}
while(g->m->waitsemacount == 0) {
@@ -163,7 +183,8 @@ runtime·semasleep(int64 ns)
ret = runtime·nacl_cond_timed_wait_abs(g->m->waitsema, g->m->waitsemalock, &ts);
if(ret == -ETIMEDOUT) {
runtime·nacl_mutex_unlock(g->m->waitsemalock);
- return -1;
+ g->m->scalararg[0] = -1;
+ return;
}
if(ret < 0) {
//runtime·printf("nacl_cond_timed_wait_abs: error %d\n", -ret);
@@ -174,14 +195,34 @@ runtime·semasleep(int64 ns)
g->m->waitsemacount = 0;
runtime·nacl_mutex_unlock(g->m->waitsemalock);
- return 0;
+ g->m->scalararg[0] = 0;
}
-void
-runtime·semawakeup(M *mp)
+#pragma textflag NOSPLIT
+int32
+runtime·semasleep(int64 ns)
+{
+ int32 r;
+ void (*fn)(void);
+
+ g->m->scalararg[0] = (uint32)ns;
+ g->m->scalararg[1] = (uint32)(ns>>32);
+ fn = semasleep;
+ runtime·onM(&fn);
+ r = g->m->scalararg[0];
+ g->m->scalararg[0] = 0;
+ return r;
+}
+
+static void
+semawakeup(void)
{
int32 ret;
+ M *mp;
+ mp = g->m->ptrarg[0];
+ g->m->ptrarg[0] = nil;
+
ret = runtime·nacl_mutex_lock(mp->waitsemalock);
if(ret < 0) {
//runtime·printf("nacl_mutex_lock: error %d\n", -ret);
@@ -196,10 +237,15 @@ runtime·semawakeup(M *mp)
runtime·nacl_mutex_unlock(mp->waitsemalock);
}
+#pragma textflag NOSPLIT
void
-os·sigpipe(void)
+runtime·semawakeup(M *mp)
{
- runtime·throw("too many writes on closed pipe");
+ void (*fn)(void);
+
+ g->m->ptrarg[0] = mp;
+ fn = semawakeup;
+ runtime·onM(&fn);
}
uintptr
diff --git a/src/pkg/runtime/os_nacl.go b/src/pkg/runtime/os_nacl.go
new file mode 100644
index 0000000000..12a15aea0d
--- /dev/null
+++ b/src/pkg/runtime/os_nacl.go
@@ -0,0 +1,30 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime
+
+import "unsafe"
+
+func nacl_exception_stack(p unsafe.Pointer, size int32) int32
+func nacl_exception_handler(fn, arg unsafe.Pointer) int32
+func nacl_sem_create(flag int32) int32
+func nacl_sem_wait(sem int32) int32
+func nacl_sem_post(sem int32) int32
+func nacl_mutex_create(flag int32) int32
+func nacl_mutex_lock(mutex int32) int32
+func nacl_mutex_trylock(mutex int32) int32
+func nacl_mutex_unlock(mutex int32) int32
+func nacl_cond_create(flag int32) int32
+func nacl_cond_wait(cond, n int32) int32
+func nacl_cond_signal(cond int32) int32
+func nacl_cond_broadcast(cond int32) int32
+func nacl_cond_timed_wait_abs(cond, lock int32, ts unsafe.Pointer) int32
+func nacl_thread_create(fn, stk, tls, xx unsafe.Pointer) int32
+func nacl_nanosleep(ts, extra unsafe.Pointer) int32
+
+const stackSystem = 0
+
+func os_sigpipe() {
+ gothrow("too many writes on closed pipe")
+}
diff --git a/src/pkg/runtime/os_nacl_arm.c b/src/pkg/runtime/os_nacl_arm.c
index c94b2c78a6..1248ea6449 100644
--- a/src/pkg/runtime/os_nacl_arm.c
+++ b/src/pkg/runtime/os_nacl_arm.c
@@ -5,7 +5,7 @@
#include "runtime.h"
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
void
runtime·checkgoarm(void)
diff --git a/src/pkg/runtime/os_netbsd.c b/src/pkg/runtime/os_netbsd.c
index 0889181a81..d6c3bc8261 100644
--- a/src/pkg/runtime/os_netbsd.c
+++ b/src/pkg/runtime/os_netbsd.c
@@ -7,7 +7,7 @@
#include "os_GOOS.h"
#include "signal_unix.h"
#include "stack.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
enum
{
@@ -57,18 +57,23 @@ getncpu(void)
return 1;
}
+#pragma textflag NOSPLIT
uintptr
runtime·semacreate(void)
{
return 1;
}
-#pragma textflag NOSPLIT
-int32
-runtime·semasleep(int64 ns)
+static void
+semasleep(void)
{
+ int64 ns;
Timespec ts;
+ ns = (int64)(uint32)g->m->scalararg[0] | (int64)(uint32)g->m->scalararg[1]<<32;
+ g->m->scalararg[0] = 0;
+ g->m->scalararg[1] = 0;
+
// spin-mutex lock
while(runtime·xchg(&g->m->waitsemalock, 1))
runtime·osyield();
@@ -115,7 +120,8 @@ runtime·semasleep(int64 ns)
g->m->waitsemacount--;
// spin-mutex unlock
runtime·atomicstore(&g->m->waitsemalock, 0);
- return 0; // semaphore acquired
+ g->m->scalararg[0] = 0; // semaphore acquired
+ return;
}
// semaphore not available.
@@ -128,13 +134,36 @@ runtime·semasleep(int64 ns)
// lock held but giving up
// spin-mutex unlock
runtime·atomicstore(&g->m->waitsemalock, 0);
- return -1;
+ g->m->scalararg[0] = -1;
+ return;
+}
+
+#pragma textflag NOSPLIT
+int32
+runtime·semasleep(int64 ns)
+{
+ int32 r;
+ void (*fn)(void);
+
+ g->m->scalararg[0] = (uint32)ns;
+ g->m->scalararg[1] = (uint32)(ns>>32);
+ fn = semasleep;
+ runtime·onM(&fn);
+ r = g->m->scalararg[0];
+ g->m->scalararg[0] = 0;
+ return r;
}
+static void badsemawakeup(void);
+
+#pragma textflag NOSPLIT
void
runtime·semawakeup(M *mp)
{
uint32 ret;
+ void (*fn)(void);
+ void *oldptr;
+ uintptr oldscalar;
// spin-mutex lock
while(runtime·xchg(&mp->waitsemalock, 1))
@@ -143,12 +172,39 @@ runtime·semawakeup(M *mp)
// TODO(jsing) - potential deadlock, see semasleep() for details.
// Confirm that LWP is parked before unparking...
ret = runtime·lwp_unpark(mp->procid, &mp->waitsemacount);
- if(ret != 0 && ret != ESRCH)
- runtime·printf("thrwakeup addr=%p sem=%d ret=%d\n", &mp->waitsemacount, mp->waitsemacount, ret);
+ if(ret != 0 && ret != ESRCH) {
+ // semawakeup can be called on signal stack.
+ // Save old ptrarg/scalararg so we can restore them.
+ oldptr = g->m->ptrarg[0];
+ oldscalar = g->m->scalararg[0];
+ g->m->ptrarg[0] = mp;
+ g->m->scalararg[0] = ret;
+ fn = badsemawakeup;
+ if(g == g->m->gsignal)
+ fn();
+ else
+ runtime·onM(&fn);
+ g->m->ptrarg[0] = oldptr;
+ g->m->scalararg[0] = oldscalar;
+ }
// spin-mutex unlock
runtime·atomicstore(&mp->waitsemalock, 0);
}
+static void
+badsemawakeup(void)
+{
+ M *mp;
+ int32 ret;
+
+ mp = g->m->ptrarg[0];
+ g->m->ptrarg[0] = nil;
+ ret = g->m->scalararg[0];
+ g->m->scalararg[0] = 0;
+
+ runtime·printf("thrwakeup addr=%p sem=%d ret=%d\n", &mp->waitsemacount, mp->waitsemacount, ret);
+}
+
void
runtime·newosproc(M *mp, void *stk)
{
@@ -185,6 +241,7 @@ runtime·osinit(void)
runtime·ncpu = getncpu();
}
+#pragma textflag NOSPLIT
void
runtime·get_random_data(byte **rnd, int32 *rnd_len)
{
@@ -286,12 +343,12 @@ typedef struct sigaction {
} _sa_u; /* signal handler */
uint32 sa_mask[4]; /* signal mask to apply */
int32 sa_flags; /* see signal options below */
-} Sigaction;
+} SigactionT;
void
runtime·setsig(int32 i, GoSighandler *fn, bool restart)
{
- Sigaction sa;
+ SigactionT sa;
runtime·memclr((byte*)&sa, sizeof sa);
sa.sa_flags = SA_SIGINFO|SA_ONSTACK;
@@ -310,7 +367,7 @@ runtime·setsig(int32 i, GoSighandler *fn, bool restart)
GoSighandler*
runtime·getsig(int32 i)
{
- Sigaction sa;
+ SigactionT sa;
runtime·memclr((byte*)&sa, sizeof sa);
runtime·sigaction(i, nil, &sa);
diff --git a/src/pkg/runtime/os_netbsd.go b/src/pkg/runtime/os_netbsd.go
new file mode 100644
index 0000000000..8792f497e4
--- /dev/null
+++ b/src/pkg/runtime/os_netbsd.go
@@ -0,0 +1,22 @@
+// 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"
+
+func setitimer(mode int32, new, old unsafe.Pointer)
+func sigaction(sig int32, new, old unsafe.Pointer)
+func sigaltstack(new, old unsafe.Pointer)
+func sigprocmask(mode int32, new, old unsafe.Pointer)
+func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32
+func lwp_tramp()
+func raise(sig int32)
+func getcontext(ctxt unsafe.Pointer)
+func lwp_create(ctxt unsafe.Pointer, flags uintptr, lwpid unsafe.Pointer) int32
+func lwp_park(abstime unsafe.Pointer, unpark int32, hint, unparkhint unsafe.Pointer) int32
+func lwp_unpark(lwp int32, hint unsafe.Pointer) int32
+func lwp_self() int32
+
+const stackSystem = 0
diff --git a/src/pkg/runtime/os_netbsd.h b/src/pkg/runtime/os_netbsd.h
index 16e9833af5..f95db325f0 100644
--- a/src/pkg/runtime/os_netbsd.h
+++ b/src/pkg/runtime/os_netbsd.h
@@ -2,11 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#define SS_DISABLE 4
-
-#define SIG_BLOCK 1
-#define SIG_UNBLOCK 2
-#define SIG_SETMASK 3
typedef uintptr kevent_udata;
@@ -16,15 +11,21 @@ void runtime·sigpanic(void);
void runtime·setitimer(int32, Itimerval*, Itimerval*);
void runtime·sigaction(int32, struct sigaction*, struct sigaction*);
-void runtime·sigaltstack(Sigaltstack*, Sigaltstack*);
+void runtime·sigaltstack(SigaltstackT*, SigaltstackT*);
void runtime·sigprocmask(int32, Sigset*, Sigset*);
void runtime·unblocksignals(void);
int32 runtime·sysctl(uint32*, uint32, byte*, uintptr*, byte*, uintptr);
extern void runtime·lwp_tramp(void);
-#define NSIG 33
-#define SI_USER 0
-
-// From NetBSD's <sys/ucontext.h>
-#define _UC_SIGMASK 0x01
-#define _UC_CPU 0x04
+enum {
+ 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/pkg/runtime/os_netbsd_arm.c b/src/pkg/runtime/os_netbsd_arm.c
index e440e7def3..9dd4bcdc9c 100644
--- a/src/pkg/runtime/os_netbsd_arm.c
+++ b/src/pkg/runtime/os_netbsd_arm.c
@@ -6,7 +6,7 @@
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
#include "signal_GOOS_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
void
runtime·lwp_mcontext_init(McontextT *mc, void *stack, M *mp, G *gp, void (*fn)(void))
diff --git a/src/pkg/runtime/os_openbsd.c b/src/pkg/runtime/os_openbsd.c
index 220091535d..59abc97b7c 100644
--- a/src/pkg/runtime/os_openbsd.c
+++ b/src/pkg/runtime/os_openbsd.c
@@ -7,7 +7,7 @@
#include "os_GOOS.h"
#include "signal_unix.h"
#include "stack.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
enum
{
@@ -26,7 +26,7 @@ extern SigTab runtime·sigtab[];
static Sigset sigset_none;
static Sigset sigset_all = ~(Sigset)0;
-extern int64 runtime·tfork(void *param, uintptr psize, M *mp, G *gp, void (*fn)(void));
+extern int32 runtime·tfork(TforkT *param, uintptr psize, M *mp, G *gp, void (*fn)(void));
extern int32 runtime·thrsleep(void *ident, int32 clock_id, void *tsp, void *lock, const int32 *abort);
extern int32 runtime·thrwakeup(void *ident, int32 n);
@@ -54,6 +54,7 @@ getncpu(void)
return 1;
}
+#pragma textflag NOSPLIT
uintptr
runtime·semacreate(void)
{
@@ -111,26 +112,59 @@ runtime·semasleep(int64 ns)
return -1;
}
+static void badsemawakeup(void);
+
+#pragma textflag NOSPLIT
void
runtime·semawakeup(M *mp)
{
uint32 ret;
+ void *oldptr;
+ uint32 oldscalar;
+ void (*fn)(void);
// spin-mutex lock
while(runtime·xchg(&mp->waitsemalock, 1))
runtime·osyield();
mp->waitsemacount++;
ret = runtime·thrwakeup(&mp->waitsemacount, 1);
- if(ret != 0 && ret != ESRCH)
- runtime·printf("thrwakeup addr=%p sem=%d ret=%d\n", &mp->waitsemacount, mp->waitsemacount, ret);
+ if(ret != 0 && ret != ESRCH) {
+ // semawakeup can be called on signal stack.
+ // Save old ptrarg/scalararg so we can restore them.
+ oldptr = g->m->ptrarg[0];
+ oldscalar = g->m->scalararg[0];
+ g->m->ptrarg[0] = mp;
+ g->m->scalararg[0] = ret;
+ fn = badsemawakeup;
+ if(g == g->m->gsignal)
+ fn();
+ else
+ runtime·onM(&fn);
+ g->m->ptrarg[0] = oldptr;
+ g->m->scalararg[0] = oldscalar;
+ }
// spin-mutex unlock
runtime·atomicstore(&mp->waitsemalock, 0);
}
+static void
+badsemawakeup(void)
+{
+ M *mp;
+ int32 ret;
+
+ mp = g->m->ptrarg[0];
+ g->m->ptrarg[0] = nil;
+ ret = g->m->scalararg[0];
+ g->m->scalararg[0] = 0;
+
+ runtime·printf("thrwakeup addr=%p sem=%d ret=%d\n", &mp->waitsemacount, mp->waitsemacount, ret);
+}
+
void
runtime·newosproc(M *mp, void *stk)
{
- Tfork param;
+ TforkT param;
Sigset oset;
int32 ret;
@@ -147,7 +181,7 @@ runtime·newosproc(M *mp, void *stk)
param.tf_stack = stk;
oset = runtime·sigprocmask(SIG_SETMASK, sigset_all);
- ret = runtime·tfork((byte*)&param, sizeof(param), mp, mp->g0, runtime·mstart);
+ ret = runtime·tfork(&param, sizeof(param), mp, mp->g0, runtime·mstart);
runtime·sigprocmask(SIG_SETMASK, oset);
if(ret < 0) {
@@ -164,6 +198,7 @@ runtime·osinit(void)
runtime·ncpu = getncpu();
}
+#pragma textflag NOSPLIT
void
runtime·get_random_data(byte **rnd, int32 *rnd_len)
{
@@ -263,12 +298,12 @@ typedef struct sigaction {
} __sigaction_u; /* signal handler */
uint32 sa_mask; /* signal mask to apply */
int32 sa_flags; /* see signal options below */
-} Sigaction;
+} SigactionT;
void
runtime·setsig(int32 i, GoSighandler *fn, bool restart)
{
- Sigaction sa;
+ SigactionT sa;
runtime·memclr((byte*)&sa, sizeof sa);
sa.sa_flags = SA_SIGINFO|SA_ONSTACK;
@@ -284,7 +319,7 @@ runtime·setsig(int32 i, GoSighandler *fn, bool restart)
GoSighandler*
runtime·getsig(int32 i)
{
- Sigaction sa;
+ SigactionT sa;
runtime·memclr((byte*)&sa, sizeof sa);
runtime·sigaction(i, nil, &sa);
diff --git a/src/pkg/runtime/os_openbsd.go b/src/pkg/runtime/os_openbsd.go
new file mode 100644
index 0000000000..19e2b45a15
--- /dev/null
+++ b/src/pkg/runtime/os_openbsd.go
@@ -0,0 +1,19 @@
+// 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"
+
+func setitimer(mode int32, new, old unsafe.Pointer)
+func sigaction(sig int32, new, old unsafe.Pointer)
+func sigaltstack(new, old unsafe.Pointer)
+func sigprocmask(mode int32, new uint32) uint32
+func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32
+func raise(sig int32)
+func tfork(param unsafe.Pointer, psize uintptr, mm, gg, fn unsafe.Pointer) int32
+func thrsleep(ident unsafe.Pointer, clock_id int32, tsp, lock, abort unsafe.Pointer) int32
+func thrwakeup(ident unsafe.Pointer, n int32) int32
+
+const stackSystem = 0
diff --git a/src/pkg/runtime/os_openbsd.h b/src/pkg/runtime/os_openbsd.h
index bbfde39e26..6ad98109e9 100644
--- a/src/pkg/runtime/os_openbsd.h
+++ b/src/pkg/runtime/os_openbsd.h
@@ -2,11 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#define SS_DISABLE 4
-
-#define SIG_BLOCK 1
-#define SIG_UNBLOCK 2
-#define SIG_SETMASK 3
typedef byte* kevent_udata;
@@ -16,10 +11,16 @@ void runtime·sigpanic(void);
void runtime·setitimer(int32, Itimerval*, Itimerval*);
void runtime·sigaction(int32, struct sigaction*, struct sigaction*);
-void runtime·sigaltstack(Sigaltstack*, Sigaltstack*);
+void runtime·sigaltstack(SigaltstackT*, SigaltstackT*);
Sigset runtime·sigprocmask(int32, Sigset);
void runtime·unblocksignals(void);
int32 runtime·sysctl(uint32*, uint32, byte*, uintptr*, byte*, uintptr);
-#define NSIG 33
-#define SI_USER 0
+enum {
+ SS_DISABLE = 4,
+ SIG_BLOCK = 1,
+ SIG_UNBLOCK = 2,
+ SIG_SETMASK = 3,
+ NSIG = 33,
+ SI_USER = 0,
+};
diff --git a/src/pkg/runtime/os_plan9.c b/src/pkg/runtime/os_plan9.c
index 02723fd9e4..853f3ef7a1 100644
--- a/src/pkg/runtime/os_plan9.c
+++ b/src/pkg/runtime/os_plan9.c
@@ -5,7 +5,7 @@
#include "runtime.h"
#include "os_GOOS.h"
#include "arch_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
int8 *goos = "plan9";
extern SigTab runtime·sigtab[];
@@ -20,11 +20,11 @@ runtime·mpreinit(M *mp)
// Initialize stack and goroutine for note handling.
mp->gsignal = runtime·malg(32*1024);
mp->gsignal->m = mp;
- mp->notesig = (int8*)runtime·malloc(ERRMAX*sizeof(int8));
+ mp->notesig = (int8*)runtime·mallocgc(ERRMAX*sizeof(int8), nil, 0);
// Initialize stack for handling strings from the
// errstr system call, as used in package syscall.
- mp->errstr = (byte*)runtime·malloc(ERRMAX*sizeof(byte));
+ mp->errstr = (byte*)runtime·mallocgc(ERRMAX*sizeof(byte), nil, 0);
}
// Called to initialize a new m (including the bootstrap m).
@@ -100,6 +100,7 @@ runtime·crash(void)
*(int32*)0 = 0;
}
+#pragma textflag NOSPLIT
void
runtime·get_random_data(byte **rnd, int32 *rnd_len)
{
@@ -159,18 +160,7 @@ runtime·nanotime(void)
return ns;
}
-void
-time·now(int64 sec, int32 nsec)
-{
- int64 ns;
-
- ns = runtime·nanotime();
- sec = ns / 1000000000LL;
- nsec = ns - sec * 1000000000LL;
- FLUSH(&sec);
- FLUSH(&nsec);
-}
-
+#pragma textflag NOSPLIT
void
runtime·itoa(int32 n, byte *p, uint32 len)
{
@@ -251,12 +241,29 @@ runtime·postnote(int32 pid, int8* msg)
return 0;
}
+static void exit(void);
+
+#pragma textflag NOSPLIT
void
runtime·exit(int32 e)
{
+ void (*fn)(void);
+
+ g->m->scalararg[0] = e;
+ fn = exit;
+ runtime·onM(&fn);
+}
+
+static void
+exit(void)
+{
+ int32 e;
byte tmp[16];
int8 *status;
+ e = g->m->scalararg[0];
+ g->m->scalararg[0] = 0;
+
if(e == 0)
status = "";
else {
@@ -282,6 +289,7 @@ runtime·newosproc(M *mp, void *stk)
runtime·throw("newosproc: rfork failed");
}
+#pragma textflag NOSPLIT
uintptr
runtime·semacreate(void)
{
@@ -311,18 +319,13 @@ runtime·semasleep(int64 ns)
return 0; // success
}
+#pragma textflag NOSPLIT
void
runtime·semawakeup(M *mp)
{
runtime·plan9_semrelease(&mp->waitsemacount, 1);
}
-void
-os·sigpipe(void)
-{
- runtime·throw("too many writes on closed pipe");
-}
-
static int64
atolwhex(byte *p)
{
@@ -401,12 +404,14 @@ runtime·sigpanic(void)
}
}
+#pragma textflag NOSPLIT
int32
runtime·read(int32 fd, void *buf, int32 nbytes)
{
return runtime·pread(fd, buf, nbytes, -1LL);
}
+#pragma textflag NOSPLIT
int32
runtime·write(uintptr fd, void *buf, int32 nbytes)
{
diff --git a/src/pkg/runtime/os_plan9.go b/src/pkg/runtime/os_plan9.go
new file mode 100644
index 0000000000..c45d22551f
--- /dev/null
+++ b/src/pkg/runtime/os_plan9.go
@@ -0,0 +1,34 @@
+// 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"
+
+func pread(fd int32, buf unsafe.Pointer, nbytes int32, offset int64) int32
+func pwrite(fd int32, buf unsafe.Pointer, nbytes int32, offset int64) int32
+func seek(fd int32, offset int64, whence int32) int64
+func exits(msg *byte)
+func brk_(addr unsafe.Pointer) uintptr
+func sleep(ms int32) int32
+func rfork(flags int32, stk, mm, gg, fn unsafe.Pointer) int32
+func plan9_semacquire(addr *uint32, block int32) int32
+func plan9_tsemacquire(addr *uint32, ms int32) int32
+func plan9_semrelease(addr *uint32, count int32) int32
+func notify(fn unsafe.Pointer) int32
+func noted(mode int32) int32
+func nsec(*int64) int64
+func sigtramp(ureg, msg unsafe.Pointer)
+func setfpmasks()
+func errstr() string
+
+// The size of the note handler frame varies among architectures,
+// but 512 bytes should be enough for every implementation.
+const stackSystem = 512
+
+type _Plink uintptr
+
+func os_sigpipe() {
+ gothrow("too many writes on closed pipe")
+}
diff --git a/src/pkg/runtime/os_plan9.h b/src/pkg/runtime/os_plan9.h
index 8bc57262d9..57a2cafa72 100644
--- a/src/pkg/runtime/os_plan9.h
+++ b/src/pkg/runtime/os_plan9.h
@@ -59,15 +59,15 @@ enum
};
typedef struct Tos Tos;
-typedef intptr Plink;
+typedef intptr _Plink;
struct Tos {
struct /* Per process profiling */
{
- Plink *pp; /* known to be 0(ptr) */
- Plink *next; /* known to be 4(ptr) */
- Plink *last;
- Plink *first;
+ _Plink *pp; /* known to be 0(ptr) */
+ _Plink *next; /* known to be 4(ptr) */
+ _Plink *last;
+ _Plink *first;
uint32 pid;
uint32 what;
} prof;
@@ -79,12 +79,14 @@ struct Tos {
/* top of stack is here */
};
-#define NSIG 14 /* number of signals in runtime·SigTab array */
-#define ERRMAX 128 /* max length of note string */
+enum {
+ NSIG = 14, /* number of signals in runtime·SigTab array */
+ ERRMAX = 128, /* max length of note string */
-/* Notes in runtime·sigtab that are handled by runtime·sigpanic. */
-#define SIGRFAULT 2
-#define SIGWFAULT 3
-#define SIGINTDIV 4
-#define SIGFLOAT 5
-#define SIGTRAP 6
+ /* Notes in runtime·sigtab that are handled by runtime·sigpanic. */
+ SIGRFAULT = 2,
+ SIGWFAULT = 3,
+ SIGINTDIV = 4,
+ SIGFLOAT = 5,
+ SIGTRAP = 6,
+};
diff --git a/src/pkg/runtime/os_solaris.c b/src/pkg/runtime/os_solaris.c
index fe218cdb81..e35d2b9971 100644
--- a/src/pkg/runtime/os_solaris.c
+++ b/src/pkg/runtime/os_solaris.c
@@ -7,11 +7,11 @@
#include "os_GOOS.h"
#include "signal_unix.h"
#include "stack.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
-#pragma dynexport end _end
-#pragma dynexport etext _etext
-#pragma dynexport edata _edata
+#pragma dynexport runtime·end _end
+#pragma dynexport runtime·etext _etext
+#pragma dynexport runtime·edata _edata
#pragma dynimport libc·___errno ___errno "libc.so"
#pragma dynimport libc·clock_gettime clock_gettime "libc.so"
@@ -97,21 +97,6 @@ extern SigTab runtime·sigtab[];
static Sigset sigset_none;
static Sigset sigset_all = { ~(uint32)0, ~(uint32)0, ~(uint32)0, ~(uint32)0, };
-// Calling sysvcall on os stack.
-#pragma textflag NOSPLIT
-uintptr
-runtime·sysvicall6(uintptr fn, int32 count, ...)
-{
- runtime·memclr((byte*)&g->m->scratch, sizeof(g->m->scratch));
- g->m->libcall.fn = (void*)fn;
- g->m->libcall.n = (uintptr)count;
- for(;count; count--)
- g->m->scratch.v[count - 1] = *((uintptr*)&count + count);
- g->m->libcall.args = (uintptr*)&g->m->scratch.v[0];
- runtime·asmcgocall(runtime·asmsysvicall6, &g->m->libcall);
- return g->m->libcall.r1;
-}
-
static int32
getncpu(void)
{
@@ -158,6 +143,7 @@ runtime·newosproc(M *mp, void *stk)
}
}
+#pragma textflag NOSPLIT
void
runtime·get_random_data(byte **rnd, int32 *rnd_len)
{
@@ -247,7 +233,7 @@ uintptr
runtime·memlimit(void)
{
Rlimit rl;
- extern byte text[], end[];
+ extern byte runtime·text[], runtime·end[];
uintptr used;
if(runtime·getrlimit(RLIMIT_AS, &rl) != 0)
@@ -258,7 +244,7 @@ runtime·memlimit(void)
// Estimate our VM footprint excluding the heap.
// Not an exact science: use size of binary plus
// some room for thread stacks.
- used = end - text + (64<<20);
+ used = runtime·end - runtime·text + (64<<20);
if(used >= rl.rlim_cur)
return 0;
@@ -282,7 +268,7 @@ extern void runtime·sigtramp(void);
void
runtime·setsig(int32 i, GoSighandler *fn, bool restart)
{
- Sigaction sa;
+ SigactionT sa;
runtime·memclr((byte*)&sa, sizeof sa);
sa.sa_flags = SA_SIGINFO|SA_ONSTACK;
@@ -301,7 +287,7 @@ runtime·setsig(int32 i, GoSighandler *fn, bool restart)
GoSighandler*
runtime·getsig(int32 i)
{
- Sigaction sa;
+ SigactionT sa;
runtime·memclr((byte*)&sa, sizeof sa);
runtime·sigaction(i, nil, &sa);
@@ -400,40 +386,46 @@ runtime·semawakeup(M *mp)
runtime·throw("sem_post");
}
+#pragma textflag NOSPLIT
int32
runtime·close(int32 fd)
{
- return runtime·sysvicall6(libc·close, 1, (uintptr)fd);
+ return runtime·sysvicall1(libc·close, (uintptr)fd);
}
+#pragma textflag NOSPLIT
void
runtime·exit(int32 r)
{
- runtime·sysvicall6(libc·exit, 1, (uintptr)r);
+ runtime·sysvicall1(libc·exit, (uintptr)r);
}
+#pragma textflag NOSPLIT
/* int32 */ void
runtime·getcontext(Ucontext* context)
{
- runtime·sysvicall6(libc·getcontext, 1, (uintptr)context);
+ runtime·sysvicall1(libc·getcontext, (uintptr)context);
}
+#pragma textflag NOSPLIT
int32
runtime·getrlimit(int32 res, Rlimit* rlp)
{
- return runtime·sysvicall6(libc·getrlimit, 2, (uintptr)res, (uintptr)rlp);
+ return runtime·sysvicall2(libc·getrlimit, (uintptr)res, (uintptr)rlp);
}
+#pragma textflag NOSPLIT
uint8*
runtime·mmap(byte* addr, uintptr len, int32 prot, int32 flags, int32 fildes, uint32 off)
{
- return (uint8*)runtime·sysvicall6(libc·mmap, 6, (uintptr)addr, (uintptr)len, (uintptr)prot, (uintptr)flags, (uintptr)fildes, (uintptr)off);
+ return (uint8*)runtime·sysvicall6(libc·mmap, (uintptr)addr, (uintptr)len, (uintptr)prot, (uintptr)flags, (uintptr)fildes, (uintptr)off);
}
+#pragma textflag NOSPLIT
void
runtime·munmap(byte* addr, uintptr len)
{
- runtime·sysvicall6(libc·munmap, 2, (uintptr)addr, (uintptr)len);
+ runtime·sysvicall2(libc·munmap, (uintptr)addr, (uintptr)len);
}
extern int64 runtime·nanotime1(void);
@@ -441,131 +433,121 @@ extern int64 runtime·nanotime1(void);
int64
runtime·nanotime(void)
{
- return runtime·sysvicall6((uintptr)runtime·nanotime1, 0);
-}
-
-void
-time·now(int64 sec, int32 usec)
-{
- int64 ns;
-
- ns = runtime·nanotime();
- sec = ns / 1000000000LL;
- usec = ns - sec * 1000000000LL;
- FLUSH(&sec);
- FLUSH(&usec);
+ return runtime·sysvicall0((uintptr)runtime·nanotime1);
}
+#pragma textflag NOSPLIT
int32
runtime·open(int8* path, int32 oflag, int32 mode)
{
- return runtime·sysvicall6(libc·open, 3, (uintptr)path, (uintptr)oflag, (uintptr)mode);
+ return runtime·sysvicall3(libc·open, (uintptr)path, (uintptr)oflag, (uintptr)mode);
}
int32
runtime·pthread_attr_destroy(PthreadAttr* attr)
{
- return runtime·sysvicall6(libc·pthread_attr_destroy, 1, (uintptr)attr);
+ return runtime·sysvicall1(libc·pthread_attr_destroy, (uintptr)attr);
}
int32
runtime·pthread_attr_getstack(PthreadAttr* attr, void** addr, uint64* size)
{
- return runtime·sysvicall6(libc·pthread_attr_getstack, 3, (uintptr)attr, (uintptr)addr, (uintptr)size);
+ return runtime·sysvicall3(libc·pthread_attr_getstack, (uintptr)attr, (uintptr)addr, (uintptr)size);
}
int32
runtime·pthread_attr_init(PthreadAttr* attr)
{
- return runtime·sysvicall6(libc·pthread_attr_init, 1, (uintptr)attr);
+ return runtime·sysvicall1(libc·pthread_attr_init, (uintptr)attr);
}
int32
runtime·pthread_attr_setdetachstate(PthreadAttr* attr, int32 state)
{
- return runtime·sysvicall6(libc·pthread_attr_setdetachstate, 2, (uintptr)attr, (uintptr)state);
+ return runtime·sysvicall2(libc·pthread_attr_setdetachstate, (uintptr)attr, (uintptr)state);
}
int32
runtime·pthread_attr_setstack(PthreadAttr* attr, void* addr, uint64 size)
{
- return runtime·sysvicall6(libc·pthread_attr_setstack, 3, (uintptr)attr, (uintptr)addr, (uintptr)size);
+ return runtime·sysvicall3(libc·pthread_attr_setstack, (uintptr)attr, (uintptr)addr, (uintptr)size);
}
int32
runtime·pthread_create(Pthread* thread, PthreadAttr* attr, void(*fn)(void), void *arg)
{
- return runtime·sysvicall6(libc·pthread_create, 4, (uintptr)thread, (uintptr)attr, (uintptr)fn, (uintptr)arg);
+ return runtime·sysvicall4(libc·pthread_create, (uintptr)thread, (uintptr)attr, (uintptr)fn, (uintptr)arg);
}
/* int32 */ void
runtime·raise(int32 sig)
{
- runtime·sysvicall6(libc·raise, 1, (uintptr)sig);
+ runtime·sysvicall1(libc·raise, (uintptr)sig);
}
+#pragma textflag NOSPLIT
int32
runtime·read(int32 fd, void* buf, int32 nbyte)
{
- return runtime·sysvicall6(libc·read, 3, (uintptr)fd, (uintptr)buf, (uintptr)nbyte);
+ return runtime·sysvicall3(libc·read, (uintptr)fd, (uintptr)buf, (uintptr)nbyte);
}
#pragma textflag NOSPLIT
int32
runtime·sem_init(SemT* sem, int32 pshared, uint32 value)
{
- return runtime·sysvicall6(libc·sem_init, 3, (uintptr)sem, (uintptr)pshared, (uintptr)value);
+ return runtime·sysvicall3(libc·sem_init, (uintptr)sem, (uintptr)pshared, (uintptr)value);
}
#pragma textflag NOSPLIT
int32
runtime·sem_post(SemT* sem)
{
- return runtime·sysvicall6(libc·sem_post, 1, (uintptr)sem);
+ return runtime·sysvicall1(libc·sem_post, (uintptr)sem);
}
#pragma textflag NOSPLIT
int32
runtime·sem_reltimedwait_np(SemT* sem, Timespec* timeout)
{
- return runtime·sysvicall6(libc·sem_reltimedwait_np, 2, (uintptr)sem, (uintptr)timeout);
+ return runtime·sysvicall2(libc·sem_reltimedwait_np, (uintptr)sem, (uintptr)timeout);
}
#pragma textflag NOSPLIT
int32
runtime·sem_wait(SemT* sem)
{
- return runtime·sysvicall6(libc·sem_wait, 1, (uintptr)sem);
+ return runtime·sysvicall1(libc·sem_wait, (uintptr)sem);
}
/* int32 */ void
runtime·setitimer(int32 which, Itimerval* value, Itimerval* ovalue)
{
- runtime·sysvicall6(libc·setitimer, 3, (uintptr)which, (uintptr)value, (uintptr)ovalue);
+ runtime·sysvicall3(libc·setitimer, (uintptr)which, (uintptr)value, (uintptr)ovalue);
}
/* int32 */ void
-runtime·sigaction(int32 sig, struct Sigaction* act, struct Sigaction* oact)
+runtime·sigaction(int32 sig, struct SigactionT* act, struct SigactionT* oact)
{
- runtime·sysvicall6(libc·sigaction, 3, (uintptr)sig, (uintptr)act, (uintptr)oact);
+ runtime·sysvicall3(libc·sigaction, (uintptr)sig, (uintptr)act, (uintptr)oact);
}
/* int32 */ void
-runtime·sigaltstack(Sigaltstack* ss, Sigaltstack* oss)
+runtime·sigaltstack(SigaltstackT* ss, SigaltstackT* oss)
{
- runtime·sysvicall6(libc·sigaltstack, 2, (uintptr)ss, (uintptr)oss);
+ runtime·sysvicall2(libc·sigaltstack, (uintptr)ss, (uintptr)oss);
}
/* int32 */ void
runtime·sigprocmask(int32 how, Sigset* set, Sigset* oset)
{
- runtime·sysvicall6(libc·sigprocmask, 3, (uintptr)how, (uintptr)set, (uintptr)oset);
+ runtime·sysvicall3(libc·sigprocmask, (uintptr)how, (uintptr)set, (uintptr)oset);
}
int64
runtime·sysconf(int32 name)
{
- return runtime·sysvicall6(libc·sysconf, 1, (uintptr)name);
+ return runtime·sysvicall1(libc·sysconf, (uintptr)name);
}
extern void runtime·usleep1(uint32);
@@ -577,10 +559,11 @@ runtime·usleep(uint32 µs)
runtime·usleep1(µs);
}
+#pragma textflag NOSPLIT
int32
runtime·write(uintptr fd, void* buf, int32 nbyte)
{
- return runtime·sysvicall6(libc·write, 3, (uintptr)fd, (uintptr)buf, (uintptr)nbyte);
+ return runtime·sysvicall3(libc·write, (uintptr)fd, (uintptr)buf, (uintptr)nbyte);
}
extern void runtime·osyield1(void);
@@ -592,7 +575,7 @@ runtime·osyield(void)
// Check the validity of m because we might be called in cgo callback
// path early enough where there isn't a m available yet.
if(g && g->m != nil) {
- runtime·sysvicall6(libc·sched_yield, 0);
+ runtime·sysvicall0(libc·sched_yield);
return;
}
runtime·osyield1();
diff --git a/src/pkg/runtime/os_solaris.go b/src/pkg/runtime/os_solaris.go
new file mode 100644
index 0000000000..a5e6966781
--- /dev/null
+++ b/src/pkg/runtime/os_solaris.go
@@ -0,0 +1,101 @@
+// 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"
+
+func setitimer(mode int32, new, old unsafe.Pointer)
+func sigaction(sig int32, new, old unsafe.Pointer)
+func sigaltstack(new, old unsafe.Pointer)
+func sigprocmask(mode int32, new, old unsafe.Pointer)
+func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32
+func getrlimit(kind int32, limit unsafe.Pointer)
+func miniterrno(fn unsafe.Pointer)
+func raise(sig int32)
+func getcontext(ctxt unsafe.Pointer)
+func tstart_sysvicall(mm unsafe.Pointer) uint32
+func nanotime1() int64
+func usleep1(usec uint32)
+func osyield1()
+func netpollinit()
+func netpollopen(fd uintptr, pd *pollDesc) int32
+func netpollclose(fd uintptr) int32
+func netpollarm(pd *pollDesc, mode int)
+
+type libcFunc byte
+
+var asmsysvicall6 libcFunc
+
+//go:nosplit
+func sysvicall0(fn *libcFunc) uintptr {
+ libcall := &getg().m.libcall
+ libcall.fn = unsafe.Pointer(fn)
+ libcall.n = 0
+ libcall.args = unsafe.Pointer(fn) // it's unused but must be non-nil, otherwise crashes
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(libcall))
+ return libcall.r1
+}
+
+//go:nosplit
+func sysvicall1(fn *libcFunc, a1 uintptr) uintptr {
+ libcall := &getg().m.libcall
+ libcall.fn = unsafe.Pointer(fn)
+ libcall.n = 1
+ libcall.args = noescape(unsafe.Pointer(&a1))
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(libcall))
+ return libcall.r1
+}
+
+//go:nosplit
+func sysvicall2(fn *libcFunc, a1, a2 uintptr) uintptr {
+ libcall := &getg().m.libcall
+ libcall.fn = unsafe.Pointer(fn)
+ libcall.n = 2
+ libcall.args = noescape(unsafe.Pointer(&a1))
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(libcall))
+ return libcall.r1
+}
+
+//go:nosplit
+func sysvicall3(fn *libcFunc, a1, a2, a3 uintptr) uintptr {
+ libcall := &getg().m.libcall
+ libcall.fn = unsafe.Pointer(fn)
+ libcall.n = 3
+ libcall.args = noescape(unsafe.Pointer(&a1))
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(libcall))
+ return libcall.r1
+}
+
+//go:nosplit
+func sysvicall4(fn *libcFunc, a1, a2, a3, a4 uintptr) uintptr {
+ libcall := &getg().m.libcall
+ libcall.fn = unsafe.Pointer(fn)
+ libcall.n = 4
+ libcall.args = noescape(unsafe.Pointer(&a1))
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(libcall))
+ return libcall.r1
+}
+
+//go:nosplit
+func sysvicall5(fn *libcFunc, a1, a2, a3, a4, a5 uintptr) uintptr {
+ libcall := &getg().m.libcall
+ libcall.fn = unsafe.Pointer(fn)
+ libcall.n = 5
+ libcall.args = noescape(unsafe.Pointer(&a1))
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(libcall))
+ return libcall.r1
+}
+
+//go:nosplit
+func sysvicall6(fn *libcFunc, a1, a2, a3, a4, a5, a6 uintptr) uintptr {
+ libcall := &getg().m.libcall
+ libcall.fn = unsafe.Pointer(fn)
+ libcall.n = 6
+ libcall.args = noescape(unsafe.Pointer(&a1))
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(libcall))
+ return libcall.r1
+}
+
+const stackSystem = 0
diff --git a/src/pkg/runtime/os_solaris.h b/src/pkg/runtime/os_solaris.h
index f3fae5da2a..3d9e1a2406 100644
--- a/src/pkg/runtime/os_solaris.h
+++ b/src/pkg/runtime/os_solaris.h
@@ -2,11 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#define SS_DISABLE 2
-
-#define SIG_BLOCK 1
-#define SIG_UNBLOCK 2
-#define SIG_SETMASK 3
typedef uintptr kevent_udata;
@@ -15,24 +10,30 @@ struct sigaction;
void runtime·sigpanic(void);
void runtime·setitimer(int32, Itimerval*, Itimerval*);
-void runtime·sigaction(int32, struct Sigaction*, struct Sigaction*);
-void runtime·sigaltstack(Sigaltstack*, Sigaltstack*);
+void runtime·sigaction(int32, struct SigactionT*, struct SigactionT*);
+void runtime·sigaltstack(SigaltstackT*, SigaltstackT*);
void runtime·sigprocmask(int32, Sigset*, Sigset*);
void runtime·unblocksignals(void);
int32 runtime·sysctl(uint32*, uint32, byte*, uintptr*, byte*, uintptr);
-#define NSIG 73 /* number of signals in runtime·SigTab array */
-#define SI_USER 0
void runtime·raisesigpipe(void);
void runtime·setsig(int32, void(*)(int32, Siginfo*, void*, G*), bool);
void runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp);
void runtime·sigpanic(void);
-#define _UC_SIGMASK 0x01
-#define _UC_CPU 0x04
+enum {
+ SS_DISABLE = 2,
+ SIG_BLOCK = 1,
+ SIG_UNBLOCK = 2,
+ SIG_SETMASK = 3,
+ NSIG = 73, /* number of signals in runtime·SigTab array */
+ SI_USER = 0,
+ _UC_SIGMASK = 0x01,
+ _UC_CPU = 0x04,
+ RLIMIT_AS = 10,
+};
-#define RLIMIT_AS 10
typedef struct Rlimit Rlimit;
struct Rlimit {
int64 rlim_cur;
@@ -40,12 +41,15 @@ struct Rlimit {
};
int32 runtime·getrlimit(int32, Rlimit*);
-// Call a library function with SysV conventions,
-// and switch to os stack during the call.
-#pragma varargck countpos runtime·sysvicall6 2
-#pragma varargck type runtime·sysvicall6 uintptr
-#pragma varargck type runtime·sysvicall6 int32
+// Call an external library function described by {fn, a0, ..., an}, with
+// SysV conventions, switching to os stack during the call, if necessary.
+uintptr runtime·sysvicall0(uintptr fn);
+uintptr runtime·sysvicall1(uintptr fn, uintptr a1);
+uintptr runtime·sysvicall2(uintptr fn, uintptr a1, uintptr a2);
+uintptr runtime·sysvicall3(uintptr fn, uintptr a1, uintptr a2, uintptr a3);
+uintptr runtime·sysvicall4(uintptr fn, uintptr a1, uintptr a2, uintptr a3, uintptr a4);
+uintptr runtime·sysvicall5(uintptr fn, uintptr a1, uintptr a2, uintptr a3, uintptr a4, uintptr a5);
+uintptr runtime·sysvicall6(uintptr fn, uintptr a1, uintptr a2, uintptr a3, uintptr a4, uintptr a5, uintptr a6);
void runtime·asmsysvicall6(void *c);
-uintptr runtime·sysvicall6(uintptr fn, int32 count, ...);
void runtime·miniterrno(void *fn);
diff --git a/src/pkg/runtime/os_windows.c b/src/pkg/runtime/os_windows.c
index 1dc0780ba9..a4d77f6b75 100644
--- a/src/pkg/runtime/os_windows.c
+++ b/src/pkg/runtime/os_windows.c
@@ -6,7 +6,7 @@
#include "type.h"
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
#pragma dynimport runtime·AddVectoredExceptionHandler AddVectoredExceptionHandler "kernel32.dll"
#pragma dynimport runtime·CloseHandle CloseHandle "kernel32.dll"
@@ -81,7 +81,7 @@ getproccount(void)
{
SystemInfo info;
- runtime·stdcall(runtime·GetSystemInfo, 1, &info);
+ runtime·stdcall1(runtime·GetSystemInfo, (uintptr)&info);
return info.dwNumberOfProcessors;
}
@@ -92,38 +92,39 @@ runtime·osinit(void)
runtime·externalthreadhandlerp = (uintptr)runtime·externalthreadhandler;
- runtime·stdcall(runtime·AddVectoredExceptionHandler, 2, (uintptr)1, (uintptr)runtime·sigtramp);
- runtime·stdcall(runtime·SetConsoleCtrlHandler, 2, runtime·ctrlhandler, (uintptr)1);
- runtime·stdcall(runtime·timeBeginPeriod, 1, (uintptr)1);
+ runtime·stdcall2(runtime·AddVectoredExceptionHandler, 1, (uintptr)runtime·sigtramp);
+ runtime·stdcall2(runtime·SetConsoleCtrlHandler, (uintptr)runtime·ctrlhandler, 1);
+ runtime·stdcall1(runtime·timeBeginPeriod, 1);
runtime·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.
- runtime·stdcall(runtime·SetProcessPriorityBoost, 2, (uintptr)-1, (uintptr)1);
+ runtime·stdcall2(runtime·SetProcessPriorityBoost, -1, 1);
- kernel32 = runtime·stdcall(runtime·LoadLibraryA, 1, "kernel32.dll");
+ kernel32 = runtime·stdcall1(runtime·LoadLibraryA, (uintptr)"kernel32.dll");
if(kernel32 != nil) {
- runtime·GetQueuedCompletionStatusEx = runtime·stdcall(runtime·GetProcAddress, 2, kernel32, "GetQueuedCompletionStatusEx");
+ runtime·GetQueuedCompletionStatusEx = runtime·stdcall2(runtime·GetProcAddress, (uintptr)kernel32, (uintptr)"GetQueuedCompletionStatusEx");
}
}
+#pragma textflag NOSPLIT
void
runtime·get_random_data(byte **rnd, int32 *rnd_len)
{
uintptr handle;
*rnd = nil;
*rnd_len = 0;
- if(runtime·stdcall(runtime·CryptAcquireContextW, 5, &handle, nil, nil,
- (uintptr)1 /* PROV_RSA_FULL */,
- (uintptr)0xf0000000U /* CRYPT_VERIFYCONTEXT */) != 0) {
+ if(runtime·stdcall5(runtime·CryptAcquireContextW, (uintptr)&handle, (uintptr)nil, (uintptr)nil,
+ 1 /* PROV_RSA_FULL */,
+ 0xf0000000U /* CRYPT_VERIFYCONTEXT */) != 0) {
static byte random_data[HashRandomBytes];
- if(runtime·stdcall(runtime·CryptGenRandom, 3, handle, (uintptr)HashRandomBytes, random_data)) {
+ if(runtime·stdcall3(runtime·CryptGenRandom, handle, HashRandomBytes, (uintptr)&random_data[0])) {
*rnd = random_data;
*rnd_len = HashRandomBytes;
}
- runtime·stdcall(runtime·CryptReleaseContext, 2, handle, (uintptr)0);
+ runtime·stdcall2(runtime·CryptReleaseContext, handle, 0);
}
}
@@ -137,13 +138,13 @@ runtime·goenvs(void)
int32 i, n;
uint16 *p;
- env = runtime·stdcall(runtime·GetEnvironmentStringsW, 0);
+ env = runtime·stdcall0(runtime·GetEnvironmentStringsW);
n = 0;
for(p=env; *p; n++)
p += runtime·findnullw(p)+1;
- s = runtime·malloc(n*sizeof s[0]);
+ s = runtime·mallocgc(n*sizeof s[0], nil, 0);
p = env;
for(i=0; i<n; i++) {
@@ -154,15 +155,17 @@ runtime·goenvs(void)
syscall·envs.len = n;
syscall·envs.cap = n;
- runtime·stdcall(runtime·FreeEnvironmentStringsW, 1, env);
+ runtime·stdcall1(runtime·FreeEnvironmentStringsW, (uintptr)env);
}
+#pragma textflag NOSPLIT
void
runtime·exit(int32 code)
{
- runtime·stdcall(runtime·ExitProcess, 1, (uintptr)code);
+ runtime·stdcall1(runtime·ExitProcess, code);
}
+#pragma textflag NOSPLIT
int32
runtime·write(uintptr fd, void *buf, int32 n)
{
@@ -172,17 +175,17 @@ runtime·write(uintptr fd, void *buf, int32 n)
written = 0;
switch(fd) {
case 1:
- handle = runtime·stdcall(runtime·GetStdHandle, 1, (uintptr)-11);
+ handle = runtime·stdcall1(runtime·GetStdHandle, -11);
break;
case 2:
- handle = runtime·stdcall(runtime·GetStdHandle, 1, (uintptr)-12);
+ handle = runtime·stdcall1(runtime·GetStdHandle, -12);
break;
default:
// assume fd is real windows handle.
handle = (void*)fd;
break;
}
- runtime·stdcall(runtime·WriteFile, 5, handle, buf, (uintptr)n, &written, (uintptr)0);
+ runtime·stdcall5(runtime·WriteFile, (uintptr)handle, (uintptr)buf, n, (uintptr)&written, 0);
return written;
}
@@ -200,21 +203,23 @@ runtime·semasleep(int64 ns)
if(ns == 0)
ns = 1;
}
- if(runtime·stdcall(runtime·WaitForSingleObject, 2, g->m->waitsema, (uintptr)ns) != 0)
+ if(runtime·stdcall2(runtime·WaitForSingleObject, (uintptr)g->m->waitsema, ns) != 0)
return -1; // timeout
return 0;
}
+#pragma textflag NOSPLIT
void
runtime·semawakeup(M *mp)
{
- runtime·stdcall(runtime·SetEvent, 1, mp->waitsema);
+ runtime·stdcall1(runtime·SetEvent, mp->waitsema);
}
+#pragma textflag NOSPLIT
uintptr
runtime·semacreate(void)
{
- return (uintptr)runtime·stdcall(runtime·CreateEvent, 4, (uintptr)0, (uintptr)0, (uintptr)0, (uintptr)0);
+ return (uintptr)runtime·stdcall4(runtime·CreateEvent, 0, 0, 0, 0);
}
#define STACK_SIZE_PARAM_IS_A_RESERVATION ((uintptr)0x00010000)
@@ -226,9 +231,9 @@ runtime·newosproc(M *mp, void *stk)
USED(stk);
- thandle = runtime·stdcall(runtime·CreateThread, 6,
- nil, (uintptr)0x20000, runtime·tstart_stdcall, mp,
- STACK_SIZE_PARAM_IS_A_RESERVATION, nil);
+ thandle = runtime·stdcall6(runtime·CreateThread,
+ (uintptr)nil, 0x20000, (uintptr)runtime·tstart_stdcall, (uintptr)mp,
+ STACK_SIZE_PARAM_IS_A_RESERVATION, (uintptr)nil);
if(thandle == nil) {
runtime·printf("runtime: failed to create new OS thread (have %d already; errno=%d)\n", runtime·mcount(), runtime·getlasterror());
runtime·throw("runtime.newosproc");
@@ -251,9 +256,7 @@ runtime·minit(void)
void *thandle;
// -1 = current process, -2 = current thread
- runtime·stdcall(runtime·DuplicateHandle, 7,
- (uintptr)-1, (uintptr)-2, (uintptr)-1, &thandle,
- (uintptr)0, (uintptr)0, (uintptr)DUPLICATE_SAME_ACCESS);
+ runtime·stdcall7(runtime·DuplicateHandle, -1, -2, -1, (uintptr)&thandle, 0, 0, DUPLICATE_SAME_ACCESS);
runtime·atomicstorep(&g->m->thread, thandle);
}
@@ -273,12 +276,15 @@ typedef struct KSYSTEM_TIME {
const KSYSTEM_TIME* INTERRUPT_TIME = (KSYSTEM_TIME*)0x7ffe0008;
const KSYSTEM_TIME* SYSTEM_TIME = (KSYSTEM_TIME*)0x7ffe0014;
+static void badsystime(void);
+
#pragma textflag NOSPLIT
int64
runtime·systime(KSYSTEM_TIME *timeaddr)
{
KSYSTEM_TIME t;
int32 i;
+ void (*fn)(void);
for(i = 1; i < 10000; i++) {
// these fields must be read in that order (see URL above)
@@ -290,40 +296,37 @@ runtime·systime(KSYSTEM_TIME *timeaddr)
if((i%100) == 0)
runtime·osyield();
}
- runtime·throw("interrupt/system time is changing too fast");
+ fn = badsystime;
+ runtime·onM(&fn);
return 0;
}
#pragma textflag NOSPLIT
int64
-runtime·nanotime(void)
+runtime·unixnano(void)
{
- return runtime·systime(INTERRUPT_TIME) * 100LL;
+ return (runtime·systime(SYSTEM_TIME) - 116444736000000000LL) * 100LL;
}
-void
-time·now(int64 sec, int32 usec)
+static void
+badsystime(void)
{
- int64 ns;
-
- // SystemTime is 100s of nanoseconds since January 1, 1601.
- // Convert to nanoseconds since January 1, 1970.
- ns = (runtime·systime(SYSTEM_TIME) - 116444736000000000LL) * 100LL;
+ runtime·throw("interrupt/system time is changing too fast");
+}
- sec = ns / 1000000000LL;
- usec = ns - sec * 1000000000LL;
- FLUSH(&sec);
- FLUSH(&usec);
+#pragma textflag NOSPLIT
+int64
+runtime·nanotime(void)
+{
+ return runtime·systime(INTERRUPT_TIME) * 100LL;
}
// Calling stdcall on os stack.
#pragma textflag NOSPLIT
-void *
-runtime·stdcall(void *fn, int32 count, ...)
+static void*
+stdcall(void *fn)
{
g->m->libcall.fn = fn;
- g->m->libcall.n = count;
- g->m->libcall.args = (uintptr*)&count + 1;
if(g->m->profilehz != 0) {
// leave pc/sp for cpu profiler
g->m->libcallg = g;
@@ -337,6 +340,85 @@ runtime·stdcall(void *fn, int32 count, ...)
return (void*)g->m->libcall.r1;
}
+#pragma textflag NOSPLIT
+void*
+runtime·stdcall0(void *fn)
+{
+ g->m->libcall.n = 0;
+ g->m->libcall.args = &fn; // it's unused but must be non-nil, otherwise crashes
+ return stdcall(fn);
+}
+
+#pragma textflag NOSPLIT
+void*
+runtime·stdcall1(void *fn, uintptr a0)
+{
+ USED(a0);
+ g->m->libcall.n = 1;
+ g->m->libcall.args = &a0;
+ return stdcall(fn);
+}
+
+#pragma textflag NOSPLIT
+void*
+runtime·stdcall2(void *fn, uintptr a0, uintptr a1)
+{
+ USED(a0, a1);
+ g->m->libcall.n = 2;
+ g->m->libcall.args = &a0;
+ return stdcall(fn);
+}
+
+#pragma textflag NOSPLIT
+void*
+runtime·stdcall3(void *fn, uintptr a0, uintptr a1, uintptr a2)
+{
+ USED(a0, a1, a2);
+ g->m->libcall.n = 3;
+ g->m->libcall.args = &a0;
+ return stdcall(fn);
+}
+
+#pragma textflag NOSPLIT
+void*
+runtime·stdcall4(void *fn, uintptr a0, uintptr a1, uintptr a2, uintptr a3)
+{
+ USED(a0, a1, a2, a3);
+ g->m->libcall.n = 4;
+ g->m->libcall.args = &a0;
+ return stdcall(fn);
+}
+
+#pragma textflag NOSPLIT
+void*
+runtime·stdcall5(void *fn, uintptr a0, uintptr a1, uintptr a2, uintptr a3, uintptr a4)
+{
+ USED(a0, a1, a2, a3, a4);
+ g->m->libcall.n = 5;
+ g->m->libcall.args = &a0;
+ return stdcall(fn);
+}
+
+#pragma textflag NOSPLIT
+void*
+runtime·stdcall6(void *fn, uintptr a0, uintptr a1, uintptr a2, uintptr a3, uintptr a4, uintptr a5)
+{
+ USED(a0, a1, a2, a3, a4, a5);
+ g->m->libcall.n = 6;
+ g->m->libcall.args = &a0;
+ return stdcall(fn);
+}
+
+#pragma textflag NOSPLIT
+void*
+runtime·stdcall7(void *fn, uintptr a0, uintptr a1, uintptr a2, uintptr a3, uintptr a4, uintptr a5, uintptr a6)
+{
+ USED(a0, a1, a2, a3, a4, a5, a6);
+ g->m->libcall.n = 7;
+ g->m->libcall.args = &a0;
+ return stdcall(fn);
+}
+
extern void runtime·usleep1(uint32);
#pragma textflag NOSPLIT
@@ -451,7 +533,7 @@ profilem(M *mp)
// align Context to 16 bytes
r = (Context*)((uintptr)(&rbuf[15]) & ~15);
r->ContextFlags = CONTEXT_CONTROL;
- runtime·stdcall(runtime·GetThreadContext, 2, mp->thread, r);
+ runtime·stdcall2(runtime·GetThreadContext, (uintptr)mp->thread, (uintptr)r);
runtime·dosigprof(r, gp, mp);
}
@@ -461,11 +543,10 @@ runtime·profileloop1(void)
M *mp, *allm;
void *thread;
- runtime·stdcall(runtime·SetThreadPriority, 2,
- (uintptr)-2, (uintptr)THREAD_PRIORITY_HIGHEST);
+ runtime·stdcall2(runtime·SetThreadPriority, -2, THREAD_PRIORITY_HIGHEST);
for(;;) {
- runtime·stdcall(runtime·WaitForSingleObject, 2, profiletimer, (uintptr)-1);
+ runtime·stdcall2(runtime·WaitForSingleObject, (uintptr)profiletimer, -1);
allm = runtime·atomicloadp(&runtime·allm);
for(mp = allm; mp != nil; mp = mp->alllink) {
thread = runtime·atomicloadp(&mp->thread);
@@ -474,10 +555,10 @@ runtime·profileloop1(void)
// idle timer thread, idle heap scavenger, etc.
if(thread == nil || mp->profilehz == 0 || mp->blocked)
continue;
- runtime·stdcall(runtime·SuspendThread, 1, thread);
+ runtime·stdcall1(runtime·SuspendThread, (uintptr)thread);
if(mp->profilehz != 0 && !mp->blocked)
profilem(mp);
- runtime·stdcall(runtime·ResumeThread, 1, thread);
+ runtime·stdcall1(runtime·ResumeThread, (uintptr)thread);
}
}
}
@@ -485,18 +566,19 @@ runtime·profileloop1(void)
void
runtime·resetcpuprofiler(int32 hz)
{
- static Lock lock;
+ static Mutex lock;
void *timer, *thread;
int32 ms;
int64 due;
runtime·lock(&lock);
if(profiletimer == nil) {
- timer = runtime·stdcall(runtime·CreateWaitableTimer, 3, nil, nil, nil);
+ timer = runtime·stdcall3(runtime·CreateWaitableTimer, (uintptr)nil, (uintptr)nil, (uintptr)nil);
runtime·atomicstorep(&profiletimer, timer);
- thread = runtime·stdcall(runtime·CreateThread, 6,
- nil, nil, runtime·profileloop, nil, nil, nil);
- runtime·stdcall(runtime·CloseHandle, 1, thread);
+ thread = runtime·stdcall6(runtime·CreateThread,
+ (uintptr)nil, (uintptr)nil, (uintptr)runtime·profileloop, (uintptr)nil, (uintptr)nil, (uintptr)nil);
+ runtime·stdcall2(runtime·SetThreadPriority, (uintptr)thread, THREAD_PRIORITY_HIGHEST);
+ runtime·stdcall1(runtime·CloseHandle, (uintptr)thread);
}
runtime·unlock(&lock);
@@ -508,17 +590,11 @@ runtime·resetcpuprofiler(int32 hz)
ms = 1;
due = ms * -10000;
}
- runtime·stdcall(runtime·SetWaitableTimer, 6,
- profiletimer, &due, (uintptr)ms, nil, nil, nil);
+ runtime·stdcall6(runtime·SetWaitableTimer,
+ (uintptr)profiletimer, (uintptr)&due, ms, (uintptr)nil, (uintptr)nil, (uintptr)nil);
runtime·atomicstore((uint32*)&g->m->profilehz, hz);
}
-void
-os·sigpipe(void)
-{
- runtime·throw("too many writes on closed pipe");
-}
-
uintptr
runtime·memlimit(void)
{
diff --git a/src/pkg/runtime/os_windows.go b/src/pkg/runtime/os_windows.go
new file mode 100644
index 0000000000..6a3bfca41f
--- /dev/null
+++ b/src/pkg/runtime/os_windows.go
@@ -0,0 +1,33 @@
+// 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"
+
+type stdFunction *byte
+
+func stdcall0(fn stdFunction) uintptr
+func stdcall1(fn stdFunction, a0 uintptr) uintptr
+func stdcall2(fn stdFunction, a0, a1 uintptr) uintptr
+func stdcall3(fn stdFunction, a0, a1, a2 uintptr) uintptr
+func stdcall4(fn stdFunction, a0, a1, a2, a3 uintptr) uintptr
+func stdcall5(fn stdFunction, a0, a1, a2, a3, a4 uintptr) uintptr
+func stdcall6(fn stdFunction, a0, a1, a2, a3, a4, a5 uintptr) uintptr
+func stdcall7(fn stdFunction, a0, a1, a2, a3, a4, a5, a6 uintptr) uintptr
+
+func asmstdcall(fn unsafe.Pointer)
+func getlasterror() uint32
+func setlasterror(err uint32)
+func usleep1(usec uint32)
+func netpollinit()
+func netpollopen(fd uintptr, pd *pollDesc) int32
+func netpollclose(fd uintptr) int32
+func netpollarm(pd *pollDesc, mode int)
+
+const stackSystem = 512 * ptrSize
+
+func os_sigpipe() {
+ gothrow("too many writes on closed pipe")
+}
diff --git a/src/pkg/runtime/os_windows.h b/src/pkg/runtime/os_windows.h
index b64fa88736..d5d168d77b 100644
--- a/src/pkg/runtime/os_windows.h
+++ b/src/pkg/runtime/os_windows.h
@@ -8,11 +8,15 @@ extern void *runtime·GetQueuedCompletionStatusEx;
// Call a Windows function with stdcall conventions,
// and switch to os stack during the call.
-#pragma varargck countpos runtime·stdcall 2
-#pragma varargck type runtime·stdcall void*
-#pragma varargck type runtime·stdcall uintptr
void runtime·asmstdcall(void *c);
-void *runtime·stdcall(void *fn, int32 count, ...);
+void *runtime·stdcall0(void *fn);
+void *runtime·stdcall1(void *fn, uintptr a0);
+void *runtime·stdcall2(void *fn, uintptr a0, uintptr a1);
+void *runtime·stdcall3(void *fn, uintptr a0, uintptr a1, uintptr a2);
+void *runtime·stdcall4(void *fn, uintptr a0, uintptr a1, uintptr a2, uintptr a3);
+void *runtime·stdcall5(void *fn, uintptr a0, uintptr a1, uintptr a2, uintptr a3, uintptr a4);
+void *runtime·stdcall6(void *fn, uintptr a0, uintptr a1, uintptr a2, uintptr a3, uintptr a4, uintptr a5);
+void *runtime·stdcall7(void *fn, uintptr a0, uintptr a1, uintptr a2, uintptr a3, uintptr a4, uintptr a5, uintptr a6);
uint32 runtime·getlasterror(void);
void runtime·setlasterror(uint32 err);
@@ -33,4 +37,6 @@ void runtime·install_exception_handler(void);
void runtime·remove_exception_handler(void);
// TODO(brainman): should not need those
-#define NSIG 65
+enum {
+ NSIG = 65,
+};
diff --git a/src/pkg/runtime/os_windows_386.c b/src/pkg/runtime/os_windows_386.c
index 02bc81adbf..15a5ea5d1f 100644
--- a/src/pkg/runtime/os_windows_386.c
+++ b/src/pkg/runtime/os_windows_386.c
@@ -34,7 +34,7 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
{
bool crash;
uintptr *sp;
- extern byte text[], etext[];
+ extern byte runtime·text[], runtime·etext[];
if(info->ExceptionCode == DBG_PRINTEXCEPTION_C) {
// This exception is intended to be caught by debuggers.
@@ -51,7 +51,7 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
// Only handle exception if executing instructions in Go binary
// (not Windows library code).
- if(r->Eip < (uint32)text || (uint32)etext < r->Eip)
+ if(r->Eip < (uint32)runtime·text || (uint32)runtime·etext < r->Eip)
return 0;
switch(info->ExceptionCode) {
diff --git a/src/pkg/runtime/os_windows_386.go b/src/pkg/runtime/os_windows_386.go
new file mode 100644
index 0000000000..86a1906c0c
--- /dev/null
+++ b/src/pkg/runtime/os_windows_386.go
@@ -0,0 +1,11 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime
+
+// contextPC returns the EIP (program counter) register from the context.
+func contextPC(r *context) uintptr { return uintptr(r.eip) }
+
+// contextSP returns the ESP (stack pointer) register from the context.
+func contextSP(r *context) uintptr { return uintptr(r.esp) }
diff --git a/src/pkg/runtime/os_windows_amd64.c b/src/pkg/runtime/os_windows_amd64.c
index a7acf1d786..9a69d73c07 100644
--- a/src/pkg/runtime/os_windows_amd64.c
+++ b/src/pkg/runtime/os_windows_amd64.c
@@ -42,7 +42,7 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
{
bool crash;
uintptr *sp;
- extern byte text[], etext[];
+ extern byte runtime·text[], runtime·etext[];
if(info->ExceptionCode == DBG_PRINTEXCEPTION_C) {
// This exception is intended to be caught by debuggers.
@@ -59,7 +59,7 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
// Only handle exception if executing instructions in Go binary
// (not Windows library code).
- if(r->Rip < (uint64)text || (uint64)etext < r->Rip)
+ if(r->Rip < (uint64)runtime·text || (uint64)runtime·etext < r->Rip)
return 0;
switch(info->ExceptionCode) {
diff --git a/src/pkg/runtime/os_windows_amd64.go b/src/pkg/runtime/os_windows_amd64.go
new file mode 100644
index 0000000000..3f4d4d07cb
--- /dev/null
+++ b/src/pkg/runtime/os_windows_amd64.go
@@ -0,0 +1,11 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime
+
+// contextPC returns the RIP (program counter) register from the context.
+func contextPC(r *context) uintptr { return uintptr(r.rip) }
+
+// contextSP returns the RSP (stack pointer) register from the context.
+func contextSP(r *context) uintptr { return uintptr(r.rsp) }
diff --git a/src/pkg/runtime/panic.c b/src/pkg/runtime/panic.c
index 748c4174bb..50a7d36474 100644
--- a/src/pkg/runtime/panic.c
+++ b/src/pkg/runtime/panic.c
@@ -6,289 +6,43 @@
#include "arch_GOARCH.h"
#include "stack.h"
#include "malloc.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// Code related to defer, panic and recover.
-uint32 runtime·panicking;
-static Lock paniclk;
-
-// Each P holds pool for defers with arg sizes 8, 24, 40, 56 and 72 bytes.
-// Memory block is 40 (24 for 32 bits) bytes larger due to Defer header.
-// This maps exactly to malloc size classes.
-
-// defer size class for arg size sz
-#define DEFERCLASS(sz) (((sz)+7)>>4)
-// total size of memory block for defer with arg size sz
-#define TOTALSIZE(sz) (sizeof(Defer) - sizeof(((Defer*)nil)->args) + ROUND(sz, sizeof(uintptr)))
-
-// Allocate a Defer, usually using per-P pool.
-// Each defer must be released with freedefer.
-static Defer*
-newdefer(int32 siz)
-{
- int32 total, sc;
- Defer *d;
- P *p;
-
- d = nil;
- sc = DEFERCLASS(siz);
- if(sc < nelem(p->deferpool)) {
- p = g->m->p;
- d = p->deferpool[sc];
- if(d)
- p->deferpool[sc] = d->link;
- }
- if(d == nil) {
- // deferpool is empty or just a big defer
- total = runtime·roundupsize(TOTALSIZE(siz));
- d = runtime·malloc(total);
- }
- d->siz = siz;
- d->special = 0;
- d->link = g->defer;
- g->defer = d;
- return d;
-}
-
-// Free the given defer.
-// The defer cannot be used after this call.
-static void
-freedefer(Defer *d)
-{
- int32 sc;
- P *p;
-
- if(d->special)
- return;
- sc = DEFERCLASS(d->siz);
- if(sc < nelem(p->deferpool)) {
- p = g->m->p;
- d->link = p->deferpool[sc];
- p->deferpool[sc] = d;
- // No need to wipe out pointers in argp/pc/fn/args,
- // because we empty the pool before GC.
- }
-}
-
-// Create a new deferred function fn with siz bytes of arguments.
-// The compiler turns a defer statement into a call to this.
-// Cannot split the stack because it assumes that the arguments
-// are available sequentially after &fn; they would not be
-// copied if a stack split occurred. It's OK for this to call
-// functions that split the stack.
-#pragma textflag NOSPLIT
-uintptr
-runtime·deferproc(int32 siz, FuncVal *fn, ...)
-{
- Defer *d;
-
- d = newdefer(siz);
- d->fn = fn;
- d->pc = runtime·getcallerpc(&siz);
- if(thechar == '5' || thechar == '9')
- d->argp = (byte*)(&fn+2); // skip caller's saved link register
- else
- d->argp = (byte*)(&fn+1);
- runtime·memmove(d->args, d->argp, d->siz);
+// TODO: remove once code is moved to Go
+extern Defer* runtime·newdefer(int32 siz);
+extern runtime·freedefer(Defer *d);
- // deferproc returns 0 normally.
- // a deferred func that stops a panic
- // makes the deferproc return 1.
- // the code the compiler generates always
- // checks the return value and jumps to the
- // end of the function if deferproc returns != 0.
- return 0;
-}
-
-// Run a deferred function if there is one.
-// The compiler inserts a call to this at the end of any
-// function which calls defer.
-// If there is a deferred function, this will call runtime·jmpdefer,
-// which will jump to the deferred function such that it appears
-// to have been called by the caller of deferreturn at the point
-// just before deferreturn was called. The effect is that deferreturn
-// is called again and again until there are no more deferred functions.
-// Cannot split the stack because we reuse the caller's frame to
-// call the deferred function.
+uint32 runtime·panicking;
+static Mutex paniclk;
-// The single argument isn't actually used - it just has its address
-// taken so it can be matched against pending defers.
-#pragma textflag NOSPLIT
void
-runtime·deferreturn(uintptr arg0)
-{
- Defer *d;
- byte *argp;
+runtime·deferproc_m(void) {
+ int32 siz;
FuncVal *fn;
-
- d = g->defer;
- if(d == nil)
- return;
- argp = (byte*)&arg0;
- if(d->argp != argp)
- return;
-
- // Moving arguments around.
- // Do not allow preemption here, because the garbage collector
- // won't know the form of the arguments until the jmpdefer can
- // flip the PC over to fn.
- g->m->locks++;
- runtime·memmove(argp, d->args, d->siz);
- fn = d->fn;
- g->defer = d->link;
- freedefer(d);
- g->m->locks--;
- if(g->m->locks == 0 && g->preempt)
- g->stackguard0 = StackPreempt;
- runtime·jmpdefer(fn, argp);
-}
-
-// Ensure that defer arg sizes that map to the same defer size class
-// also map to the same malloc size class.
-void
-runtime·testdefersizes(void)
-{
- P *p;
- int32 i, siz, defersc, mallocsc;
- int32 map[nelem(p->deferpool)];
-
- for(i=0; i<nelem(p->deferpool); i++)
- map[i] = -1;
- for(i=0;; i++) {
- defersc = DEFERCLASS(i);
- if(defersc >= nelem(p->deferpool))
- break;
- siz = TOTALSIZE(i);
- mallocsc = runtime·SizeToClass(siz);
- siz = runtime·class_to_size[mallocsc];
- // runtime·printf("defer class %d: arg size %d, block size %d(%d)\n", defersc, i, siz, mallocsc);
- if(map[defersc] < 0) {
- map[defersc] = mallocsc;
- continue;
- }
- if(map[defersc] != mallocsc) {
- runtime·printf("bad defer size class: i=%d siz=%d mallocsc=%d/%d\n",
- i, siz, map[defersc], mallocsc);
- runtime·throw("bad defer size class");
- }
- }
-}
-
-// Run all deferred functions for the current goroutine.
-static void
-rundefer(void)
-{
+ uintptr argp;
+ uintptr callerpc;
Defer *d;
- while((d = g->defer) != nil) {
- g->defer = d->link;
- reflect·call(d->fn, (byte*)d->args, d->siz, d->siz);
- freedefer(d);
- }
-}
-
-// Print all currently active panics. Used when crashing.
-static void
-printpanics(Panic *p)
-{
- if(p->link) {
- printpanics(p->link);
- runtime·printf("\t");
- }
- runtime·printf("panic: ");
- runtime·printany(p->arg);
- if(p->recovered)
- runtime·printf(" [recovered]");
- runtime·printf("\n");
-}
-
-static void recovery(G*);
-static void abortpanic(Panic*);
-static FuncVal abortpanicV = { (void(*)(void))abortpanic };
-
-// The implementation of the predeclared function panic.
-void
-runtime·panic(Eface e)
-{
- Defer *d, dabort;
- Panic p;
- void *pc, *argp;
-
- runtime·memclr((byte*)&p, sizeof p);
- p.arg = e;
- p.link = g->panic;
- p.stackbase = g->stackbase;
- g->panic = &p;
-
- dabort.fn = &abortpanicV;
- dabort.siz = sizeof(&p);
- dabort.args[0] = &p;
- dabort.argp = NoArgs;
- dabort.special = true;
-
- for(;;) {
- d = g->defer;
- if(d == nil)
- break;
- // take defer off list in case of recursive panic
- g->defer = d->link;
- g->ispanic = true; // rock for runtime·newstack, where runtime·newstackcall ends up
- argp = d->argp;
- pc = d->pc;
-
- // The deferred function may cause another panic,
- // so newstackcall may not return. Set up a defer
- // to mark this panic aborted if that happens.
- dabort.link = g->defer;
- g->defer = &dabort;
- p.defer = d;
-
- runtime·newstackcall(d->fn, (byte*)d->args, d->siz);
+ siz = g->m->scalararg[0];
+ fn = g->m->ptrarg[0];
+ argp = g->m->scalararg[1];
+ callerpc = g->m->scalararg[2];
+ g->m->ptrarg[0] = nil;
- // Newstackcall did not panic. Remove dabort.
- if(g->defer != &dabort)
- runtime·throw("bad defer entry in panic");
- g->defer = dabort.link;
-
- freedefer(d);
- if(p.recovered) {
- g->panic = p.link;
- // Aborted panics are marked but remain on the g->panic list.
- // Recovery will unwind the stack frames containing their Panic structs.
- // Remove them from the list and free the associated defers.
- while(g->panic && g->panic->aborted) {
- freedefer(g->panic->defer);
- g->panic = g->panic->link;
- }
- if(g->panic == nil) // must be done with signal
- g->sig = 0;
- // Pass information about recovering frame to recovery.
- g->sigcode0 = (uintptr)argp;
- g->sigcode1 = (uintptr)pc;
- runtime·mcall(recovery);
- runtime·throw("recovery failed"); // mcall should not return
- }
- }
-
- // ran out of deferred calls - old-school panic now
- runtime·startpanic();
- printpanics(g->panic);
- runtime·dopanic(0); // should not return
- runtime·exit(1); // not reached
-}
-
-static void
-abortpanic(Panic *p)
-{
- p->aborted = true;
+ d = runtime·newdefer(siz);
+ d->fn = fn;
+ d->pc = callerpc;
+ d->argp = argp;
+ runtime·memmove(d->args, (void*)argp, siz);
}
// Unwind the stack after a deferred function calls recover
// after a panic. Then arrange to continue running as though
// the caller of the deferred function returned normally.
-static void
-recovery(G *gp)
+void
+runtime·recovery_m(G *gp)
{
void *argp;
uintptr pc;
@@ -347,40 +101,8 @@ runtime·unwindstack(G *gp, byte *sp)
}
}
-// The implementation of the predeclared function recover.
-// Cannot split the stack because it needs to reliably
-// find the stack segment of its caller.
-#pragma textflag NOSPLIT
-void
-runtime·recover(byte *argp, GoOutput retbase, ...)
-{
- Panic *p;
- Stktop *top;
- Eface *ret;
-
- // Must be an unrecovered panic in progress.
- // Must be on a stack segment created for a deferred call during a panic.
- // Must be at the top of that segment, meaning the deferred call itself
- // and not something it called. The top frame in the segment will have
- // argument pointer argp == top - top->argsize.
- // The subtraction of g->panicwrap allows wrapper functions that
- // do not count as official calls to adjust what we consider the top frame
- // while they are active on the stack. The linker emits adjustments of
- // g->panicwrap in the prologue and epilogue of functions marked as wrappers.
- ret = (Eface*)&retbase;
- top = (Stktop*)g->stackbase;
- p = g->panic;
- if(p != nil && !p->recovered && top->panic && argp == (byte*)top - top->argsize - g->panicwrap) {
- p->recovered = 1;
- *ret = p->arg;
- } else {
- ret->type = nil;
- ret->data = nil;
- }
-}
-
void
-runtime·startpanic(void)
+runtime·startpanic_m(void)
{
if(runtime·mheap.cachealloc.size == 0) { // very early
runtime·printf("runtime: panic before malloc heap initialized\n");
@@ -390,8 +112,11 @@ runtime·startpanic(void)
switch(g->m->dying) {
case 0:
g->m->dying = 1;
- if(g != nil)
- g->writebuf = nil;
+ if(g != nil) {
+ g->writebuf.array = nil;
+ g->writebuf.len = 0;
+ g->writebuf.cap = 0;
+ }
runtime·xadd(&runtime·panicking, 1);
runtime·lock(&paniclk);
if(runtime·debug.schedtrace > 0 || runtime·debug.scheddetail > 0)
@@ -418,28 +143,34 @@ runtime·startpanic(void)
}
void
-runtime·dopanic(int32 unused)
+runtime·dopanic_m(void)
{
+ G *gp;
+ uintptr sp, pc;
static bool didothers;
bool crash;
int32 t;
- if(g->sig != 0)
+ gp = g->m->ptrarg[0];
+ g->m->ptrarg[0] = nil;
+ pc = g->m->scalararg[0];
+ sp = g->m->scalararg[1];
+ if(gp->sig != 0)
runtime·printf("[signal %x code=%p addr=%p pc=%p]\n",
- g->sig, g->sigcode0, g->sigcode1, g->sigpc);
+ gp->sig, gp->sigcode0, gp->sigcode1, gp->sigpc);
if((t = runtime·gotraceback(&crash)) > 0){
- if(g != g->m->g0) {
+ if(gp != gp->m->g0) {
runtime·printf("\n");
- runtime·goroutineheader(g);
- runtime·traceback((uintptr)runtime·getcallerpc(&unused), (uintptr)runtime·getcallersp(&unused), 0, g);
+ runtime·goroutineheader(gp);
+ runtime·traceback(pc, sp, 0, gp);
} else if(t >= 2 || g->m->throwing > 0) {
runtime·printf("\nruntime stack:\n");
- runtime·traceback((uintptr)runtime·getcallerpc(&unused), (uintptr)runtime·getcallersp(&unused), 0, g);
+ runtime·traceback(pc, sp, 0, gp);
}
if(!didothers) {
didothers = true;
- runtime·tracebackothers(g);
+ runtime·tracebackothers(gp);
}
}
runtime·unlock(&paniclk);
@@ -448,7 +179,7 @@ runtime·dopanic(int32 unused)
// Let it print what it needs to print.
// Wait forever without chewing up cpu.
// It will exit when it's done.
- static Lock deadlock;
+ static Mutex deadlock;
runtime·lock(&deadlock);
runtime·lock(&deadlock);
}
@@ -459,36 +190,11 @@ runtime·dopanic(int32 unused)
runtime·exit(2);
}
-void
-runtime·panicindex(void)
-{
- runtime·panicstring("index out of range");
-}
-
-void
-runtime·panicslice(void)
-{
- runtime·panicstring("slice bounds out of range");
-}
-
-void
-runtime·throwreturn(void)
-{
- // can only happen if compiler is broken
- runtime·throw("no return at end of a typed function - compiler is broken");
-}
-
-void
-runtime·throwinit(void)
-{
- // can only happen with linker skew
- runtime·throw("recursive call during initialization - linker skew");
-}
-
bool
runtime·canpanic(G *gp)
{
M *m;
+ uint32 status;
// Note that g is m->gsignal, different from gp.
// Note also that g->m can change at preemption, so m can go stale
@@ -502,7 +208,8 @@ runtime·canpanic(G *gp)
return false;
if(m->locks-m->softfloat != 0 || m->mallocing != 0 || m->throwing != 0 || m->gcing != 0 || m->dying != 0)
return false;
- if(gp->status != Grunning || gp->syscallsp != 0)
+ status = runtime·readgstatus(gp);
+ if((status&~Gscan) != Grunning || gp->syscallsp != 0)
return false;
#ifdef GOOS_windows
if(m->libcallsp != 0)
@@ -510,71 +217,3 @@ runtime·canpanic(G *gp)
#endif
return true;
}
-
-void
-runtime·throw(int8 *s)
-{
- runtime·printf("fatal error: %s\n", s);
- if(g->m->throwing == 0)
- g->m->throwing = 1;
- runtime·startpanic();
- runtime·dopanic(0);
- *(int32*)0 = 0; // not reached
- runtime·exit(1); // even more not reached
-}
-
-void
-runtime·gothrow(String s)
-{
- if(g->m->throwing == 0)
- g->m->throwing = 1;
- runtime·startpanic();
- runtime·printf("fatal error: %S\n", s);
- runtime·dopanic(0);
- *(int32*)0 = 0; // not reached
- runtime·exit(1); // even more not reached
-}
-
-void
-runtime·panicstring(int8 *s)
-{
- Eface err;
-
- // m->softfloat is set during software floating point,
- // which might cause a fault during a memory load.
- // It increments m->locks to avoid preemption.
- // If we're panicking, the software floating point frames
- // will be unwound, so decrement m->locks as they would.
- if(g->m->softfloat) {
- g->m->locks--;
- g->m->softfloat = 0;
- }
-
- if(g->m->mallocing) {
- runtime·printf("panic: %s\n", s);
- runtime·throw("panic during malloc");
- }
- if(g->m->gcing) {
- runtime·printf("panic: %s\n", s);
- runtime·throw("panic during gc");
- }
- if(g->m->locks) {
- runtime·printf("panic: %s\n", s);
- runtime·throw("panic holding locks");
- }
- runtime·newErrorCString(s, &err);
- runtime·panic(err);
-}
-
-void
-runtime·Goexit(void)
-{
- rundefer();
- runtime·goexit();
-}
-
-void
-runtime·panicdivide(void)
-{
- runtime·panicstring("integer divide by zero");
-}
diff --git a/src/pkg/runtime/panic.go b/src/pkg/runtime/panic.go
new file mode 100644
index 0000000000..75d155b3fc
--- /dev/null
+++ b/src/pkg/runtime/panic.go
@@ -0,0 +1,216 @@
+// 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"
+
+var indexError = error(errorString("index out of range"))
+
+func panicindex() {
+ panic(indexError)
+}
+
+var sliceError = error(errorString("slice bounds out of range"))
+
+func panicslice() {
+ panic(sliceError)
+}
+
+var divideError = error(errorString("integer divide by zero"))
+
+func panicdivide() {
+ panic(divideError)
+}
+
+func throwreturn() {
+ gothrow("no return at end of a typed function - compiler is broken")
+}
+
+func throwinit() {
+ gothrow("recursive call during initialization - linker skew")
+}
+
+// Create a new deferred function fn with siz bytes of arguments.
+// The compiler turns a defer statement into a call to this.
+//go:nosplit
+func deferproc(siz int32, fn *funcval) { // arguments of fn follow fn
+ // the arguments of fn are in a perilous state. The stack map
+ // for deferproc does not describe them. So we can't let garbage
+ // collection or stack copying trigger until we've copied them out
+ // to somewhere safe. deferproc_m does that. Until deferproc_m,
+ // we can only call nosplit routines.
+ argp := uintptr(unsafe.Pointer(&fn))
+ argp += unsafe.Sizeof(fn)
+ if GOARCH == "arm" || GOARCH == "power64" || GOARCH == "power64le" {
+ argp += ptrSize // skip caller's saved link register
+ }
+ mp := acquirem()
+ mp.scalararg[0] = uintptr(siz)
+ mp.ptrarg[0] = unsafe.Pointer(fn)
+ mp.scalararg[1] = argp
+ mp.scalararg[2] = getcallerpc(unsafe.Pointer(&siz))
+
+ if mp.curg != getg() {
+ // go code on the m stack can't defer
+ gothrow("defer on m")
+ }
+
+ onM(deferproc_m)
+
+ releasem(mp)
+
+ // deferproc returns 0 normally.
+ // a deferred func that stops a panic
+ // makes the deferproc return 1.
+ // the code the compiler generates always
+ // checks the return value and jumps to the
+ // end of the function if deferproc returns != 0.
+ return0()
+ // No code can go here - the C return register has
+ // been set and must not be clobbered.
+}
+
+// Each P holds pool for defers with arg sizes 8, 24, 40, 56 and 72 bytes.
+// Memory block is 40 (24 for 32 bits) bytes larger due to Defer header.
+// This maps exactly to malloc size classes.
+
+// defer size class for arg size sz
+func deferclass(siz uintptr) uintptr {
+ return (siz + 7) >> 4
+}
+
+// total size of memory block for defer with arg size sz
+func totaldefersize(siz uintptr) uintptr {
+ return (unsafe.Sizeof(_defer{}) - unsafe.Sizeof(_defer{}.args)) + round(siz, ptrSize)
+}
+
+// Ensure that defer arg sizes that map to the same defer size class
+// also map to the same malloc size class.
+func testdefersizes() {
+ var m [len(p{}.deferpool)]int32
+
+ for i := range m {
+ m[i] = -1
+ }
+ for i := uintptr(0); ; i++ {
+ defersc := deferclass(i)
+ if defersc >= uintptr(len(m)) {
+ break
+ }
+ siz := goroundupsize(totaldefersize(i))
+ if m[defersc] < 0 {
+ m[defersc] = int32(siz)
+ continue
+ }
+ if m[defersc] != int32(siz) {
+ print("bad defer size class: i=", i, " siz=", siz, " defersc=", defersc, "\n")
+ gothrow("bad defer size class")
+ }
+ }
+}
+
+// Allocate a Defer, usually using per-P pool.
+// Each defer must be released with freedefer.
+// Note: runs on M stack
+func newdefer(siz int32) *_defer {
+ var d *_defer
+ sc := deferclass(uintptr(siz))
+ mp := acquirem()
+ if sc < uintptr(len(p{}.deferpool)) {
+ pp := mp.p
+ d = pp.deferpool[sc]
+ if d != nil {
+ pp.deferpool[sc] = d.link
+ }
+ }
+ if d == nil {
+ // deferpool is empty or just a big defer
+ total := goroundupsize(totaldefersize(uintptr(siz)))
+ d = (*_defer)(gomallocgc(total, conservative, 0))
+ }
+ d.siz = siz
+ d.special = false
+ gp := mp.curg
+ d.link = gp._defer
+ gp._defer = d
+ releasem(mp)
+ return d
+}
+
+// Free the given defer.
+// The defer cannot be used after this call.
+func freedefer(d *_defer) {
+ if d.special {
+ return
+ }
+ sc := deferclass(uintptr(d.siz))
+ if sc < uintptr(len(p{}.deferpool)) {
+ mp := acquirem()
+ pp := mp.p
+ d.link = pp.deferpool[sc]
+ pp.deferpool[sc] = d
+ releasem(mp)
+ // No need to wipe out pointers in argp/pc/fn/args,
+ // because we empty the pool before GC.
+ }
+}
+
+// Run a deferred function if there is one.
+// The compiler inserts a call to this at the end of any
+// function which calls defer.
+// If there is a deferred function, this will call runtime·jmpdefer,
+// which will jump to the deferred function such that it appears
+// to have been called by the caller of deferreturn at the point
+// just before deferreturn was called. The effect is that deferreturn
+// is called again and again until there are no more deferred functions.
+// Cannot split the stack because we reuse the caller's frame to
+// call the deferred function.
+
+// The single argument isn't actually used - it just has its address
+// taken so it can be matched against pending defers.
+//go:nosplit
+func deferreturn(arg0 uintptr) {
+ gp := getg()
+ d := gp._defer
+ if d == nil {
+ return
+ }
+ argp := uintptr(unsafe.Pointer(&arg0))
+ if d.argp != argp {
+ return
+ }
+
+ // Moving arguments around.
+ // Do not allow preemption here, because the garbage collector
+ // won't know the form of the arguments until the jmpdefer can
+ // flip the PC over to fn.
+ mp := acquirem()
+ memmove(unsafe.Pointer(argp), unsafe.Pointer(&d.args), uintptr(d.siz))
+ fn := d.fn
+ gp._defer = d.link
+ freedefer(d)
+ releasem(mp)
+ jmpdefer(fn, argp)
+}
+
+// Goexit terminates the goroutine that calls it. No other goroutine is affected.
+// Goexit runs all deferred calls before terminating the goroutine.
+//
+// Calling Goexit from the main goroutine terminates that goroutine
+// without func main returning. Since func main has not returned,
+// the program continues execution of other goroutines.
+// If all other goroutines exit, the program crashes.
+func Goexit() {
+ // Run all deferred functions for the current goroutine.
+ gp := getg()
+ for gp._defer != nil {
+ d := gp._defer
+ gp._defer = d.link
+ reflectcall(unsafe.Pointer(d.fn), unsafe.Pointer(&d.args), uint32(d.siz), uint32(d.siz))
+ freedefer(d)
+ // Note: we ignore recovers here because Goexit isn't a panic
+ }
+ goexit()
+}
diff --git a/src/pkg/runtime/panic1.go b/src/pkg/runtime/panic1.go
new file mode 100644
index 0000000000..9faae8a4b1
--- /dev/null
+++ b/src/pkg/runtime/panic1.go
@@ -0,0 +1,209 @@
+// 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 runtime
+
+import "unsafe"
+
+// Print all currently active panics. Used when crashing.
+func printpanics(p *_panic) {
+ if p.link != nil {
+ printpanics(p.link)
+ print("\t")
+ }
+ print("panic: ")
+ printany(p.arg)
+ if p.recovered {
+ print(" [recovered]")
+ }
+ print("\n")
+}
+
+// The implementation of the predeclared function panic.
+func gopanic(e interface{}) {
+ gp := getg()
+ if gp.m.curg != gp {
+ gothrow("panic on m stack")
+ }
+ var p _panic
+ var dabort _defer
+ p.arg = e
+ p.link = gp._panic
+ gp._panic = (*_panic)(noescape(unsafe.Pointer(&p)))
+
+ fn := abortpanic
+ dabort.fn = *(**funcval)(unsafe.Pointer(&fn))
+ dabort.siz = ptrSize
+ dabort.args[0] = noescape((unsafe.Pointer)(&p)) // TODO(khr): why do I need noescape here?
+ dabort.argp = _NoArgs
+ dabort.special = true
+
+ for {
+ d := gp._defer
+ if d == nil {
+ break
+ }
+ // take defer off list in case of recursive panic
+ gp._defer = d.link
+ argp := unsafe.Pointer(d.argp) // must be pointer so it gets adjusted during stack copy
+ pc := d.pc
+
+ // The deferred function may cause another panic,
+ // so reflectcall may not return. Set up a defer
+ // to mark this panic aborted if that happens.
+ dabort.link = gp._defer
+ gp._defer = (*_defer)(noescape(unsafe.Pointer(&dabort)))
+ p._defer = d
+
+ p.argp = getargp(0)
+ reflectcall(unsafe.Pointer(d.fn), unsafe.Pointer(&d.args), uint32(d.siz), uint32(d.siz))
+ p.argp = 0
+
+ // reflectcall did not panic. Remove dabort.
+ if gp._defer != &dabort {
+ gothrow("bad defer entry in panic")
+ }
+ gp._defer = dabort.link
+
+ // trigger shrinkage to test stack copy. See stack_test.go:TestStackPanic
+ //GC()
+
+ freedefer(d)
+ if p.recovered {
+ gp._panic = p.link
+ // Aborted panics are marked but remain on the g.panic list.
+ // Remove them from the list and free the associated defers.
+ for gp._panic != nil && gp._panic.aborted {
+ freedefer(gp._panic._defer)
+ gp._panic = gp._panic.link
+ }
+ if gp._panic == nil { // must be done with signal
+ gp.sig = 0
+ }
+ // Pass information about recovering frame to recovery.
+ gp.sigcode0 = uintptr(argp)
+ gp.sigcode1 = pc
+ mcall(recovery_m)
+ gothrow("recovery failed") // mcall should not return
+ }
+ }
+
+ // ran out of deferred calls - old-school panic now
+ startpanic()
+ printpanics(gp._panic)
+ dopanic(0) // should not return
+ *(*int)(nil) = 0 // not reached
+}
+
+// getargp returns the location where the caller
+// writes outgoing function call arguments.
+//go:nosplit
+func getargp(x int) uintptr {
+ // x is an argument mainly so that we can return its address.
+ // However, we need to make the function complex enough
+ // that it won't be inlined. We always pass x = 0, so this code
+ // does nothing other than keep the compiler from thinking
+ // the function is simple enough to inline.
+ if x > 0 {
+ return getcallersp(unsafe.Pointer(&x)) * 0
+ }
+ return uintptr(noescape(unsafe.Pointer(&x)))
+}
+
+func abortpanic(p *_panic) {
+ p.aborted = true
+}
+
+// The implementation of the predeclared function recover.
+// Cannot split the stack because it needs to reliably
+// find the stack segment of its caller.
+//
+// TODO(rsc): Once we commit to CopyStackAlways,
+// this doesn't need to be nosplit.
+//go:nosplit
+func gorecover(argp uintptr) interface{} {
+ // Must be in a function running as part of a deferred call during the panic.
+ // Must be called from the topmost function of the call
+ // (the function used in the defer statement).
+ // p.argp is the argument pointer of that topmost deferred function call.
+ // Compare against argp reported by caller.
+ // If they match, the caller is the one who can recover.
+ gp := getg()
+ p := gp._panic
+ if p != nil && !p.recovered && argp == p.argp {
+ p.recovered = true
+ return p.arg
+ }
+ return nil
+}
+
+//go:nosplit
+func startpanic() {
+ onM(startpanic_m)
+}
+
+//go:nosplit
+func dopanic(unused int) {
+ gp := getg()
+ mp := acquirem()
+ mp.ptrarg[0] = unsafe.Pointer(gp)
+ mp.scalararg[0] = getcallerpc((unsafe.Pointer)(&unused))
+ mp.scalararg[1] = getcallersp((unsafe.Pointer)(&unused))
+ onM(dopanic_m) // should never return
+ *(*int)(nil) = 0
+}
+
+//go:nosplit
+func throw(s *byte) {
+ print("fatal error: ", gostringnocopy(s), "\n")
+ gp := getg()
+ if gp.m.throwing == 0 {
+ gp.m.throwing = 1
+ }
+ startpanic()
+ dopanic(0)
+ *(*int)(nil) = 0 // not reached
+}
+
+//go:nosplit
+func gothrow(s string) {
+ gp := getg()
+ if gp.m.throwing == 0 {
+ gp.m.throwing = 1
+ }
+ startpanic()
+ print("fatal error: ", s, "\n")
+ dopanic(0)
+ *(*int)(nil) = 0 // not reached
+}
+
+func panicstring(s *int8) {
+ // m.softfloat is set during software floating point,
+ // which might cause a fault during a memory load.
+ // It increments m.locks to avoid preemption.
+ // If we're panicking, the software floating point frames
+ // will be unwound, so decrement m.locks as they would.
+ gp := getg()
+ if gp.m.softfloat != 0 {
+ gp.m.locks--
+ gp.m.softfloat = 0
+ }
+
+ if gp.m.mallocing != 0 {
+ print("panic: ", s, "\n")
+ gothrow("panic during malloc")
+ }
+ if gp.m.gcing != 0 {
+ print("panic: ", s, "\n")
+ gothrow("panic during gc")
+ }
+ if gp.m.locks != 0 {
+ print("panic: ", s, "\n")
+ gothrow("panic holding locks")
+ }
+
+ var err interface{}
+ newErrorCString(unsafe.Pointer(s), &err)
+ gopanic(err)
+}
diff --git a/src/pkg/runtime/parfor.c b/src/pkg/runtime/parfor.c
index 4706e0a43a..6023193b5c 100644
--- a/src/pkg/runtime/parfor.c
+++ b/src/pkg/runtime/parfor.c
@@ -27,7 +27,7 @@ runtime·parforalloc(uint32 nthrmax)
// The ParFor object is followed by CacheLineSize padding
// and then nthrmax ParForThread.
- desc = (ParFor*)runtime·malloc(sizeof(ParFor) + CacheLineSize + nthrmax * sizeof(ParForThread));
+ desc = (ParFor*)runtime·mallocgc(sizeof(ParFor) + CacheLineSize + nthrmax * sizeof(ParForThread), nil, 0);
desc->thr = (ParForThread*)((byte*)(desc+1) + CacheLineSize);
desc->nthrmax = nthrmax;
return desc;
@@ -192,8 +192,47 @@ exit:
// For testing from Go.
void
-runtime·parforiters(ParFor *desc, uintptr tid, uintptr *start, uintptr *end)
+runtime·newparfor_m(void)
{
- *start = (uint32)desc->thr[tid].pos;
- *end = (uint32)(desc->thr[tid].pos>>32);
+ g->m->ptrarg[0] = runtime·parforalloc(g->m->scalararg[0]);
+}
+
+void
+runtime·parforsetup_m(void)
+{
+ ParFor *desc;
+ void *ctx;
+ void (*body)(ParFor*, uint32);
+
+ desc = g->m->ptrarg[0];
+ g->m->ptrarg[0] = nil;
+ ctx = g->m->ptrarg[1];
+ g->m->ptrarg[1] = nil;
+ body = g->m->ptrarg[2];
+ g->m->ptrarg[2] = nil;
+
+ runtime·parforsetup(desc, g->m->scalararg[0], g->m->scalararg[1], ctx, g->m->scalararg[2], body);
+}
+
+void
+runtime·parfordo_m(void)
+{
+ ParFor *desc;
+
+ desc = g->m->ptrarg[0];
+ g->m->ptrarg[0] = nil;
+ runtime·parfordo(desc);
+}
+
+void
+runtime·parforiters_m(void)
+{
+ ParFor *desc;
+ uintptr tid;
+
+ desc = g->m->ptrarg[0];
+ g->m->ptrarg[0] = nil;
+ tid = g->m->scalararg[0];
+ g->m->scalararg[0] = desc->thr[tid].pos;
+ g->m->scalararg[1] = desc->thr[tid].pos>>32;
}
diff --git a/src/pkg/runtime/pprof/pprof.go b/src/pkg/runtime/pprof/pprof.go
index 26aa0b8be5..236de54f38 100644
--- a/src/pkg/runtime/pprof/pprof.go
+++ b/src/pkg/runtime/pprof/pprof.go
@@ -574,12 +574,6 @@ func StartCPUProfile(w io.Writer) error {
// each client to specify the frequency, we hard code it.
const hz = 100
- // Avoid queueing behind StopCPUProfile.
- // Could use TryLock instead if we had it.
- if cpu.profiling {
- return fmt.Errorf("cpu profiling already in use")
- }
-
cpu.Lock()
defer cpu.Unlock()
if cpu.done == nil {
diff --git a/src/pkg/runtime/pprof/pprof_test.go b/src/pkg/runtime/pprof/pprof_test.go
index aba538e755..df271273ce 100644
--- a/src/pkg/runtime/pprof/pprof_test.go
+++ b/src/pkg/runtime/pprof/pprof_test.go
@@ -9,7 +9,6 @@ package pprof_test
import (
"bytes"
"fmt"
- "hash/crc32"
"math/big"
"os/exec"
"regexp"
@@ -22,35 +21,65 @@ import (
"unsafe"
)
-func TestCPUProfile(t *testing.T) {
- buf := make([]byte, 100000)
- testCPUProfile(t, []string{"crc32.ChecksumIEEE"}, func() {
- // This loop takes about a quarter second on a 2 GHz laptop.
- // We only need to get one 100 Hz clock tick, so we've got
- // a 25x safety buffer.
- for i := 0; i < 1000; i++ {
- crc32.ChecksumIEEE(buf)
+func cpuHogger(f func()) {
+ // We only need to get one 100 Hz clock tick, so we've got
+ // a 100x safety buffer.
+ // But do at least 2000 iterations (which should take about 400ms),
+ // otherwise TestCPUProfileMultithreaded can fail if only one
+ // thread is scheduled during the 1 second period.
+ t0 := time.Now()
+ for i := 0; i < 2000 || time.Since(t0) < time.Second; i++ {
+ f()
+ }
+}
+
+var (
+ salt1 = 0
+ salt2 = 0
+)
+
+// The actual CPU hogging function.
+// Must not call other functions nor access heap/globals in the loop,
+// otherwise under race detector the samples will be in the race runtime.
+func cpuHog1() {
+ foo := salt1
+ for i := 0; i < 1e5; i++ {
+ if foo > 0 {
+ foo *= foo
+ } else {
+ foo *= foo + 1
+ }
+ }
+ salt1 = foo
+}
+
+func cpuHog2() {
+ foo := salt2
+ for i := 0; i < 1e5; i++ {
+ if foo > 0 {
+ foo *= foo
+ } else {
+ foo *= foo + 2
}
+ }
+ salt2 = foo
+}
+
+func TestCPUProfile(t *testing.T) {
+ testCPUProfile(t, []string{"runtime/pprof_test.cpuHog1"}, func() {
+ cpuHogger(cpuHog1)
})
}
func TestCPUProfileMultithreaded(t *testing.T) {
- buf := make([]byte, 100000)
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
- testCPUProfile(t, []string{"crc32.ChecksumIEEE", "crc32.Update"}, func() {
+ testCPUProfile(t, []string{"runtime/pprof_test.cpuHog1", "runtime/pprof_test.cpuHog2"}, func() {
c := make(chan int)
go func() {
- for i := 0; i < 2000; i++ {
- crc32.Update(0, crc32.IEEETable, buf)
- }
+ cpuHogger(cpuHog1)
c <- 1
}()
- // This loop takes about a quarter second on a 2 GHz laptop.
- // We only need to get one 100 Hz clock tick, so we've got
- // a 25x safety buffer.
- for i := 0; i < 2000; i++ {
- crc32.ChecksumIEEE(buf)
- }
+ cpuHogger(cpuHog2)
<-c
})
}
@@ -110,7 +139,7 @@ func testCPUProfile(t *testing.T, need []string, f func()) {
f()
StopCPUProfile()
- // Check that profile is well formed and contains ChecksumIEEE.
+ // Check that profile is well formed and contains need.
have := make([]uintptr, len(need))
parseProfile(t, prof.Bytes(), func(count uintptr, stk []uintptr) {
for _, pc := range stk {
@@ -281,31 +310,31 @@ func TestBlockProfile(t *testing.T) {
tests := [...]TestCase{
{"chan recv", blockChanRecv, `
[0-9]+ [0-9]+ @ 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]+
-# 0x[0-9,a-f]+ runtime\.chanrecv1\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.goc:[0-9]+
+# 0x[0-9,a-f]+ runtime\.chanrecv1\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.go:[0-9]+
# 0x[0-9,a-f]+ runtime/pprof_test\.blockChanRecv\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
# 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
`},
{"chan send", blockChanSend, `
[0-9]+ [0-9]+ @ 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]+
-# 0x[0-9,a-f]+ runtime\.chansend1\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.goc:[0-9]+
+# 0x[0-9,a-f]+ runtime\.chansend1\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.go:[0-9]+
# 0x[0-9,a-f]+ runtime/pprof_test\.blockChanSend\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
# 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
`},
{"chan close", blockChanClose, `
[0-9]+ [0-9]+ @ 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]+
-# 0x[0-9,a-f]+ runtime\.chanrecv1\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.goc:[0-9]+
+# 0x[0-9,a-f]+ runtime\.chanrecv1\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.go:[0-9]+
# 0x[0-9,a-f]+ runtime/pprof_test\.blockChanClose\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
# 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
`},
{"select recv async", blockSelectRecvAsync, `
[0-9]+ [0-9]+ @ 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]+
-# 0x[0-9,a-f]+ runtime\.selectgo\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.goc:[0-9]+
+# 0x[0-9,a-f]+ runtime\.selectgo\+0x[0-9,a-f]+ .*/src/pkg/runtime/select.go:[0-9]+
# 0x[0-9,a-f]+ runtime/pprof_test\.blockSelectRecvAsync\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
# 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
`},
{"select send sync", blockSelectSendSync, `
[0-9]+ [0-9]+ @ 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]+
-# 0x[0-9,a-f]+ runtime\.selectgo\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.goc:[0-9]+
+# 0x[0-9,a-f]+ runtime\.selectgo\+0x[0-9,a-f]+ .*/src/pkg/runtime/select.go:[0-9]+
# 0x[0-9,a-f]+ runtime/pprof_test\.blockSelectSendSync\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
# 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
`},
@@ -315,6 +344,12 @@ func TestBlockProfile(t *testing.T) {
# 0x[0-9,a-f]+ runtime/pprof_test\.blockMutex\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
# 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
`},
+ {"cond", blockCond, `
+[0-9]+ [0-9]+ @ 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]+
+# 0x[0-9,a-f]+ sync\.\(\*Cond\)\.Wait\+0x[0-9,a-f]+ .*/src/pkg/sync/cond\.go:[0-9]+
+# 0x[0-9,a-f]+ runtime/pprof_test\.blockCond\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
+# 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
+`},
}
runtime.SetBlockProfileRate(1)
@@ -401,3 +436,17 @@ func blockMutex() {
}()
mu.Lock()
}
+
+func blockCond() {
+ var mu sync.Mutex
+ c := sync.NewCond(&mu)
+ mu.Lock()
+ go func() {
+ time.Sleep(blockDelay)
+ mu.Lock()
+ c.Signal()
+ mu.Unlock()
+ }()
+ c.Wait()
+ mu.Unlock()
+}
diff --git a/src/pkg/runtime/print.c b/src/pkg/runtime/print.c
deleted file mode 100644
index e2905c2db3..0000000000
--- a/src/pkg/runtime/print.c
+++ /dev/null
@@ -1,443 +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.
-
-#include "runtime.h"
-#include "type.h"
-#include "../../cmd/ld/textflag.h"
-
-//static Lock debuglock;
-
-static void vprintf(int8*, byte*);
-
-// write to goroutine-local buffer if diverting output,
-// or else standard error.
-static void
-gwrite(void *v, intgo n)
-{
- if(g == nil || g->writebuf == nil) {
- runtime·write(2, v, n);
- return;
- }
-
- if(g->writenbuf == 0)
- return;
-
- if(n > g->writenbuf)
- n = g->writenbuf;
- runtime·memmove(g->writebuf, v, n);
- g->writebuf += n;
- g->writenbuf -= n;
-}
-
-void
-runtime·dump(byte *p, int32 n)
-{
- int32 i;
-
- for(i=0; i<n; i++) {
- runtime·printpointer_c((byte*)(p[i]>>4));
- runtime·printpointer_c((byte*)(p[i]&0xf));
- if((i&15) == 15)
- runtime·prints("\n");
- else
- runtime·prints(" ");
- }
- if(n & 15)
- runtime·prints("\n");
-}
-
-void
-runtime·prints(int8 *s)
-{
- gwrite(s, runtime·findnull((byte*)s));
-}
-
-#pragma textflag NOSPLIT
-void
-runtime·printf(int8 *s, ...)
-{
- byte *arg;
-
- arg = (byte*)(&s+1);
- vprintf(s, arg);
-}
-
-#pragma textflag NOSPLIT
-int32
-runtime·snprintf(byte *buf, int32 n, int8 *s, ...)
-{
- byte *arg;
- int32 m;
-
- arg = (byte*)(&s+1);
- g->writebuf = buf;
- g->writenbuf = n-1;
- vprintf(s, arg);
- *g->writebuf = '\0';
- m = g->writebuf - buf;
- g->writenbuf = 0;
- g->writebuf = nil;
- return m;
-}
-
-// Very simple printf. Only for debugging prints.
-// Do not add to this without checking with Rob.
-static void
-vprintf(int8 *s, byte *base)
-{
- int8 *p, *lp;
- uintptr arg, siz;
- byte *v;
-
- //runtime·lock(&debuglock);
-
- lp = p = s;
- arg = (uintptr)base;
- for(; *p; p++) {
- if(*p != '%')
- continue;
- if(p > lp)
- gwrite(lp, p-lp);
- p++;
- siz = 0;
- switch(*p) {
- case 't':
- case 'c':
- siz = 1;
- break;
- case 'd': // 32-bit
- case 'x':
- arg = ROUND(arg, 4);
- siz = 4;
- break;
- case 'D': // 64-bit
- case 'U':
- case 'X':
- case 'f':
- arg = ROUND(arg, sizeof(uintreg));
- siz = 8;
- break;
- case 'C':
- arg = ROUND(arg, sizeof(uintreg));
- siz = 16;
- break;
- case 'p': // pointer-sized
- case 's':
- arg = ROUND(arg, sizeof(uintptr));
- siz = sizeof(uintptr);
- break;
- case 'S': // pointer-aligned but bigger
- arg = ROUND(arg, sizeof(uintptr));
- siz = sizeof(String);
- break;
- case 'a': // pointer-aligned but bigger
- arg = ROUND(arg, sizeof(uintptr));
- siz = sizeof(Slice);
- break;
- case 'i': // pointer-aligned but bigger
- case 'e':
- arg = ROUND(arg, sizeof(uintptr));
- siz = sizeof(Eface);
- break;
- }
- v = (byte*)arg;
- switch(*p) {
- case 'a':
- runtime·printslice_c(*(Slice*)v);
- break;
- case 'c':
- runtime·printbyte_c(*(int8*)v);
- break;
- case 'd':
- runtime·printint_c(*(int32*)v);
- break;
- case 'D':
- runtime·printint_c(*(int64*)v);
- break;
- case 'e':
- runtime·printeface_c(*(Eface*)v);
- break;
- case 'f':
- runtime·printfloat_c(*(float64*)v);
- break;
- case 'C':
- runtime·printcomplex_c(*(Complex128*)v);
- break;
- case 'i':
- runtime·printiface_c(*(Iface*)v);
- break;
- case 'p':
- runtime·printpointer_c(*(void**)v);
- break;
- case 's':
- runtime·prints(*(int8**)v);
- break;
- case 'S':
- runtime·printstring_c(*(String*)v);
- break;
- case 't':
- runtime·printbool_c(*(bool*)v);
- break;
- case 'U':
- runtime·printuint_c(*(uint64*)v);
- break;
- case 'x':
- runtime·printhex_c(*(uint32*)v);
- break;
- case 'X':
- runtime·printhex_c(*(uint64*)v);
- break;
- }
- arg += siz;
- lp = p+1;
- }
- if(p > lp)
- gwrite(lp, p-lp);
-
- //runtime·unlock(&debuglock);
-}
-
-static void
-goprintf_m(void)
-{
- // Can assume s has terminating NUL because only
- // the Go compiler generates calls to runtime·goprintf, using
- // string constants, and all the string constants have NULs.
- vprintf(g->m->ptrarg[0], g->m->ptrarg[1]);
- g->m->ptrarg[0] = nil;
- g->m->ptrarg[1] = nil;
-}
-
-#pragma textflag NOSPLIT
-void
-runtime·goprintf(String s, ...)
-{
- g->m->ptrarg[0] = s.str;
- g->m->ptrarg[1] = (byte*)(&s+1);
- runtime·onM(goprintf_m);
-}
-
-void
-runtime·printpc_c(void *p)
-{
- runtime·prints("PC=");
- runtime·printhex_c((uint64)runtime·getcallerpc(p));
-}
-
-void
-runtime·printbool_c(bool v)
-{
- if(v) {
- gwrite((byte*)"true", 4);
- return;
- }
- gwrite((byte*)"false", 5);
-}
-
-void
-runtime·printbyte_c(int8 c)
-{
- gwrite(&c, 1);
-}
-
-void
-runtime·printfloat_c(float64 v)
-{
- byte buf[20];
- int32 e, s, i, n;
- float64 h;
-
- if(ISNAN(v)) {
- gwrite("NaN", 3);
- return;
- }
- if(v == runtime·posinf) {
- gwrite("+Inf", 4);
- return;
- }
- if(v == runtime·neginf) {
- gwrite("-Inf", 4);
- return;
- }
-
- n = 7; // digits printed
- e = 0; // exp
- s = 0; // sign
- if(v == 0) {
- if(1/v == runtime·neginf)
- s = 1;
- } else {
- // sign
- if(v < 0) {
- v = -v;
- s = 1;
- }
-
- // normalize
- while(v >= 10) {
- e++;
- v /= 10;
- }
- while(v < 1) {
- e--;
- v *= 10;
- }
-
- // round
- h = 5;
- for(i=0; i<n; i++)
- h /= 10;
-
- v += h;
- if(v >= 10) {
- e++;
- v /= 10;
- }
- }
-
- // format +d.dddd+edd
- buf[0] = '+';
- if(s)
- buf[0] = '-';
- for(i=0; i<n; i++) {
- s = v;
- buf[i+2] = s+'0';
- v -= s;
- v *= 10.;
- }
- buf[1] = buf[2];
- buf[2] = '.';
-
- buf[n+2] = 'e';
- buf[n+3] = '+';
- if(e < 0) {
- e = -e;
- buf[n+3] = '-';
- }
-
- buf[n+4] = (e/100) + '0';
- buf[n+5] = (e/10)%10 + '0';
- buf[n+6] = (e%10) + '0';
- gwrite(buf, n+7);
-}
-
-void
-runtime·printcomplex_c(Complex128 v)
-{
- gwrite("(", 1);
- runtime·printfloat_c(v.real);
- runtime·printfloat_c(v.imag);
- gwrite("i)", 2);
-}
-
-void
-runtime·printuint_c(uint64 v)
-{
- byte buf[100];
- int32 i;
-
- for(i=nelem(buf)-1; i>0; i--) {
- buf[i] = v%10 + '0';
- if(v < 10)
- break;
- v = v/10;
- }
- gwrite(buf+i, nelem(buf)-i);
-}
-
-void
-runtime·printint_c(int64 v)
-{
- if(v < 0) {
- gwrite("-", 1);
- v = -v;
- }
- runtime·printuint_c(v);
-}
-
-void
-runtime·printhex_c(uint64 v)
-{
- static int8 *dig = "0123456789abcdef";
- byte buf[100];
- int32 i;
-
- i=nelem(buf);
- for(; v>0; v/=16)
- buf[--i] = dig[v%16];
- if(i == nelem(buf))
- buf[--i] = '0';
- buf[--i] = 'x';
- buf[--i] = '0';
- gwrite(buf+i, nelem(buf)-i);
-}
-
-void
-runtime·printpointer_c(void *p)
-{
- runtime·printhex_c((uintptr)p);
-}
-
-void
-runtime·printstring_c(String v)
-{
- if(v.len > runtime·maxstring) {
- gwrite("[string too long]", 17);
- return;
- }
- if(v.len > 0)
- gwrite(v.str, v.len);
-}
-
-void
-runtime·printslice_c(Slice s)
-{
- runtime·prints("[");
- runtime·printint_c(s.len);
- runtime·prints("/");
- runtime·printint_c(s.cap);
- runtime·prints("]");
- runtime·printpointer_c(s.array);
-}
-
-void
-runtime·printeface_c(Eface e)
-{
- runtime·printf("(%p,%p)", e.type, e.data);
-}
-
-void
-runtime·printiface_c(Iface i)
-{
- runtime·printf("(%p,%p)", i.tab, i.data);
-}
-
-void
-runtime·printstring_m(void)
-{
- String s;
-
- s.str = g->m->ptrarg[0];
- g->m->ptrarg[0] = nil;
- s.len = g->m->scalararg[0];
- runtime·printstring_c(s);
-}
-
-void
-runtime·printuint_m(void)
-{
- runtime·printuint_c(*(uint64*)(&g->m->scalararg[0]));
-}
-
-void
-runtime·printhex_m(void)
-{
- runtime·printhex_c(g->m->scalararg[0]);
-}
-
-void
-runtime·printfloat_m(void)
-{
- runtime·printfloat_c(*(float64*)(&g->m->scalararg[0]));
-}
diff --git a/src/pkg/runtime/print.go b/src/pkg/runtime/print.go
deleted file mode 100644
index 904af5d333..0000000000
--- a/src/pkg/runtime/print.go
+++ /dev/null
@@ -1,109 +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"
-)
-
-// these 4 functions are complicated enough that we will share
-// the print logic with the C printf.
-var printstring_m byte
-var printuint_m byte
-var printhex_m byte
-var printfloat_m byte
-
-func printstring(s string) {
- mp := acquirem()
- mp.scalararg[0] = uint(len(s))
- mp.ptrarg[0] = (*stringStruct)(unsafe.Pointer(&s)).str
- onM(&printstring_m)
- releasem(mp)
-}
-
-func printuint(x uint64) {
- mp := acquirem()
- *(*uint64)(unsafe.Pointer(&mp.scalararg[0])) = x
- onM(&printuint_m)
- releasem(mp)
-}
-
-func printhex(x uintptr) {
- mp := acquirem()
- mp.scalararg[0] = uint(x)
- onM(&printhex_m)
- releasem(mp)
-}
-
-func printfloat(x float64) {
- mp := acquirem()
- *(*float64)(unsafe.Pointer(&mp.scalararg[0])) = x
- onM(&printfloat_m)
- releasem(mp)
-}
-
-// all other print functions are expressible as combinations
-// of the above 4 functions.
-func printnl() {
- printstring("\n")
-}
-
-func printsp() {
- printstring(" ")
-}
-
-func printbool(b bool) {
- if b {
- printstring("true")
- } else {
- printstring("false")
- }
-}
-
-func printpointer(p unsafe.Pointer) {
- printhex(uintptr(p))
-}
-
-func printint(x int64) {
- if x < 0 {
- printstring("-")
- x = -x
- }
- printuint(uint64(x))
-}
-
-func printcomplex(x complex128) {
- printstring("(")
- printfloat(real(x))
- printfloat(imag(x))
- printstring("i)")
-}
-
-func printiface(i interface {
- f()
-}) {
- printstring("(")
- printhex((*[2]uintptr)(unsafe.Pointer(&i))[0])
- printstring(",")
- printhex((*[2]uintptr)(unsafe.Pointer(&i))[1])
- printstring(")")
-}
-
-func printeface(e interface{}) {
- printstring("(")
- printhex((*[2]uintptr)(unsafe.Pointer(&e))[0])
- printstring(",")
- printhex((*[2]uintptr)(unsafe.Pointer(&e))[1])
- printstring(")")
-}
-
-func printslice(b []byte) {
- printstring("[")
- printint(int64(len(b)))
- printstring("/")
- printint(int64(cap(b)))
- printstring("]")
- printhex((*[3]uintptr)(unsafe.Pointer(&b))[0])
-}
diff --git a/src/pkg/runtime/print1.go b/src/pkg/runtime/print1.go
new file mode 100644
index 0000000000..93f83ed26f
--- /dev/null
+++ b/src/pkg/runtime/print1.go
@@ -0,0 +1,341 @@
+// 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"
+
+// The compiler knows that a print of a value of this type
+// should use printhex instead of printuint (decimal).
+type hex uint64
+
+//go:noescape
+func gostring(*byte) string
+
+func bytes(s string) (ret []byte) {
+ rp := (*slice)(unsafe.Pointer(&ret))
+ sp := (*_string)(noescape(unsafe.Pointer(&s)))
+ rp.array = sp.str
+ rp.len = uint(sp.len)
+ rp.cap = uint(sp.len)
+ return
+}
+
+// goprintf is the function call that is actually deferred when you write
+// defer print(...)
+// It is otherwise unused. In particular it is not used for ordinary prints.
+// Right now a dynamically allocated string that is being passed as an
+// argument is invisible to the garbage collector and might be collected
+// if that argument list is the only reference. For now we ignore that possibility.
+// To fix, we should change to defer a call to vprintf with a pointer to
+// an argument list on the stack, stored in an appropriately typed
+// struct. golang.org/issue/8614.
+//go:nosplit
+func goprintf(s string) {
+ vprintf(s, add(unsafe.Pointer(&s), unsafe.Sizeof(s)))
+}
+
+// printf is only called from C code. It has the same problem as goprintf
+// with strings possibly being collected from underneath.
+// However, the runtime never prints dynamically allocated
+// Go strings using printf. The strings it prints come from the symbol
+// and type tables.
+//go:nosplit
+func printf(s *byte) {
+ vprintf(gostringnocopy(s), add(unsafe.Pointer(&s), unsafe.Sizeof(s)))
+}
+
+// sprintf is only called from C code.
+// It has the same problem as goprintf.
+//go:nosplit
+func snprintf(dst *byte, n int32, s *byte) {
+ buf := (*[1 << 30]byte)(unsafe.Pointer(dst))[0:n:n]
+
+ gp := getg()
+ gp.writebuf = buf[0:0 : n-1] // leave room for NUL, this is called from C
+ vprintf(gostringnocopy(s), add(unsafe.Pointer(&s), unsafe.Sizeof(s)))
+ buf[len(gp.writebuf)] = '\x00'
+ gp.writebuf = nil
+}
+
+//var debuglock mutex
+
+// write to goroutine-local buffer if diverting output,
+// or else standard error.
+func gwrite(b []byte) {
+ if len(b) == 0 {
+ return
+ }
+ gp := getg()
+ if gp == nil || gp.writebuf == nil {
+ write(2, unsafe.Pointer(&b[0]), int32(len(b)))
+ return
+ }
+
+ n := copy(gp.writebuf[len(gp.writebuf):cap(gp.writebuf)], b)
+ gp.writebuf = gp.writebuf[:len(gp.writebuf)+n]
+}
+
+func prints(s *byte) {
+ b := (*[1 << 30]byte)(unsafe.Pointer(s))
+ for i := 0; ; i++ {
+ if b[i] == 0 {
+ gwrite(b[:i])
+ return
+ }
+ }
+}
+
+func printsp() {
+ print(" ")
+}
+
+func printnl() {
+ print("\n")
+}
+
+// Very simple printf. Only for debugging prints.
+// Do not add to this without checking with Rob.
+func vprintf(str string, arg unsafe.Pointer) {
+ //lock(&debuglock);
+
+ s := bytes(str)
+ start := 0
+ i := 0
+ for ; i < len(s); i++ {
+ if s[i] != '%' {
+ continue
+ }
+ if i > start {
+ gwrite(s[start:i])
+ }
+ if i++; i >= len(s) {
+ break
+ }
+ var siz uintptr
+ switch s[i] {
+ case 't', 'c':
+ siz = 1
+ case 'd', 'x': // 32-bit
+ arg = roundup(arg, 4)
+ siz = 4
+ case 'D', 'U', 'X', 'f': // 64-bit
+ arg = roundup(arg, unsafe.Sizeof(uintreg(0)))
+ siz = 8
+ case 'C':
+ arg = roundup(arg, unsafe.Sizeof(uintreg(0)))
+ siz = 16
+ case 'p', 's': // pointer-sized
+ arg = roundup(arg, unsafe.Sizeof(uintptr(0)))
+ siz = unsafe.Sizeof(uintptr(0))
+ case 'S': // pointer-aligned but bigger
+ arg = roundup(arg, unsafe.Sizeof(uintptr(0)))
+ siz = unsafe.Sizeof(string(""))
+ case 'a': // pointer-aligned but bigger
+ arg = roundup(arg, unsafe.Sizeof(uintptr(0)))
+ siz = unsafe.Sizeof([]byte{})
+ case 'i', 'e': // pointer-aligned but bigger
+ arg = roundup(arg, unsafe.Sizeof(uintptr(0)))
+ siz = unsafe.Sizeof(interface{}(nil))
+ }
+ switch s[i] {
+ case 'a':
+ printslice(*(*[]byte)(arg))
+ case 'c':
+ printbyte(*(*byte)(arg))
+ case 'd':
+ printint(int64(*(*int32)(arg)))
+ case 'D':
+ printint(int64(*(*int64)(arg)))
+ case 'e':
+ printeface(*(*interface{})(arg))
+ case 'f':
+ printfloat(*(*float64)(arg))
+ case 'C':
+ printcomplex(*(*complex128)(arg))
+ case 'i':
+ printiface(*(*fInterface)(arg))
+ case 'p':
+ printpointer(*(*unsafe.Pointer)(arg))
+ case 's':
+ prints(*(**byte)(arg))
+ case 'S':
+ printstring(*(*string)(arg))
+ case 't':
+ printbool(*(*bool)(arg))
+ case 'U':
+ printuint(*(*uint64)(arg))
+ case 'x':
+ printhex(uint64(*(*uint32)(arg)))
+ case 'X':
+ printhex(*(*uint64)(arg))
+ }
+ arg = add(arg, siz)
+ start = i + 1
+ }
+ if start < i {
+ gwrite(s[start:i])
+ }
+
+ //unlock(&debuglock);
+}
+
+func printpc(p unsafe.Pointer) {
+ print("PC=", hex(uintptr(p)))
+}
+
+func printbool(v bool) {
+ if v {
+ print("true")
+ } else {
+ print("false")
+ }
+}
+
+func printbyte(c byte) {
+ gwrite((*[1]byte)(unsafe.Pointer(&c))[:])
+}
+
+func printfloat(v float64) {
+ switch {
+ case v != v:
+ print("NaN")
+ return
+ case v+v == v && v > 0:
+ print("+Inf")
+ return
+ case v+v == v && v < 0:
+ print("-Inf")
+ return
+ }
+
+ const n = 7 // digits printed
+ var buf [n + 7]byte
+ buf[0] = '+'
+ e := 0 // exp
+ if v == 0 {
+ if 1/v < 0 {
+ buf[0] = '-'
+ }
+ } else {
+ if v < 0 {
+ v = -v
+ buf[0] = '-'
+ }
+
+ // normalize
+ for v >= 10 {
+ e++
+ v /= 10
+ }
+ for v < 1 {
+ e--
+ v *= 10
+ }
+
+ // round
+ h := 5.0
+ for i := 0; i < n; i++ {
+ h /= 10
+ }
+ v += h
+ if v >= 10 {
+ e++
+ v /= 10
+ }
+ }
+
+ // format +d.dddd+edd
+ for i := 0; i < n; i++ {
+ s := int(v)
+ buf[i+2] = byte(s + '0')
+ v -= float64(s)
+ v *= 10
+ }
+ buf[1] = buf[2]
+ buf[2] = '.'
+
+ buf[n+2] = 'e'
+ buf[n+3] = '+'
+ if e < 0 {
+ e = -e
+ buf[n+3] = '-'
+ }
+
+ buf[n+4] = byte(e/100) + '0'
+ buf[n+5] = byte(e/10)%10 + '0'
+ buf[n+6] = byte(e%10) + '0'
+ gwrite(buf[:])
+}
+
+func printcomplex(c complex128) {
+ print("(", real(c), imag(c), "i)")
+}
+
+func printuint(v uint64) {
+ var buf [100]byte
+ i := len(buf)
+ for i--; i > 0; i-- {
+ buf[i] = byte(v%10 + '0')
+ if v < 10 {
+ break
+ }
+ v /= 10
+ }
+ gwrite(buf[i:])
+}
+
+func printint(v int64) {
+ if v < 0 {
+ print("-")
+ v = -v
+ }
+ printuint(uint64(v))
+}
+
+func printhex(v uint64) {
+ const dig = "0123456789abcdef"
+ var buf [100]byte
+ i := len(buf)
+ for i--; i > 0; i-- {
+ buf[i] = dig[v%16]
+ if v < 16 {
+ break
+ }
+ v /= 16
+ }
+ i--
+ buf[i] = 'x'
+ i--
+ buf[i] = '0'
+ gwrite(buf[i:])
+}
+
+func printpointer(p unsafe.Pointer) {
+ printhex(uint64(uintptr(p)))
+}
+
+func printstring(s string) {
+ if uintptr(len(s)) > maxstring {
+ gwrite(bytes("[string too long]"))
+ return
+ }
+ gwrite(bytes(s))
+}
+
+func printslice(s []byte) {
+ sp := (*slice)(unsafe.Pointer(&s))
+ print("[", len(s), "/", cap(s), "]")
+ printpointer(unsafe.Pointer(sp.array))
+}
+
+func printeface(e interface{}) {
+ ep := (*eface)(unsafe.Pointer(&e))
+ print("(", ep._type, ",", ep.data, ")")
+}
+
+func printiface(i fInterface) {
+ ip := (*iface)(unsafe.Pointer(&i))
+ print("(", ip.tab, ",", ip.data, ")")
+}
diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c
index 23513e1a7b..d77f20abf9 100644
--- a/src/pkg/runtime/proc.c
+++ b/src/pkg/runtime/proc.c
@@ -10,7 +10,7 @@
#include "race.h"
#include "type.h"
#include "mgc0.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// Goroutine scheduler
// The scheduler's job is to distribute ready-to-run goroutines over worker threads.
@@ -26,7 +26,7 @@
typedef struct Sched Sched;
struct Sched {
- Lock;
+ Mutex lock;
uint64 goidgen;
@@ -46,7 +46,7 @@ struct Sched {
int32 runqsize;
// Global cache of dead G's.
- Lock gflock;
+ Mutex gflock;
G* gfree;
int32 ngfree;
@@ -62,10 +62,6 @@ struct Sched {
enum
{
- // The max value of GOMAXPROCS.
- // There are no fundamental restrictions on the value.
- MaxGomaxprocs = 1<<8,
-
// Number of goroutine ids to grab from runtime·sched.goidgen to local per-P cache at once.
// 16 seems to provide enough amortization, but other than that it's mostly arbitrary number.
GoidCacheBatch = 16,
@@ -80,14 +76,17 @@ G runtime·g0; // idle goroutine for m0
G* runtime·lastg;
M* runtime·allm;
M* runtime·extram;
+P* runtime·allp[MaxGomaxprocs+1];
int8* runtime·goos;
int32 runtime·ncpu;
static int32 newprocs;
-static Lock allglock; // the following vars are protected by this lock or by stoptheworld
+static Mutex allglock; // the following vars are protected by this lock or by stoptheworld
G** runtime·allg;
+Slice runtime·allgs;
uintptr runtime·allglen;
static uintptr allgcap;
+ForceGCState runtime·forcegc;
void runtime·mstart(void);
static void runqput(P*, G*);
@@ -113,7 +112,7 @@ static uint32 retake(int64);
static void incidlelocked(int32);
static void checkdead(void);
static void exitsyscall0(G*);
-static void park0(G*);
+void runtime·park_m(G*);
static void goexit0(G*);
static void gfput(P*, G*);
static G* gfget(P*);
@@ -129,9 +128,25 @@ static bool preemptone(P*);
static bool exitsyscallfast(void);
static bool haveexperiment(int8*);
static void allgadd(G*);
+static void dropg(void);
extern String runtime·buildVersion;
+// For cgo-using programs with external linking,
+// export "main" (defined in assembly) so that libc can handle basic
+// C runtime startup and call the Go program as if it were
+// the C main function.
+#pragma cgo_export_static main
+
+// Filled in by dynamic linker when Cgo is available.
+void* _cgo_init;
+void* _cgo_malloc;
+void* _cgo_free;
+
+// Copy for Go code.
+void* runtime·cgoMalloc;
+void* runtime·cgoFree;
+
// The bootstrap sequence is:
//
// call osinit
@@ -158,7 +173,6 @@ runtime·schedinit(void)
runtime·symtabinit();
runtime·stackinit();
runtime·mallocinit();
- runtime·chaninit();
mcommoninit(g->m);
// Initialize the itable value for newErrorCString,
@@ -167,13 +181,10 @@ runtime·schedinit(void)
// need to allocated memory.
runtime·newErrorCString(0, &i);
- // Initialize the cached gotraceback value, since
- // gotraceback calls getenv, which mallocs on Plan 9.
- runtime·gotraceback(nil);
-
runtime·goargs();
runtime·goenvs();
runtime·parsedebugvars();
+ runtime·gcinit();
runtime·sched.lastpoll = runtime·nanotime();
procs = 1;
@@ -183,7 +194,6 @@ runtime·schedinit(void)
n = MaxGomaxprocs;
procs = n;
}
- runtime·allp = runtime·malloc((MaxGomaxprocs+1)*sizeof(runtime·allp[0]));
procresize(procs);
runtime·copystack = runtime·precisestack;
@@ -191,21 +201,21 @@ runtime·schedinit(void)
if(p != nil && !runtime·strcmp(p, (byte*)"0"))
runtime·copystack = false;
- mstats.enablegc = 1;
-
if(runtime·buildVersion.str == nil) {
// Condition should never trigger. This code just serves
// to ensure runtime·buildVersion is kept in the resulting binary.
runtime·buildVersion.str = (uint8*)"unknown";
runtime·buildVersion.len = 7;
}
+
+ runtime·cgoMalloc = _cgo_malloc;
+ runtime·cgoFree = _cgo_free;
}
extern void main·init(void);
+extern void runtime·init(void);
extern void main·main(void);
-static FuncVal scavenger = {runtime·MHeap_Scavenger};
-
static FuncVal initDone = { runtime·unlockOSThread };
// The main goroutine.
@@ -222,7 +232,11 @@ void
runtime·main(void)
{
Defer d;
-
+
+ // Racectx of m0->g0 is used only as the parent of the main goroutine.
+ // It must not be used for anything else.
+ g->m->g0->racectx = 0;
+
// Max stack size is 1 GB on 64-bit, 250 MB on 32-bit.
// Using decimal instead of binary GB and MB because
// they look nicer in the stack overflow failure message.
@@ -251,7 +265,10 @@ runtime·main(void)
if(g->m != &runtime·m0)
runtime·throw("runtime·main not on m0");
- runtime·newproc1(&scavenger, nil, 0, 0, runtime·main);
+
+ runtime·init();
+ mstats.enablegc = 1; // now that runtime is initialized, GC is okay
+
main·init();
if(g->defer != &d || d.fn != &initDone)
@@ -268,95 +285,25 @@ runtime·main(void)
// let the other goroutine finish printing the panic trace.
// Once it does, it will exit. See issue 3934.
if(runtime·panicking)
- runtime·park(nil, nil, "panicwait");
+ runtime·park(nil, nil, runtime·gostringnocopy((byte*)"panicwait"));
runtime·exit(0);
for(;;)
*(int32*)runtime·main = 0;
}
-void
-runtime·goroutineheader(G *gp)
-{
- int8 *status;
- int64 waitfor;
-
- switch(gp->status) {
- case Gidle:
- status = "idle";
- break;
- case Grunnable:
- status = "runnable";
- break;
- case Grunning:
- status = "running";
- break;
- case Gsyscall:
- status = "syscall";
- break;
- case Gwaiting:
- if(gp->waitreason)
- status = gp->waitreason;
- else
- status = "waiting";
- break;
- default:
- status = "???";
- break;
- }
-
- // approx time the G is blocked, in minutes
- waitfor = 0;
- if((gp->status == Gwaiting || gp->status == Gsyscall) && gp->waitsince != 0)
- waitfor = (runtime·nanotime() - gp->waitsince) / (60LL*1000*1000*1000);
-
- runtime·printf("goroutine %D [%s", gp->goid, status);
- if(waitfor >= 1)
- runtime·printf(", %D minutes", waitfor);
- if(gp->lockedm != nil)
- runtime·printf(", locked to thread");
- runtime·printf("]:\n");
-}
-
-void
-runtime·tracebackothers(G *me)
+static void
+dumpgstatus(G* gp)
{
- G *gp;
- int32 traceback;
- uintptr i;
-
- traceback = runtime·gotraceback(nil);
-
- // Show the current goroutine first, if we haven't already.
- if((gp = g->m->curg) != nil && gp != me) {
- runtime·printf("\n");
- runtime·goroutineheader(gp);
- runtime·traceback(~(uintptr)0, ~(uintptr)0, 0, gp);
- }
-
- runtime·lock(&allglock);
- for(i = 0; i < runtime·allglen; i++) {
- gp = runtime·allg[i];
- if(gp == me || gp == g->m->curg || gp->status == Gdead)
- continue;
- if(gp->issystem && traceback < 2)
- continue;
- runtime·printf("\n");
- runtime·goroutineheader(gp);
- if(gp->status == Grunning) {
- runtime·printf("\tgoroutine running on other thread; stack unavailable\n");
- runtime·printcreatedby(gp);
- } else
- runtime·traceback(~(uintptr)0, ~(uintptr)0, 0, gp);
- }
- runtime·unlock(&allglock);
+ runtime·printf("runtime: gp: gp=%p, goid=%D, gp->atomicstatus=%x\n", gp, gp->goid, runtime·readgstatus(gp));
+ runtime·printf("runtime: g: g=%p, goid=%D, g->atomicstatus=%x\n", g, g->goid, runtime·readgstatus(g));
}
static void
checkmcount(void)
{
// sched lock is held
- if(runtime·sched.mcount > runtime·sched.maxmcount) {
+ if(runtime·sched.mcount > runtime·sched.maxmcount){
runtime·printf("runtime: program exceeds %d-thread limit\n", runtime·sched.maxmcount);
runtime·throw("thread exhaustion");
}
@@ -371,7 +318,7 @@ mcommoninit(M *mp)
mp->fastrand = 0x49f6428aUL + mp->id + runtime·cputicks();
- runtime·lock(&runtime·sched);
+ runtime·lock(&runtime·sched.lock);
mp->id = runtime·sched.mcount++;
checkmcount();
runtime·mpreinit(mp);
@@ -382,20 +329,24 @@ mcommoninit(M *mp)
// runtime·NumCgoCall() iterates over allm w/o schedlock,
// so we need to publish it safely.
runtime·atomicstorep(&runtime·allm, mp);
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
}
// Mark gp ready to run.
void
runtime·ready(G *gp)
{
+ uint32 status;
+
+ status = runtime·readgstatus(gp);
// Mark runnable.
g->m->locks++; // disable preemption because it can be holding p in a local var
- if(gp->status != Gwaiting) {
- runtime·printf("goroutine %D has status %d\n", gp->goid, gp->status);
+ if((status&~Gscan) != Gwaiting){
+ dumpgstatus(gp);
runtime·throw("bad g->status in ready");
}
- gp->status = Grunnable;
+ // status is Gwaiting or Gscanwaiting, make Grunnable and put on runq
+ runtime·casgstatus(gp, Gwaiting, Grunnable);
runqput(g->m->p, gp);
if(runtime·atomicload(&runtime·sched.npidle) != 0 && runtime·atomicload(&runtime·sched.nmspinning) == 0) // TODO: fast atomic
wakep();
@@ -404,6 +355,16 @@ runtime·ready(G *gp)
g->stackguard0 = StackPreempt;
}
+void
+runtime·ready_m(void)
+{
+ G *gp;
+
+ gp = g->m->ptrarg[0];
+ g->m->ptrarg[0] = nil;
+ runtime·ready(gp);
+}
+
int32
runtime·gcprocs(void)
{
@@ -411,7 +372,7 @@ runtime·gcprocs(void)
// Figure out how many CPUs to use during GC.
// Limited by gomaxprocs, number of actual CPUs, and MaxGcproc.
- runtime·lock(&runtime·sched);
+ runtime·lock(&runtime·sched.lock);
n = runtime·gomaxprocs;
if(n > runtime·ncpu)
n = runtime·ncpu;
@@ -419,7 +380,7 @@ runtime·gcprocs(void)
n = MaxGcproc;
if(n > runtime·sched.nmidle+1) // one M is currently running
n = runtime·sched.nmidle+1;
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
return n;
}
@@ -428,14 +389,14 @@ needaddgcproc(void)
{
int32 n;
- runtime·lock(&runtime·sched);
+ runtime·lock(&runtime·sched.lock);
n = runtime·gomaxprocs;
if(n > runtime·ncpu)
n = runtime·ncpu;
if(n > MaxGcproc)
n = MaxGcproc;
n -= runtime·sched.nmidle+1; // one M is currently running
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
return n > 0;
}
@@ -445,7 +406,7 @@ runtime·helpgc(int32 nproc)
M *mp;
int32 n, pos;
- runtime·lock(&runtime·sched);
+ runtime·lock(&runtime·sched.lock);
pos = 0;
for(n = 1; n < nproc; n++) { // one M is currently running
if(runtime·allp[pos]->mcache == g->m->mcache)
@@ -458,7 +419,7 @@ runtime·helpgc(int32 nproc)
pos++;
runtime·notewakeup(&mp->park);
}
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
}
// Similar to stoptheworld but best-effort and can be called several times.
@@ -489,6 +450,324 @@ runtime·freezetheworld(void)
runtime·usleep(1000);
}
+static bool
+isscanstatus(uint32 status)
+{
+ if(status == Gscan)
+ runtime·throw("isscanstatus: Bad status Gscan");
+ return (status&Gscan) == Gscan;
+}
+
+// All reads and writes of g's status go through readgstatus, casgstatus
+// castogscanstatus, casfromgscanstatus.
+#pragma textflag NOSPLIT
+uint32
+runtime·readgstatus(G *gp)
+{
+ return runtime·atomicload(&gp->atomicstatus);
+}
+
+// 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
+// simple atomic stores but for now we are going to throw if
+// we see an inconsistent state.
+void
+runtime·casfromgscanstatus(G *gp, uint32 oldval, uint32 newval)
+{
+ bool success = false;
+
+ // Check that transition is valid.
+ switch(oldval) {
+ case Gscanrunnable:
+ case Gscanwaiting:
+ case Gscanrunning:
+ case Gscansyscall:
+ if(newval == (oldval&~Gscan))
+ success = runtime·cas(&gp->atomicstatus, oldval, newval);
+ break;
+ case Gscanenqueue:
+ if(newval == Gwaiting)
+ success = runtime·cas(&gp->atomicstatus, oldval, newval);
+ break;
+ }
+ if(!success){
+ runtime·printf("runtime: casfromgscanstatus failed gp=%p, oldval=%d, newval=%d\n",
+ gp, oldval, newval);
+ dumpgstatus(gp);
+ runtime·throw("casfromgscanstatus: gp->status is not in scan state");
+ }
+}
+
+// This will return false if the gp is not in the expected status and the cas fails.
+// This acts like a lock acquire while the casfromgstatus acts like a lock release.
+bool
+runtime·castogscanstatus(G *gp, uint32 oldval, uint32 newval)
+{
+ switch(oldval) {
+ case Grunnable:
+ case Gwaiting:
+ case Gsyscall:
+ if(newval == (oldval|Gscan))
+ return runtime·cas(&gp->atomicstatus, oldval, newval);
+ break;
+ case Grunning:
+ if(newval == Gscanrunning || newval == Gscanenqueue)
+ return runtime·cas(&gp->atomicstatus, oldval, newval);
+ break;
+ }
+
+ runtime·printf("runtime: castogscanstatus oldval=%d newval=%d\n", oldval, newval);
+ runtime·throw("castogscanstatus");
+ return false; // not reached
+}
+
+static void badcasgstatus(void);
+static void helpcasgstatus(void);
+
+// If asked to move to or from a Gscanstatus this will throw. Use the castogscanstatus
+// and casfromgscanstatus instead.
+// casgstatus will loop if the g->atomicstatus is in a Gscan status until the routine that
+// put it in the Gscan state is finished.
+#pragma textflag NOSPLIT
+void
+runtime·casgstatus(G *gp, uint32 oldval, uint32 newval)
+{
+ void (*fn)(void);
+
+ if((oldval&Gscan) || (newval&Gscan) || oldval == newval) {
+ g->m->scalararg[0] = oldval;
+ g->m->scalararg[1] = newval;
+ fn = badcasgstatus;
+ runtime·onM(&fn);
+ }
+
+ // loop if gp->atomicstatus is in a scan state giving
+ // GC time to finish and change the state to oldval.
+ while(!runtime·cas(&gp->atomicstatus, oldval, newval)) {
+ // Help GC if needed.
+ if(gp->preemptscan && !gp->gcworkdone && (oldval == Grunning || oldval == Gsyscall)) {
+ gp->preemptscan = false;
+ g->m->ptrarg[0] = gp;
+ fn = helpcasgstatus;
+ runtime·onM(&fn);
+ }
+ }
+}
+
+static void
+badcasgstatus(void)
+{
+ uint32 oldval, newval;
+
+ oldval = g->m->scalararg[0];
+ newval = g->m->scalararg[1];
+ g->m->scalararg[0] = 0;
+ g->m->scalararg[1] = 0;
+
+ runtime·printf("casgstatus: oldval=%d, newval=%d\n", oldval, newval);
+ runtime·throw("casgstatus: bad incoming values");
+}
+
+static void
+helpcasgstatus(void)
+{
+ G *gp;
+
+ gp = g->m->ptrarg[0];
+ g->m->ptrarg[0] = 0;
+ runtime·gcphasework(gp);
+}
+
+// stopg ensures that gp is stopped at a GC safe point where its stack can be scanned
+// or in the context of a moving collector the pointers can be flipped from pointing
+// to old object to pointing to new objects.
+// If stopg returns true, the caller knows gp is at a GC safe point and will remain there until
+// the caller calls restartg.
+// If stopg returns false, the caller is not responsible for calling restartg. This can happen
+// if another thread, either the gp itself or another GC thread is taking the responsibility
+// to do the GC work related to this thread.
+bool
+runtime·stopg(G *gp)
+{
+ uint32 s;
+
+ for(;;) {
+ if(gp->gcworkdone)
+ return false;
+
+ s = runtime·readgstatus(gp);
+ switch(s) {
+ default:
+ dumpgstatus(gp);
+ runtime·throw("stopg: gp->atomicstatus is not valid");
+
+ case Gdead:
+ return false;
+
+ case Gcopystack:
+ // Loop until a new stack is in place.
+ break;
+
+ case Grunnable:
+ case Gsyscall:
+ case Gwaiting:
+ // Claim goroutine by setting scan bit.
+ if(!runtime·castogscanstatus(gp, s, s|Gscan))
+ break;
+ // In scan state, do work.
+ runtime·gcphasework(gp);
+ return true;
+
+ case Gscanrunnable:
+ case Gscanwaiting:
+ case Gscansyscall:
+ // Goroutine already claimed by another GC helper.
+ return false;
+
+ case Grunning:
+ // Claim goroutine, so we aren't racing with a status
+ // transition away from Grunning.
+ if(!runtime·castogscanstatus(gp, Grunning, Gscanrunning))
+ break;
+
+ // Mark gp for preemption.
+ if(!gp->gcworkdone) {
+ gp->preemptscan = true;
+ gp->preempt = true;
+ gp->stackguard0 = StackPreempt;
+ }
+
+ // Unclaim.
+ runtime·casfromgscanstatus(gp, Gscanrunning, Grunning);
+ return false;
+ }
+ }
+ // Should not be here....
+}
+
+// The GC requests that this routine be moved from a scanmumble state to a mumble state.
+void
+runtime·restartg (G *gp)
+{
+ uint32 s;
+
+ s = runtime·readgstatus(gp);
+ switch(s) {
+ default:
+ dumpgstatus(gp);
+ runtime·throw("restartg: unexpected status");
+
+ case Gdead:
+ break;
+
+ case Gscanrunnable:
+ case Gscanwaiting:
+ case Gscansyscall:
+ runtime·casfromgscanstatus(gp, s, s&~Gscan);
+ break;
+
+ case Gscanenqueue:
+ // Scan is now completed.
+ // Goroutine now needs to be made runnable.
+ // We put it on the global run queue; ready blocks on the global scheduler lock.
+ runtime·casfromgscanstatus(gp, Gscanenqueue, Gwaiting);
+ if(gp != g->m->curg)
+ runtime·throw("processing Gscanenqueue on wrong m");
+ dropg();
+ runtime·ready(gp);
+ break;
+ }
+}
+
+static void
+stopscanstart(G* gp)
+{
+ if(g == gp)
+ runtime·throw("GC not moved to G0");
+ if(runtime·stopg(gp)) {
+ if(!isscanstatus(runtime·readgstatus(gp))) {
+ dumpgstatus(gp);
+ runtime·throw("GC not in scan state");
+ }
+ runtime·restartg(gp);
+ }
+}
+
+// Runs on g0 and does the actual work after putting the g back on the run queue.
+static void
+mquiesce(G *gpmaster)
+{
+ G* gp;
+ uint32 i;
+ uint32 status;
+ uint32 activeglen;
+
+ activeglen = runtime·allglen;
+ // enqueue the calling goroutine.
+ runtime·restartg(gpmaster);
+ for(i = 0; i < activeglen; i++) {
+ gp = runtime·allg[i];
+ if(runtime·readgstatus(gp) == Gdead)
+ gp->gcworkdone = true; // noop scan.
+ else
+ gp->gcworkdone = false;
+ stopscanstart(gp);
+ }
+
+ // Check that the G's gcwork (such as scanning) has been done. If not do it now.
+ // You can end up doing work here if the page trap on a Grunning Goroutine has
+ // not been sprung or in some race situations. For example a runnable goes dead
+ // and is started up again with a gp->gcworkdone set to false.
+ for(i = 0; i < activeglen; i++) {
+ gp = runtime·allg[i];
+ while (!gp->gcworkdone) {
+ status = runtime·readgstatus(gp);
+ if(status == Gdead) {
+ gp->gcworkdone = true; // scan is a noop
+ break;
+ //do nothing, scan not needed.
+ }
+ if(status == Grunning && gp->stackguard0 == (uintptr)StackPreempt && runtime·notetsleep(&runtime·sched.stopnote, 100*1000)) // nanosecond arg
+ runtime·noteclear(&runtime·sched.stopnote);
+ else
+ stopscanstart(gp);
+ }
+ }
+
+ for(i = 0; i < activeglen; i++) {
+ gp = runtime·allg[i];
+ status = runtime·readgstatus(gp);
+ if(isscanstatus(status)) {
+ runtime·printf("mstopandscang:bottom: post scan bad status gp=%p has status %x\n", gp, status);
+ dumpgstatus(gp);
+ }
+ if(!gp->gcworkdone && status != Gdead) {
+ runtime·printf("mstopandscang:bottom: post scan gp=%p->gcworkdone still false\n", gp);
+ dumpgstatus(gp);
+ }
+ }
+
+ schedule(); // Never returns.
+}
+
+// quiesce moves all the goroutines to a GC safepoint which for now is a at preemption point.
+// If the global runtime·gcphase is GCmark quiesce will ensure that all of the goroutine's stacks
+// have been scanned before it returns.
+void
+runtime·quiesce(G* mastergp)
+{
+ void (*fn)(G*);
+
+ runtime·castogscanstatus(mastergp, Grunning, Gscanenqueue);
+ // Now move this to the g0 (aka m) stack.
+ // g0 will potentially scan this thread and put mastergp on the runqueue
+ fn = mquiesce;
+ runtime·mcall(&fn);
+}
+
+// This is used by the GC as well as the routines that do stack dumps. In the case
+// of GC all the routines can be reliably stopped. This is not always the case
+// when the system is in panic or being exited.
void
runtime·stoptheworld(void)
{
@@ -497,12 +776,17 @@ runtime·stoptheworld(void)
P *p;
bool wait;
- runtime·lock(&runtime·sched);
+ // If we hold a lock, then we won't be able to stop another M
+ // that is blocked trying to acquire the lock.
+ if(g->m->locks > 0)
+ runtime·throw("stoptheworld: holding locks");
+
+ runtime·lock(&runtime·sched.lock);
runtime·sched.stopwait = runtime·gomaxprocs;
runtime·atomicstore((uint32*)&runtime·sched.gcwaiting, 1);
preemptall();
// stop current P
- g->m->p->status = Pgcstop;
+ g->m->p->status = Pgcstop; // Pgcstop is only diagnostic.
runtime·sched.stopwait--;
// try to retake all P's in Psyscall status
for(i = 0; i < runtime·gomaxprocs; i++) {
@@ -517,7 +801,7 @@ runtime·stoptheworld(void)
runtime·sched.stopwait--;
}
wait = runtime·sched.stopwait > 0;
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
// wait for remaining P's to stop voluntarily
if(wait) {
@@ -557,7 +841,7 @@ runtime·starttheworld(void)
gp = runtime·netpoll(false); // non-blocking
injectglist(gp);
add = needaddgcproc();
- runtime·lock(&runtime·sched);
+ runtime·lock(&runtime·sched.lock);
if(newprocs) {
procresize(newprocs);
newprocs = 0;
@@ -581,7 +865,7 @@ runtime·starttheworld(void)
runtime·sched.sysmonwait = false;
runtime·notewakeup(&runtime·sched.sysmonnote);
}
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
while(p1) {
p = p1;
@@ -823,7 +1107,9 @@ runtime·newextram(void)
gp->syscallsp = gp->sched.sp;
gp->syscallstack = gp->stackbase;
gp->syscallguard = gp->stackguard;
- gp->status = Gsyscall;
+ // malg returns status as Gidle, change to Gsyscall before adding to allg
+ // where GC will see it.
+ runtime·casgstatus(gp, Gidle, Gsyscall);
gp->m = mp;
mp->curg = gp;
mp->locked = LockInternal;
@@ -964,9 +1250,9 @@ stopm(void)
}
retry:
- runtime·lock(&runtime·sched);
+ runtime·lock(&runtime·sched.lock);
mput(g->m);
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
runtime·notesleep(&g->m->park);
runtime·noteclear(&g->m->park);
if(g->m->helpgc) {
@@ -993,18 +1279,18 @@ startm(P *p, bool spinning)
M *mp;
void (*fn)(void);
- runtime·lock(&runtime·sched);
+ runtime·lock(&runtime·sched.lock);
if(p == nil) {
p = pidleget();
if(p == nil) {
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
if(spinning)
runtime·xadd(&runtime·sched.nmspinning, -1);
return;
}
}
mp = mget();
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
if(mp == nil) {
fn = nil;
if(spinning)
@@ -1033,32 +1319,32 @@ handoffp(P *p)
// no local work, check that there are no spinning/idle M's,
// otherwise our help is not required
if(runtime·atomicload(&runtime·sched.nmspinning) + runtime·atomicload(&runtime·sched.npidle) == 0 && // TODO: fast atomic
- runtime·cas(&runtime·sched.nmspinning, 0, 1)) {
+ runtime·cas(&runtime·sched.nmspinning, 0, 1)){
startm(p, true);
return;
}
- runtime·lock(&runtime·sched);
+ runtime·lock(&runtime·sched.lock);
if(runtime·sched.gcwaiting) {
p->status = Pgcstop;
if(--runtime·sched.stopwait == 0)
runtime·notewakeup(&runtime·sched.stopnote);
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
return;
}
if(runtime·sched.runqsize) {
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
startm(p, false);
return;
}
// If this is the last running P and nobody is polling network,
// need to wakeup another M to poll network.
if(runtime·sched.npidle == runtime·gomaxprocs-1 && runtime·atomicload64(&runtime·sched.lastpoll) != 0) {
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
startm(p, false);
return;
}
pidleput(p);
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
}
// Tries to add one more P to execute G's.
@@ -1078,6 +1364,7 @@ static void
stoplockedm(void)
{
P *p;
+ uint32 status;
if(g->m->lockedg == nil || g->m->lockedg->lockedm != g->m)
runtime·throw("stoplockedm: inconsistent locking");
@@ -1090,8 +1377,12 @@ stoplockedm(void)
// Wait until another thread schedules lockedg again.
runtime·notesleep(&g->m->park);
runtime·noteclear(&g->m->park);
- if(g->m->lockedg->status != Grunnable)
+ status = runtime·readgstatus(g->m->lockedg);
+ if((status&~Gscan) != Grunnable){
+ runtime·printf("runtime:stoplockedm: g is not Grunnable or Gscanrunnable");
+ dumpgstatus(g);
runtime·throw("stoplockedm: not runnable");
+ }
acquirep(g->m->nextp);
g->m->nextp = nil;
}
@@ -1130,11 +1421,11 @@ gcstopm(void)
runtime·xadd(&runtime·sched.nmspinning, -1);
}
p = releasep();
- runtime·lock(&runtime·sched);
+ runtime·lock(&runtime·sched.lock);
p->status = Pgcstop;
if(--runtime·sched.stopwait == 0)
runtime·notewakeup(&runtime·sched.stopnote);
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
stopm();
}
@@ -1144,12 +1435,8 @@ static void
execute(G *gp)
{
int32 hz;
-
- if(gp->status != Grunnable) {
- runtime·printf("execute: bad g status %d\n", gp->status);
- runtime·throw("execute: bad g status");
- }
- gp->status = Grunning;
+
+ runtime·casgstatus(gp, Grunnable, Grunning);
gp->waitsince = 0;
gp->preempt = false;
gp->stackguard0 = gp->stackguard;
@@ -1187,9 +1474,9 @@ top:
return gp;
// global runq
if(runtime·sched.runqsize) {
- runtime·lock(&runtime·sched);
+ runtime·lock(&runtime·sched.lock);
gp = globrunqget(g->m->p, 0);
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
if(gp)
return gp;
}
@@ -1197,7 +1484,7 @@ top:
gp = runtime·netpoll(false); // non-blocking
if(gp) {
injectglist(gp->schedlink);
- gp->status = Grunnable;
+ runtime·casgstatus(gp, Gwaiting, Grunnable);
return gp;
}
// If number of spinning M's >= number of busy P's, block.
@@ -1223,19 +1510,19 @@ top:
}
stop:
// return P and block
- runtime·lock(&runtime·sched);
+ runtime·lock(&runtime·sched.lock);
if(runtime·sched.gcwaiting) {
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
goto top;
}
if(runtime·sched.runqsize) {
gp = globrunqget(g->m->p, 0);
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
return gp;
}
p = releasep();
pidleput(p);
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
if(g->m->spinning) {
g->m->spinning = false;
runtime·xadd(&runtime·sched.nmspinning, -1);
@@ -1244,9 +1531,9 @@ stop:
for(i = 0; i < runtime·gomaxprocs; i++) {
p = runtime·allp[i];
if(p && p->runqhead != p->runqtail) {
- runtime·lock(&runtime·sched);
+ runtime·lock(&runtime·sched.lock);
p = pidleget();
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
if(p) {
acquirep(p);
goto top;
@@ -1263,13 +1550,13 @@ stop:
gp = runtime·netpoll(true); // block until new work is available
runtime·atomicstore64(&runtime·sched.lastpoll, runtime·nanotime());
if(gp) {
- runtime·lock(&runtime·sched);
+ runtime·lock(&runtime·sched.lock);
p = pidleget();
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
if(p) {
acquirep(p);
injectglist(gp->schedlink);
- gp->status = Grunnable;
+ runtime·casgstatus(gp, Gwaiting, Grunnable);
return gp;
}
injectglist(gp);
@@ -1308,14 +1595,14 @@ injectglist(G *glist)
if(glist == nil)
return;
- runtime·lock(&runtime·sched);
+ runtime·lock(&runtime·sched.lock);
for(n = 0; glist; n++) {
gp = glist;
glist = gp->schedlink;
- gp->status = Grunnable;
+ runtime·casgstatus(gp, Gwaiting, Grunnable);
globrunqput(gp);
}
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
for(; n && runtime·sched.npidle; n--)
startm(nil, false);
@@ -1351,9 +1638,9 @@ top:
// This is a fancy way to say tick%61==0,
// it uses 2 MUL instructions instead of a single DIV and so is faster on modern processors.
if(tick - (((uint64)tick*0x4325c53fu)>>36)*61 == 0 && runtime·sched.runqsize > 0) {
- runtime·lock(&runtime·sched);
+ runtime·lock(&runtime·sched.lock);
gp = globrunqget(g->m->p, 1);
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
if(gp)
resetspinning();
}
@@ -1384,7 +1671,7 @@ top:
// appropriate time. After calling dropg and arranging for gp to be
// readied later, the caller can do other work but eventually should
// call schedule to restart the scheduling of goroutines on this m.
-void
+static void
dropg(void)
{
if(g->m->lockedg == nil) {
@@ -1396,18 +1683,19 @@ dropg(void)
// Puts the current goroutine into a waiting state and calls unlockf.
// If unlockf returns false, the goroutine is resumed.
void
-runtime·park(bool(*unlockf)(G*, void*), void *lock, int8 *reason)
+runtime·park(bool(*unlockf)(G*, void*), void *lock, String reason)
{
- if(g->status != Grunning)
- runtime·throw("bad g status");
+ void (*fn)(G*);
+
g->m->waitlock = lock;
g->m->waitunlockf = unlockf;
g->waitreason = reason;
- runtime·mcall(park0);
+ fn = runtime·park_m;
+ runtime·mcall(&fn);
}
-static bool
-parkunlock(G *gp, void *lock)
+bool
+runtime·parkunlock_c(G *gp, void *lock)
{
USED(gp);
runtime·unlock(lock);
@@ -1417,18 +1705,18 @@ parkunlock(G *gp, void *lock)
// Puts the current goroutine into a waiting state and unlocks the lock.
// The goroutine can be made runnable again by calling runtime·ready(gp).
void
-runtime·parkunlock(Lock *lock, int8 *reason)
+runtime·parkunlock(Mutex *lock, String reason)
{
- runtime·park(parkunlock, lock, reason);
+ runtime·park(runtime·parkunlock_c, lock, reason);
}
// runtime·park continuation on g0.
-static void
-park0(G *gp)
+void
+runtime·park_m(G *gp)
{
bool ok;
- gp->status = Gwaiting;
+ runtime·casgstatus(gp, Grunning, Gwaiting);
dropg();
if(g->m->waitunlockf) {
@@ -1436,7 +1724,7 @@ park0(G *gp)
g->m->waitunlockf = nil;
g->m->waitlock = nil;
if(!ok) {
- gp->status = Grunnable;
+ runtime·casgstatus(gp, Gwaiting, Grunnable);
execute(gp); // Schedule it back, never returns.
}
}
@@ -1448,20 +1736,28 @@ park0(G *gp)
void
runtime·gosched(void)
{
- if(g->status != Grunning)
- runtime·throw("bad g status");
- runtime·mcall(runtime·gosched0);
+ void (*fn)(G*);
+
+ fn = runtime·gosched_m;
+ runtime·mcall(&fn);
}
// runtime·gosched continuation on g0.
void
-runtime·gosched0(G *gp)
+runtime·gosched_m(G *gp)
{
- gp->status = Grunnable;
+ uint32 status;
+
+ status = runtime·readgstatus(gp);
+ if((status&~Gscan) != Grunning){
+ dumpgstatus(gp);
+ runtime·throw("bad g status");
+ }
+ runtime·casgstatus(gp, Grunning, Grunnable);
dropg();
- runtime·lock(&runtime·sched);
+ runtime·lock(&runtime·sched.lock);
globrunqput(gp);
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
schedule();
}
@@ -1474,29 +1770,32 @@ runtime·gosched0(G *gp)
void
runtime·goexit(void)
{
- if(g->status != Grunning)
- runtime·throw("bad g status");
+ void (*fn)(G*);
+
if(raceenabled)
runtime·racegoend();
- runtime·mcall(goexit0);
+ fn = goexit0;
+ runtime·mcall(&fn);
}
// runtime·goexit continuation on g0.
static void
goexit0(G *gp)
{
- gp->status = Gdead;
+ runtime·casgstatus(gp, Grunning, Gdead);
gp->m = nil;
gp->lockedm = nil;
g->m->lockedg = nil;
gp->paniconfault = 0;
gp->defer = nil; // should be true already but just in case.
gp->panic = nil; // non-nil for Goexit during panic. points at stack-allocated data.
- gp->writenbuf = 0;
- gp->writebuf = nil;
- gp->waitreason = nil;
+ gp->writebuf.array = nil;
+ gp->writebuf.len = 0;
+ gp->writebuf.cap = 0;
+ gp->waitreason.str = nil;
+ gp->waitreason.len = 0;
gp->param = nil;
-
+
dropg();
if(g->m->locked & ~LockExternal) {
@@ -1521,6 +1820,10 @@ save(void *pc, uintptr sp)
g->sched.g = g;
}
+static void entersyscall_bad(void);
+static void entersyscall_sysmon(void);
+static void entersyscall_gcwait(void);
+
// The goroutine g is about to enter a system call.
// Record that it's not using the cpu anymore.
// This is called only from the go syscall library and cgocall,
@@ -1529,13 +1832,30 @@ save(void *pc, uintptr sp)
// Entersyscall cannot split the stack: the runtime·gosave must
// make g->sched refer to the caller's stack segment, because
// entersyscall is going to return immediately after.
+//
+// Nothing entersyscall calls can split the stack either.
+// We cannot safely move the stack during an active call to syscall,
+// because we do not know which of the uintptr arguments are
+// really pointers (back into the stack).
+// In practice, this means that we make the fast path run through
+// entersyscall doing no-split things, and the slow path has to use onM
+// to run bigger things on the m stack.
#pragma textflag NOSPLIT
void
·entersyscall(int32 dummy)
{
+ void (*fn)(void);
+
// Disable preemption because during this function g is in Gsyscall status,
// but can have inconsistent g->sched, do not let GC observe it.
g->m->locks++;
+
+ // Entersyscall must not call any function that might split/grow the stack.
+ // (See details in comment above.)
+ // Catch calls that might, by replacing the stack guard with something that
+ // will trip any stack check and leaving a flag to tell newstack to die.
+ g->stackguard0 = StackPreempt;
+ g->throwsplit = 1;
// Leave SP around for GC and traceback.
save(runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy));
@@ -1543,20 +1863,15 @@ void
g->syscallpc = g->sched.pc;
g->syscallstack = g->stackbase;
g->syscallguard = g->stackguard;
- g->status = Gsyscall;
+ runtime·casgstatus(g, Grunning, Gsyscall);
if(g->syscallsp < g->syscallguard-StackGuard || g->syscallstack < g->syscallsp) {
- // runtime·printf("entersyscall inconsistent %p [%p,%p]\n",
- // g->syscallsp, g->syscallguard-StackGuard, g->syscallstack);
- runtime·throw("entersyscall");
+ fn = entersyscall_bad;
+ runtime·onM(&fn);
}
if(runtime·atomicload(&runtime·sched.sysmonwait)) { // TODO: fast atomic
- runtime·lock(&runtime·sched);
- if(runtime·atomicload(&runtime·sched.sysmonwait)) {
- runtime·atomicstore(&runtime·sched.sysmonwait, 0);
- runtime·notewakeup(&runtime·sched.sysmonnote);
- }
- runtime·unlock(&runtime·sched);
+ fn = entersyscall_sysmon;
+ runtime·onM(&fn);
save(runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy));
}
@@ -1564,12 +1879,8 @@ void
g->m->p->m = nil;
runtime·atomicstore(&g->m->p->status, Psyscall);
if(runtime·sched.gcwaiting) {
- runtime·lock(&runtime·sched);
- if (runtime·sched.stopwait > 0 && runtime·cas(&g->m->p->status, Psyscall, Pgcstop)) {
- if(--runtime·sched.stopwait == 0)
- runtime·notewakeup(&runtime·sched.stopnote);
- }
- runtime·unlock(&runtime·sched);
+ fn = entersyscall_gcwait;
+ runtime·onM(&fn);
save(runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy));
}
@@ -1580,14 +1891,51 @@ void
g->m->locks--;
}
+static void
+entersyscall_bad(void)
+{
+ G *gp;
+
+ gp = g->m->curg;
+ runtime·printf("entersyscall inconsistent %p [%p,%p]\n",
+ gp->syscallsp, gp->syscallguard-StackGuard, gp->syscallstack);
+ runtime·throw("entersyscall");
+}
+
+static void
+entersyscall_sysmon(void)
+{
+ runtime·lock(&runtime·sched.lock);
+ if(runtime·atomicload(&runtime·sched.sysmonwait)) {
+ runtime·atomicstore(&runtime·sched.sysmonwait, 0);
+ runtime·notewakeup(&runtime·sched.sysmonnote);
+ }
+ runtime·unlock(&runtime·sched.lock);
+}
+
+static void
+entersyscall_gcwait(void)
+{
+ runtime·lock(&runtime·sched.lock);
+ if (runtime·sched.stopwait > 0 && runtime·cas(&g->m->p->status, Psyscall, Pgcstop)) {
+ if(--runtime·sched.stopwait == 0)
+ runtime·notewakeup(&runtime·sched.stopnote);
+ }
+ runtime·unlock(&runtime·sched.lock);
+}
+
+static void entersyscallblock_handoff(void);
+
// The same as runtime·entersyscall(), but with a hint that the syscall is blocking.
#pragma textflag NOSPLIT
void
·entersyscallblock(int32 dummy)
{
- P *p;
+ void (*fn)(void);
g->m->locks++; // see comment in entersyscall
+ g->throwsplit = 1;
+ g->stackguard0 = StackPreempt; // see comment in entersyscall
// Leave SP around for GC and traceback.
save(runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy));
@@ -1595,25 +1943,27 @@ void
g->syscallpc = g->sched.pc;
g->syscallstack = g->stackbase;
g->syscallguard = g->stackguard;
- g->status = Gsyscall;
+ runtime·casgstatus(g, Grunning, Gsyscall);
if(g->syscallsp < g->syscallguard-StackGuard || g->syscallstack < g->syscallsp) {
- // runtime·printf("entersyscall inconsistent %p [%p,%p]\n",
- // g->syscallsp, g->syscallguard-StackGuard, g->syscallstack);
- runtime·throw("entersyscallblock");
+ fn = entersyscall_bad;
+ runtime·onM(&fn);
}
-
- p = releasep();
- handoffp(p);
- if(g->isbackground) // do not consider blocked scavenger for deadlock detection
- incidlelocked(1);
+
+ fn = entersyscallblock_handoff;
+ runtime·onM(&fn);
// Resave for traceback during blocked call.
save(runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy));
- g->stackguard0 = StackPreempt; // see comment in entersyscall
g->m->locks--;
}
+static void
+entersyscallblock_handoff(void)
+{
+ handoffp(releasep());
+}
+
// The goroutine g exited its system call.
// Arrange for it to run on a cpu again.
// This is called only from the go syscall library, not
@@ -1622,16 +1972,17 @@ void
void
runtime·exitsyscall(void)
{
- g->m->locks++; // see comment in entersyscall
+ void (*fn)(G*);
- if(g->isbackground) // do not consider blocked scavenger for deadlock detection
- incidlelocked(-1);
+ g->m->locks++; // see comment in entersyscall
g->waitsince = 0;
if(exitsyscallfast()) {
// There's a cpu for us, so we can run.
g->m->p->syscalltick++;
- g->status = Grunning;
+ // We need to cas the status and scan before resuming...
+ runtime·casgstatus(g, Gsyscall, Grunning);
+
// Garbage collector isn't running (since we are),
// so okay to clear gcstack and gcsp.
g->syscallstack = (uintptr)nil;
@@ -1644,13 +1995,15 @@ runtime·exitsyscall(void)
// otherwise restore the real stackguard, we've spoiled it in entersyscall/entersyscallblock
g->stackguard0 = g->stackguard;
}
+ g->throwsplit = 0;
return;
}
g->m->locks--;
// Call the scheduler.
- runtime·mcall(exitsyscall0);
+ fn = exitsyscall0;
+ runtime·mcall(&fn);
// Scheduler returned, so we're allowed to run now.
// Delete the gcstack information that we left for
@@ -1661,13 +2014,16 @@ runtime·exitsyscall(void)
g->syscallstack = (uintptr)nil;
g->syscallsp = (uintptr)nil;
g->m->p->syscalltick++;
+ g->throwsplit = 0;
}
+static void exitsyscallfast_pidle(void);
+
#pragma textflag NOSPLIT
static bool
exitsyscallfast(void)
{
- P *p;
+ void (*fn)(void);
// Freezetheworld sets stopwait but does not retake P's.
if(runtime·sched.stopwait) {
@@ -1685,21 +2041,35 @@ exitsyscallfast(void)
// Try to get any other idle P.
g->m->p = nil;
if(runtime·sched.pidle) {
- runtime·lock(&runtime·sched);
- p = pidleget();
- if(p && runtime·atomicload(&runtime·sched.sysmonwait)) {
- runtime·atomicstore(&runtime·sched.sysmonwait, 0);
- runtime·notewakeup(&runtime·sched.sysmonnote);
- }
- runtime·unlock(&runtime·sched);
- if(p) {
- acquirep(p);
+ fn = exitsyscallfast_pidle;
+ runtime·onM(&fn);
+ if(g->m->scalararg[0]) {
+ g->m->scalararg[0] = 0;
return true;
}
}
return false;
}
+static void
+exitsyscallfast_pidle(void)
+{
+ P *p;
+
+ runtime·lock(&runtime·sched.lock);
+ p = pidleget();
+ if(p && runtime·atomicload(&runtime·sched.sysmonwait)) {
+ runtime·atomicstore(&runtime·sched.sysmonwait, 0);
+ runtime·notewakeup(&runtime·sched.sysmonnote);
+ }
+ runtime·unlock(&runtime·sched.lock);
+ if(p) {
+ acquirep(p);
+ g->m->scalararg[0] = 1;
+ } else
+ g->m->scalararg[0] = 0;
+}
+
// runtime·exitsyscall slow path on g0.
// Failed to acquire P, enqueue gp as runnable.
static void
@@ -1707,9 +2077,9 @@ exitsyscall0(G *gp)
{
P *p;
- gp->status = Grunnable;
+ runtime·casgstatus(gp, Gsyscall, Grunnable);
dropg();
- runtime·lock(&runtime·sched);
+ runtime·lock(&runtime·sched.lock);
p = pidleget();
if(p == nil)
globrunqput(gp);
@@ -1717,7 +2087,7 @@ exitsyscall0(G *gp)
runtime·atomicstore(&runtime·sched.sysmonwait, 0);
runtime·notewakeup(&runtime·sched.sysmonnote);
}
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
if(p) {
acquirep(p);
execute(gp); // Never returns.
@@ -1731,42 +2101,65 @@ exitsyscall0(G *gp)
schedule(); // Never returns.
}
-// Called from syscall package before fork.
-#pragma textflag NOSPLIT
-void
-syscall·runtime_BeforeFork(void)
+static void
+beforefork(void)
{
+ G *gp;
+
+ gp = g->m->curg;
// Fork can hang if preempted with signals frequently enough (see issue 5517).
// Ensure that we stay on the same M where we disable profiling.
- g->m->locks++;
- if(g->m->profilehz != 0)
+ gp->m->locks++;
+ if(gp->m->profilehz != 0)
runtime·resetcpuprofiler(0);
// This function is called before fork in syscall package.
// Code between fork and exec must not allocate memory nor even try to grow stack.
// Here we spoil g->stackguard to reliably detect any attempts to grow stack.
// runtime_AfterFork will undo this in parent process, but not in child.
- g->m->forkstackguard = g->stackguard;
- g->stackguard0 = StackPreempt-1;
- g->stackguard = StackPreempt-1;
+ gp->m->forkstackguard = gp->stackguard;
+ gp->stackguard0 = StackPreempt-1;
+ gp->stackguard = StackPreempt-1;
}
-// Called from syscall package after fork in parent.
+// Called from syscall package before fork.
#pragma textflag NOSPLIT
void
-syscall·runtime_AfterFork(void)
+syscall·runtime_BeforeFork(void)
{
- int32 hz;
+ void (*fn)(void);
+
+ fn = beforefork;
+ runtime·onM(&fn);
+}
+static void
+afterfork(void)
+{
+ int32 hz;
+ G *gp;
+
+ gp = g->m->curg;
// See the comment in runtime_BeforeFork.
- g->stackguard0 = g->m->forkstackguard;
- g->stackguard = g->m->forkstackguard;
- g->m->forkstackguard = 0;
+ gp->stackguard0 = gp->m->forkstackguard;
+ gp->stackguard = gp->m->forkstackguard;
+ gp->m->forkstackguard = 0;
hz = runtime·sched.profilehz;
if(hz != 0)
runtime·resetcpuprofiler(hz);
- g->m->locks--;
+ gp->m->locks--;
+}
+
+// Called from syscall package after fork in parent.
+#pragma textflag NOSPLIT
+void
+syscall·runtime_AfterFork(void)
+{
+ void (*fn)(void);
+
+ fn = afterfork;
+ runtime·onM(&fn);
}
// Hook used by runtime·malg to call runtime·stackalloc on the
@@ -1792,6 +2185,7 @@ runtime·malg(int32 stacksize)
{
G *newg;
byte *stk;
+ void (*fn)(G*);
if(StackTop < sizeof(Stktop)) {
runtime·printf("runtime: SizeofStktop=%d, should be >=%d\n", (int32)StackTop, (int32)sizeof(Stktop));
@@ -1808,7 +2202,8 @@ runtime·malg(int32 stacksize)
// have to call stackalloc on scheduler stack.
newg->stacksize = stacksize;
g->param = newg;
- runtime·mcall(mstackalloc);
+ fn = mstackalloc;
+ runtime·mcall(&fn);
stk = g->param;
g->param = nil;
}
@@ -1820,24 +2215,50 @@ runtime·malg(int32 stacksize)
return newg;
}
+static void
+newproc_m(void)
+{
+ byte *argp;
+ void *callerpc;
+ FuncVal *fn;
+ int32 siz;
+
+ siz = g->m->scalararg[0];
+ callerpc = (void*)g->m->scalararg[1];
+ argp = g->m->ptrarg[0];
+ fn = (FuncVal*)g->m->ptrarg[1];
+
+ runtime·newproc1(fn, argp, siz, 0, callerpc);
+ g->m->ptrarg[0] = nil;
+ g->m->ptrarg[1] = nil;
+}
+
// Create a new g running fn with siz bytes of arguments.
// Put it on the queue of g's waiting to run.
// The compiler turns a go statement into a call to this.
// Cannot split the stack because it assumes that the arguments
// are available sequentially after &fn; they would not be
-// copied if a stack split occurred. It's OK for this to call
-// functions that split the stack.
+// copied if a stack split occurred.
#pragma textflag NOSPLIT
void
runtime·newproc(int32 siz, FuncVal* fn, ...)
{
byte *argp;
+ void (*mfn)(void);
if(thechar == '5' || thechar == '9')
argp = (byte*)(&fn+2); // skip caller's saved LR
else
argp = (byte*)(&fn+1);
- runtime·newproc1(fn, argp, siz, 0, runtime·getcallerpc(&siz));
+
+ g->m->locks++;
+ g->m->scalararg[0] = siz;
+ g->m->scalararg[1] = (uintptr)runtime·getcallerpc(&siz);
+ g->m->ptrarg[0] = argp;
+ g->m->ptrarg[1] = fn;
+ mfn = newproc_m;
+ runtime·onM(&mfn);
+ g->m->locks--;
}
// Create a new g running fn with narg bytes of arguments starting
@@ -1852,7 +2273,6 @@ runtime·newproc1(FuncVal *fn, byte *argp, int32 narg, int32 nret, void *callerp
P *p;
int32 siz;
-//runtime·printf("newproc1 %p %p narg=%d nret=%d\n", fn->fn, argp, narg, nret);
if(fn == nil) {
g->m->throwing = -1; // do not dump full stacks
runtime·throw("go of nil func value");
@@ -1874,9 +2294,13 @@ runtime·newproc1(FuncVal *fn, byte *argp, int32 narg, int32 nret, void *callerp
runtime·throw("invalid stack in newg");
} else {
newg = runtime·malg(StackMin);
- allgadd(newg);
+ runtime·casgstatus(newg, Gidle, Gdead);
+ allgadd(newg); // publishes with a g->status of Gdead so GC scanner doesn't look at uninitialized stack.
}
+ if(runtime·readgstatus(newg) != Gdead)
+ runtime·throw("newproc1: new g is not Gdead");
+
sp = (byte*)newg->stackbase;
sp -= siz;
runtime·memmove(sp, argp, narg);
@@ -1892,7 +2316,8 @@ runtime·newproc1(FuncVal *fn, byte *argp, int32 narg, int32 nret, void *callerp
newg->sched.g = newg;
runtime·gostartcallfn(&newg->sched, fn);
newg->gopc = (uintptr)callerpc;
- newg->status = Grunnable;
+ runtime·casgstatus(newg, Gdead, Grunnable);
+
if(p->goidcache == p->goidcacheend) {
// Sched.goidgen is the last allocated id,
// this batch must be [sched.goidgen+1, sched.goidgen+GoidCacheBatch].
@@ -1902,7 +2327,6 @@ runtime·newproc1(FuncVal *fn, byte *argp, int32 narg, int32 nret, void *callerp
p->goidcacheend = p->goidcache + GoidCacheBatch;
}
newg->goid = p->goidcache++;
- newg->panicwrap = 0;
if(raceenabled)
newg->racectx = runtime·racegostart((void*)callerpc);
runqput(p, newg);
@@ -1921,20 +2345,26 @@ allgadd(G *gp)
G **new;
uintptr cap;
+ if(runtime·readgstatus(gp) == Gidle)
+ runtime·throw("allgadd: bad status Gidle");
+
runtime·lock(&allglock);
if(runtime·allglen >= allgcap) {
cap = 4096/sizeof(new[0]);
if(cap < 2*allgcap)
cap = 2*allgcap;
- new = runtime·malloc(cap*sizeof(new[0]));
+ new = runtime·mallocgc(cap*sizeof(new[0]), nil, 0);
if(new == nil)
runtime·throw("runtime: cannot allocate memory");
if(runtime·allg != nil)
runtime·memmove(new, runtime·allg, runtime·allglen*sizeof(new[0]));
runtime·allg = new;
+ runtime·allgs.array = (void*)runtime·allg;
allgcap = cap;
+ runtime·allgs.cap = allgcap;
}
runtime·allg[runtime·allglen++] = gp;
+ runtime·allgs.len = runtime·allglen;
runtime·unlock(&allglock);
}
@@ -1946,6 +2376,9 @@ gfput(P *p, G *gp)
uintptr stksize;
Stktop *top;
+ if(runtime·readgstatus(gp) != Gdead)
+ runtime·throw("gfput: bad status (not Gdead)");
+
if(gp->stackguard - StackGuard != gp->stack0)
runtime·throw("invalid stack in gfput");
stksize = gp->stackbase + sizeof(Stktop) - gp->stack0;
@@ -1987,6 +2420,7 @@ gfget(P *p)
{
G *gp;
byte *stk;
+ void (*fn)(G*);
retry:
gp = p->gfree;
@@ -2014,7 +2448,8 @@ retry:
} else {
gp->stacksize = FixedStack;
g->param = gp;
- runtime·mcall(mstackalloc);
+ fn = mstackalloc;
+ runtime·mcall(&fn);
stk = g->param;
g->param = nil;
}
@@ -2054,28 +2489,26 @@ runtime·Breakpoint(void)
runtime·breakpoint();
}
-void
-runtime·Gosched(void)
-{
- runtime·gosched();
-}
-
// Implementation of runtime.GOMAXPROCS.
// delete when scheduler is even stronger
-int32
-runtime·gomaxprocsfunc(int32 n)
+void
+runtime·gomaxprocs_m(void)
{
- int32 ret;
+ int32 n, ret;
+
+ n = g->m->scalararg[0];
+ g->m->scalararg[0] = 0;
if(n > MaxGomaxprocs)
n = MaxGomaxprocs;
- runtime·lock(&runtime·sched);
+ runtime·lock(&runtime·sched.lock);
ret = runtime·gomaxprocs;
if(n <= 0 || n == ret) {
- runtime·unlock(&runtime·sched);
- return ret;
+ runtime·unlock(&runtime·sched.lock);
+ g->m->scalararg[0] = ret;
+ return;
}
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
runtime·semacquire(&runtime·worldsema, false);
g->m->gcing = 1;
@@ -2085,7 +2518,8 @@ runtime·gomaxprocsfunc(int32 n)
runtime·semrelease(&runtime·worldsema);
runtime·starttheworld();
- return ret;
+ g->m->scalararg[0] = ret;
+ return;
}
// lockOSThread is called by runtime.LockOSThread and runtime.lockOSThread below
@@ -2099,6 +2533,7 @@ lockOSThread(void)
g->lockedm = g->m;
}
+#pragma textflag NOSPLIT
void
runtime·LockOSThread(void)
{
@@ -2106,6 +2541,7 @@ runtime·LockOSThread(void)
lockOSThread();
}
+#pragma textflag NOSPLIT
void
runtime·lockOSThread(void)
{
@@ -2127,6 +2563,7 @@ unlockOSThread(void)
g->lockedm = nil;
}
+#pragma textflag NOSPLIT
void
runtime·UnlockOSThread(void)
{
@@ -2134,21 +2571,29 @@ runtime·UnlockOSThread(void)
unlockOSThread();
}
+static void badunlockOSThread(void);
+
+#pragma textflag NOSPLIT
void
runtime·unlockOSThread(void)
{
- if(g->m->locked < LockInternal)
- runtime·throw("runtime: internal error: misuse of lockOSThread/unlockOSThread");
+ void (*fn)(void);
+
+ if(g->m->locked < LockInternal) {
+ fn = badunlockOSThread;
+ runtime·onM(&fn);
+ }
g->m->locked -= LockInternal;
unlockOSThread();
}
-bool
-runtime·lockedOSThread(void)
+static void
+badunlockOSThread(void)
{
- return g->lockedm != nil && g->m->lockedg != nil;
+ runtime·throw("runtime: internal error: misuse of lockOSThread/unlockOSThread");
}
+#pragma textflag NOSPLIT
int32
runtime·gcount(void)
{
@@ -2171,37 +2616,16 @@ runtime·mcount(void)
return runtime·sched.mcount;
}
-void
-runtime·badmcall(void (*fn)(G*)) // called from assembly
-{
- USED(fn); // TODO: print fn?
- runtime·throw("runtime: mcall called on m->g0 stack");
-}
-
-void
-runtime·badmcall2(void (*fn)(G*)) // called from assembly
-{
- USED(fn);
- runtime·throw("runtime: mcall function returned");
-}
-
-void
-runtime·badreflectcall(void) // called from assembly
-{
- runtime·panicstring("runtime: arg size to reflect.call more than 1GB");
-}
-
static struct {
- Lock;
- void (*fn)(uintptr*, int32);
+ uint32 lock;
int32 hz;
- uintptr pcbuf[100];
} prof;
static void System(void) {}
static void ExternalCode(void) {}
static void GC(void) {}
-extern byte etext[];
+extern void runtime·cpuproftick(uintptr*, int32);
+extern byte runtime·etext[];
// Called if we receive a SIGPROF signal.
void
@@ -2212,11 +2636,12 @@ runtime·sigprof(uint8 *pc, uint8 *sp, uint8 *lr, G *gp, M *mp)
// Do not use global m in this function, use mp instead.
// On windows one m is sending reports about all the g's, so m means a wrong thing.
byte m;
+ uintptr stk[100];
m = 0;
USED(m);
- if(prof.fn == nil || prof.hz == 0)
+ if(prof.hz == 0)
return;
// Profiling runs concurrently with GC, so it must not allocate.
@@ -2300,15 +2725,9 @@ runtime·sigprof(uint8 *pc, uint8 *sp, uint8 *lr, G *gp, M *mp)
((uint8*)runtime·gogo <= pc && pc < (uint8*)runtime·gogo + RuntimeGogoBytes))
traceback = false;
- runtime·lock(&prof);
- if(prof.fn == nil) {
- runtime·unlock(&prof);
- mp->mallocing--;
- return;
- }
n = 0;
if(traceback)
- n = runtime·gentraceback((uintptr)pc, (uintptr)sp, (uintptr)lr, gp, 0, prof.pcbuf, nelem(prof.pcbuf), nil, nil, false);
+ n = runtime·gentraceback((uintptr)pc, (uintptr)sp, (uintptr)lr, gp, 0, stk, nelem(stk), nil, nil, false);
if(!traceback || n <= 0) {
// Normal traceback is impossible or has failed.
// See if it falls into several common cases.
@@ -2318,44 +2737,52 @@ runtime·sigprof(uint8 *pc, uint8 *sp, uint8 *lr, G *gp, M *mp)
// Cgo, we can't unwind and symbolize arbitrary C code,
// so instead collect Go stack that leads to the cgo call.
// This is especially important on windows, since all syscalls are cgo calls.
- n = runtime·gentraceback(mp->curg->syscallpc, mp->curg->syscallsp, 0, mp->curg, 0, prof.pcbuf, nelem(prof.pcbuf), nil, nil, false);
+ n = runtime·gentraceback(mp->curg->syscallpc, mp->curg->syscallsp, 0, mp->curg, 0, stk, nelem(stk), nil, nil, false);
}
#ifdef GOOS_windows
if(n == 0 && mp->libcallg != nil && mp->libcallpc != 0 && mp->libcallsp != 0) {
// Libcall, i.e. runtime syscall on windows.
// Collect Go stack that leads to the call.
- n = runtime·gentraceback(mp->libcallpc, mp->libcallsp, 0, mp->libcallg, 0, prof.pcbuf, nelem(prof.pcbuf), nil, nil, false);
+ n = runtime·gentraceback(mp->libcallpc, mp->libcallsp, 0, mp->libcallg, 0, stk, nelem(stk), nil, nil, false);
}
#endif
if(n == 0) {
// If all of the above has failed, account it against abstract "System" or "GC".
n = 2;
// "ExternalCode" is better than "etext".
- if((uintptr)pc > (uintptr)etext)
+ if((uintptr)pc > (uintptr)runtime·etext)
pc = (byte*)ExternalCode + PCQuantum;
- prof.pcbuf[0] = (uintptr)pc;
+ stk[0] = (uintptr)pc;
if(mp->gcing || mp->helpgc)
- prof.pcbuf[1] = (uintptr)GC + PCQuantum;
+ stk[1] = (uintptr)GC + PCQuantum;
else
- prof.pcbuf[1] = (uintptr)System + PCQuantum;
+ stk[1] = (uintptr)System + PCQuantum;
}
}
- prof.fn(prof.pcbuf, n);
- runtime·unlock(&prof);
+
+ if(prof.hz != 0) {
+ // Simple cas-lock to coordinate with setcpuprofilerate.
+ while(!runtime·cas(&prof.lock, 0, 1))
+ runtime·osyield();
+ if(prof.hz != 0)
+ runtime·cpuproftick(stk, n);
+ runtime·atomicstore(&prof.lock, 0);
+ }
mp->mallocing--;
}
// Arrange to call fn with a traceback hz times a second.
void
-runtime·setcpuprofilerate(void (*fn)(uintptr*, int32), int32 hz)
+runtime·setcpuprofilerate_m(void)
{
+ int32 hz;
+
+ hz = g->m->scalararg[0];
+ g->m->scalararg[0] = 0;
+
// Force sane arguments.
if(hz < 0)
hz = 0;
- if(hz == 0)
- fn = nil;
- if(fn == nil)
- hz = 0;
// Disable preemption, otherwise we can be rescheduled to another thread
// that has profiling enabled.
@@ -2366,13 +2793,14 @@ runtime·setcpuprofilerate(void (*fn)(uintptr*, int32), int32 hz)
// it would deadlock.
runtime·resetcpuprofiler(0);
- runtime·lock(&prof);
- prof.fn = fn;
+ while(!runtime·cas(&prof.lock, 0, 1))
+ runtime·osyield();
prof.hz = hz;
- runtime·unlock(&prof);
- runtime·lock(&runtime·sched);
+ runtime·atomicstore(&prof.lock, 0);
+
+ runtime·lock(&runtime·sched.lock);
runtime·sched.profilehz = hz;
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
if(hz != 0)
runtime·resetcpuprofiler(hz);
@@ -2396,7 +2824,7 @@ procresize(int32 new)
for(i = 0; i < new; i++) {
p = runtime·allp[i];
if(p == nil) {
- p = (P*)runtime·mallocgc(sizeof(*p), 0, FlagNoInvokeGC);
+ p = (P*)runtime·mallocgc(sizeof(*p), 0, 0);
p->id = i;
p->status = Pgcstop;
runtime·atomicstorep(&runtime·allp[i], p);
@@ -2510,11 +2938,11 @@ releasep(void)
static void
incidlelocked(int32 v)
{
- runtime·lock(&runtime·sched);
+ runtime·lock(&runtime·sched.lock);
runtime·sched.nmidlelocked += v;
if(v > 0)
checkdead();
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
}
// Check for deadlock situation.
@@ -2545,15 +2973,20 @@ checkdead(void)
runtime·lock(&allglock);
for(i = 0; i < runtime·allglen; i++) {
gp = runtime·allg[i];
- if(gp->isbackground)
+ if(gp->issystem)
continue;
- s = gp->status;
- if(s == Gwaiting)
+ s = runtime·readgstatus(gp);
+ switch(s&~Gscan) {
+ case Gwaiting:
grunning++;
- else if(s == Grunnable || s == Grunning || s == Gsyscall) {
+ break;
+ case Grunnable:
+ case Grunning:
+ case Gsyscall:
runtime·unlock(&allglock);
runtime·printf("runtime: checkdead: find g %D in status %d\n", gp->goid, s);
runtime·throw("checkdead: runnable g");
+ break;
}
}
runtime·unlock(&allglock);
@@ -2566,10 +2999,28 @@ checkdead(void)
static void
sysmon(void)
{
- uint32 idle, delay;
- int64 now, lastpoll, lasttrace;
+ uint32 idle, delay, nscavenge;
+ int64 now, unixnow, lastpoll, lasttrace, lastgc;
+ int64 forcegcperiod, scavengelimit, lastscavenge, maxsleep;
G *gp;
+ // If we go two minutes without a garbage collection, force one to run.
+ forcegcperiod = 2*60*1e9;
+ // If a heap span goes unused for 5 minutes after a garbage collection,
+ // we hand it back to the operating system.
+ scavengelimit = 5*60*1e9;
+ if(runtime·debug.scavenge > 0) {
+ // Scavenge-a-lot for testing.
+ forcegcperiod = 10*1e6;
+ scavengelimit = 20*1e6;
+ }
+ lastscavenge = runtime·nanotime();
+ nscavenge = 0;
+ // Make wake-up period small enough for the sampling to be correct.
+ maxsleep = forcegcperiod/2;
+ if(scavengelimit < forcegcperiod)
+ maxsleep = scavengelimit/2;
+
lasttrace = 0;
idle = 0; // how many cycles in succession we had not wokeup somebody
delay = 0;
@@ -2583,20 +3034,23 @@ sysmon(void)
runtime·usleep(delay);
if(runtime·debug.schedtrace <= 0 &&
(runtime·sched.gcwaiting || runtime·atomicload(&runtime·sched.npidle) == runtime·gomaxprocs)) { // TODO: fast atomic
- runtime·lock(&runtime·sched);
+ runtime·lock(&runtime·sched.lock);
if(runtime·atomicload(&runtime·sched.gcwaiting) || runtime·atomicload(&runtime·sched.npidle) == runtime·gomaxprocs) {
runtime·atomicstore(&runtime·sched.sysmonwait, 1);
- runtime·unlock(&runtime·sched);
- runtime·notesleep(&runtime·sched.sysmonnote);
+ runtime·unlock(&runtime·sched.lock);
+ runtime·notetsleep(&runtime·sched.sysmonnote, maxsleep);
+ runtime·lock(&runtime·sched.lock);
+ runtime·atomicstore(&runtime·sched.sysmonwait, 0);
runtime·noteclear(&runtime·sched.sysmonnote);
idle = 0;
delay = 20;
- } else
- runtime·unlock(&runtime·sched);
+ }
+ runtime·unlock(&runtime·sched.lock);
}
// poll network if not polled for more than 10ms
lastpoll = runtime·atomicload64(&runtime·sched.lastpoll);
now = runtime·nanotime();
+ unixnow = runtime·unixnanotime();
if(lastpoll != 0 && lastpoll + 10*1000*1000 < now) {
runtime·cas64(&runtime·sched.lastpoll, lastpoll, now);
gp = runtime·netpoll(false); // non-blocking
@@ -2620,6 +3074,23 @@ sysmon(void)
else
idle++;
+ // check if we need to force a GC
+ lastgc = runtime·atomicload64(&mstats.last_gc);
+ if(lastgc != 0 && unixnow - lastgc > forcegcperiod && runtime·atomicload(&runtime·forcegc.idle)) {
+ runtime·lock(&runtime·forcegc.lock);
+ runtime·forcegc.idle = 0;
+ runtime·forcegc.g->schedlink = nil;
+ injectglist(runtime·forcegc.g);
+ runtime·unlock(&runtime·forcegc.lock);
+ }
+
+ // scavenge heap once in a while
+ if(lastscavenge + scavengelimit/2 < now) {
+ runtime·MHeap_Scavenge(nscavenge, now, scavengelimit);
+ lastscavenge = now;
+ nscavenge++;
+ }
+
if(runtime·debug.schedtrace > 0 && lasttrace + runtime·debug.schedtrace*1000000ll <= now) {
lasttrace = now;
runtime·schedtrace(runtime·debug.scheddetail);
@@ -2723,6 +3194,9 @@ preemptall(void)
// simultaneously executing runtime·newstack.
// No lock needs to be held.
// Returns true if preemption request was issued.
+// The actual preemption will happen at some point in the future
+// and will be indicated by the gp->status no longer being
+// Grunning
static bool
preemptone(P *p)
{
@@ -2736,6 +3210,10 @@ preemptone(P *p)
if(gp == nil || gp == mp->g0)
return false;
gp->preempt = true;
+ // Every call in a go routine checks for stack overflow by
+ // comparing the current stack pointer to gp->stackguard0.
+ // Setting gp->stackguard0 to StackPreempt folds
+ // preemption into the normal stack overflow check.
gp->stackguard0 = StackPreempt;
return true;
}
@@ -2757,7 +3235,7 @@ runtime·schedtrace(bool detailed)
if(starttime == 0)
starttime = now;
- runtime·lock(&runtime·sched);
+ runtime·lock(&runtime·sched.lock);
runtime·printf("SCHED %Dms: gomaxprocs=%d idleprocs=%d threads=%d spinningthreads=%d idlethreads=%d runqueue=%d",
(now-starttime)/1000000, runtime·gomaxprocs, runtime·sched.npidle, runtime·sched.mcount,
runtime·sched.nmspinning, runtime·sched.nmidle, runtime·sched.runqsize);
@@ -2793,7 +3271,7 @@ runtime·schedtrace(bool detailed)
}
}
if(!detailed) {
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
return;
}
for(mp = runtime·allm; mp; mp = mp->alllink) {
@@ -2820,12 +3298,12 @@ runtime·schedtrace(bool detailed)
gp = runtime·allg[gi];
mp = gp->m;
lockedm = gp->lockedm;
- runtime·printf(" G%D: status=%d(%s) m=%d lockedm=%d\n",
- gp->goid, gp->status, gp->waitreason, mp ? mp->id : -1,
+ runtime·printf(" G%D: status=%d(%S) m=%d lockedm=%d\n",
+ gp->goid, runtime·readgstatus(gp), gp->waitreason, mp ? mp->id : -1,
lockedm ? lockedm->id : -1);
}
runtime·unlock(&allglock);
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
}
// Put mp on midle list.
@@ -2981,9 +3459,9 @@ runqputslow(P *p, G *gp, uint32 h, uint32 t)
for(i=0; i<n; i++)
batch[i]->schedlink = batch[i+1];
// Now put the batch on global queue.
- runtime·lock(&runtime·sched);
+ runtime·lock(&runtime·sched.lock);
globrunqputbatch(batch[0], batch[n], n+1);
- runtime·unlock(&runtime·sched);
+ runtime·unlock(&runtime·sched.lock);
return true;
}
@@ -3061,24 +3539,25 @@ runqsteal(P *p, P *p2)
void
runtime·testSchedLocalQueue(void)
{
- P p;
- G gs[nelem(p.runq)];
+ P *p;
+ G *gs;
int32 i, j;
- runtime·memclr((byte*)&p, sizeof(p));
+ p = (P*)runtime·mallocgc(sizeof(*p), nil, FlagNoScan);
+ gs = (G*)runtime·mallocgc(nelem(p->runq)*sizeof(*gs), nil, FlagNoScan);
- for(i = 0; i < nelem(gs); i++) {
- if(runqget(&p) != nil)
+ for(i = 0; i < nelem(p->runq); i++) {
+ if(runqget(p) != nil)
runtime·throw("runq is not empty initially");
for(j = 0; j < i; j++)
- runqput(&p, &gs[i]);
+ runqput(p, &gs[i]);
for(j = 0; j < i; j++) {
- if(runqget(&p) != &gs[i]) {
+ if(runqget(p) != &gs[i]) {
runtime·printf("bad element at iter %d/%d\n", i, j);
runtime·throw("bad element");
}
}
- if(runqget(&p) != nil)
+ if(runqget(p) != nil)
runtime·throw("runq is not empty afterwards");
}
}
@@ -3086,29 +3565,30 @@ runtime·testSchedLocalQueue(void)
void
runtime·testSchedLocalQueueSteal(void)
{
- P p1, p2;
- G gs[nelem(p1.runq)], *gp;
+ P *p1, *p2;
+ G *gs, *gp;
int32 i, j, s;
- runtime·memclr((byte*)&p1, sizeof(p1));
- runtime·memclr((byte*)&p2, sizeof(p2));
+ p1 = (P*)runtime·mallocgc(sizeof(*p1), nil, FlagNoScan);
+ p2 = (P*)runtime·mallocgc(sizeof(*p2), nil, FlagNoScan);
+ gs = (G*)runtime·mallocgc(nelem(p1->runq)*sizeof(*gs), nil, FlagNoScan);
- for(i = 0; i < nelem(gs); i++) {
+ for(i = 0; i < nelem(p1->runq); i++) {
for(j = 0; j < i; j++) {
gs[j].sig = 0;
- runqput(&p1, &gs[j]);
+ runqput(p1, &gs[j]);
}
- gp = runqsteal(&p2, &p1);
+ gp = runqsteal(p2, p1);
s = 0;
if(gp) {
s++;
gp->sig++;
}
- while(gp = runqget(&p2)) {
+ while(gp = runqget(p2)) {
s++;
gp->sig++;
}
- while(gp = runqget(&p1))
+ while(gp = runqget(p1))
gp->sig++;
for(j = 0; j < i; j++) {
if(gs[j].sig != 1) {
@@ -3124,34 +3604,21 @@ runtime·testSchedLocalQueueSteal(void)
}
}
-extern void runtime·morestack(void);
-uintptr runtime·externalthreadhandlerp;
-
-// Does f mark the top of a goroutine stack?
-bool
-runtime·topofstack(Func *f)
-{
- return f->entry == (uintptr)runtime·goexit ||
- f->entry == (uintptr)runtime·mstart ||
- f->entry == (uintptr)runtime·mcall ||
- f->entry == (uintptr)runtime·onM ||
- f->entry == (uintptr)runtime·morestack ||
- f->entry == (uintptr)runtime·lessstack ||
- f->entry == (uintptr)_rt0_go ||
- (runtime·externalthreadhandlerp != 0 && f->entry == runtime·externalthreadhandlerp);
-}
-
-int32
-runtime·setmaxthreads(int32 in)
+void
+runtime·setmaxthreads_m(void)
{
+ int32 in;
int32 out;
- runtime·lock(&runtime·sched);
+ in = g->m->scalararg[0];
+
+ runtime·lock(&runtime·sched.lock);
out = runtime·sched.maxmcount;
runtime·sched.maxmcount = in;
checkmcount();
- runtime·unlock(&runtime·sched);
- return out;
+ runtime·unlock(&runtime·sched.lock);
+
+ g->m->scalararg[0] = out;
}
static int8 experiment[] = GOEXPERIMENT; // defined in zaexperiment.h
@@ -3174,3 +3641,23 @@ haveexperiment(int8 *name)
}
return 0;
}
+
+#pragma textflag NOSPLIT
+void
+sync·runtime_procPin(intptr p)
+{
+ M *mp;
+
+ mp = g->m;
+ // Disable preemption.
+ mp->locks++;
+ p = mp->p->id;
+ FLUSH(&p);
+}
+
+#pragma textflag NOSPLIT
+void
+sync·runtime_procUnpin()
+{
+ g->m->locks--;
+}
diff --git a/src/pkg/runtime/proc.go b/src/pkg/runtime/proc.go
new file mode 100644
index 0000000000..48b8cbe394
--- /dev/null
+++ b/src/pkg/runtime/proc.go
@@ -0,0 +1,123 @@
+// 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"
+
+var parkunlock_c byte
+
+// start forcegc helper goroutine
+func init() {
+ go forcegchelper()
+}
+
+func forcegchelper() {
+ forcegc.g = getg()
+ forcegc.g.issystem = true
+ for {
+ lock(&forcegc.lock)
+ if forcegc.idle != 0 {
+ gothrow("forcegc: phase error")
+ }
+ atomicstore(&forcegc.idle, 1)
+ goparkunlock(&forcegc.lock, "force gc (idle)")
+ // this goroutine is explicitly resumed by sysmon
+ if debug.gctrace > 0 {
+ println("GC forced")
+ }
+ gogc(1)
+ }
+}
+
+// Gosched yields the processor, allowing other goroutines to run. It does not
+// suspend the current goroutine, so execution resumes automatically.
+func Gosched() {
+ mcall(gosched_m)
+}
+
+// Puts the current goroutine into a waiting state and calls unlockf.
+// If unlockf returns false, the goroutine is resumed.
+func gopark(unlockf unsafe.Pointer, lock unsafe.Pointer, reason string) {
+ mp := acquirem()
+ gp := mp.curg
+ status := readgstatus(gp)
+ if status != _Grunning && status != _Gscanrunning {
+ gothrow("gopark: bad g status")
+ }
+ mp.waitlock = lock
+ mp.waitunlockf = unlockf
+ gp.waitreason = reason
+ releasem(mp)
+ // can't do anything that might move the G between Ms here.
+ mcall(park_m)
+}
+
+// Puts the current goroutine into a waiting state and unlocks the lock.
+// The goroutine can be made runnable again by calling goready(gp).
+func goparkunlock(lock *mutex, reason string) {
+ gopark(unsafe.Pointer(&parkunlock_c), unsafe.Pointer(lock), reason)
+}
+
+func goready(gp *g) {
+ mp := acquirem()
+ mp.ptrarg[0] = unsafe.Pointer(gp)
+ onM(ready_m)
+ releasem(mp)
+}
+
+//go:nosplit
+func acquireSudog() *sudog {
+ c := gomcache()
+ s := c.sudogcache
+ if s != nil {
+ c.sudogcache = s.next
+ return s
+ }
+
+ // Delicate dance: the semaphore implementation calls
+ // acquireSudog, acquireSudog calls new(sudog),
+ // new calls malloc, malloc can call the garbage collector,
+ // and the garbage collector calls the semaphore implementation
+ // in stoptheworld.
+ // Break the cycle by doing acquirem/releasem around new(sudog).
+ // The acquirem/releasem increments m.locks during new(sudog),
+ // which keeps the garbage collector from being invoked.
+ mp := acquirem()
+ p := new(sudog)
+ releasem(mp)
+ return p
+}
+
+//go:nosplit
+func releaseSudog(s *sudog) {
+ c := gomcache()
+ s.next = c.sudogcache
+ c.sudogcache = s
+}
+
+// funcPC returns the entry PC of the function f.
+// It assumes that f is a func value. Otherwise the behavior is undefined.
+//go:nosplit
+func funcPC(f interface{}) uintptr {
+ return **(**uintptr)(add(unsafe.Pointer(&f), ptrSize))
+}
+
+// called from assembly
+func badmcall(fn func(*g)) {
+ gothrow("runtime: mcall called on m->g0 stack")
+}
+
+func badmcall2(fn func(*g)) {
+ gothrow("runtime: mcall function returned")
+}
+
+func badreflectcall() {
+ panic("runtime: arg size to reflect.call more than 1GB")
+}
+
+func lockedOSThread() bool {
+ gp := getg()
+ return gp.lockedm != nil && gp.m.lockedg != nil
+}
diff --git a/src/pkg/runtime/proc_test.go b/src/pkg/runtime/proc_test.go
index 1f597f58e4..aa9bc81ac4 100644
--- a/src/pkg/runtime/proc_test.go
+++ b/src/pkg/runtime/proc_test.go
@@ -366,11 +366,11 @@ func nonleaf(stop chan int) bool {
}
func TestSchedLocalQueue(t *testing.T) {
- runtime.TestSchedLocalQueue1()
+ runtime.RunSchedLocalQueueTest()
}
func TestSchedLocalQueueSteal(t *testing.T) {
- runtime.TestSchedLocalQueueSteal1()
+ runtime.RunSchedLocalQueueStealTest()
}
func benchmarkStackGrowth(b *testing.B, rec int) {
diff --git a/src/pkg/runtime/race.c b/src/pkg/runtime/race.c
index 12cc6a0dd8..9ac73fbccf 100644
--- a/src/pkg/runtime/race.c
+++ b/src/pkg/runtime/race.c
@@ -11,6 +11,7 @@
#include "race.h"
#include "type.h"
#include "typekind.h"
+#include "textflag.h"
// Race runtime functions called via runtime·racecall.
void __tsan_init(void);
@@ -23,6 +24,8 @@ void __tsan_malloc(void);
void __tsan_acquire(void);
void __tsan_release(void);
void __tsan_release_merge(void);
+void __tsan_go_ignore_sync_begin(void);
+void __tsan_go_ignore_sync_end(void);
// Mimic what cmd/cgo would do.
#pragma cgo_import_static __tsan_init
@@ -35,6 +38,8 @@ void __tsan_release_merge(void);
#pragma cgo_import_static __tsan_acquire
#pragma cgo_import_static __tsan_release
#pragma cgo_import_static __tsan_release_merge
+#pragma cgo_import_static __tsan_go_ignore_sync_begin
+#pragma cgo_import_static __tsan_go_ignore_sync_end
// These are called from race_amd64.s.
#pragma cgo_import_static __tsan_read
@@ -46,8 +51,19 @@ void __tsan_release_merge(void);
#pragma cgo_import_static __tsan_func_enter
#pragma cgo_import_static __tsan_func_exit
-extern byte noptrdata[];
-extern byte enoptrbss[];
+#pragma cgo_import_static __tsan_go_atomic32_load
+#pragma cgo_import_static __tsan_go_atomic64_load
+#pragma cgo_import_static __tsan_go_atomic32_store
+#pragma cgo_import_static __tsan_go_atomic64_store
+#pragma cgo_import_static __tsan_go_atomic32_exchange
+#pragma cgo_import_static __tsan_go_atomic64_exchange
+#pragma cgo_import_static __tsan_go_atomic32_fetch_add
+#pragma cgo_import_static __tsan_go_atomic64_fetch_add
+#pragma cgo_import_static __tsan_go_atomic32_compare_exchange
+#pragma cgo_import_static __tsan_go_atomic64_compare_exchange
+
+extern byte runtime·noptrdata[];
+extern byte runtime·enoptrbss[];
// start/end of heap for race_amd64.s
uintptr runtime·racearenastart;
@@ -64,16 +80,18 @@ void runtime·racesymbolizethunk(void*);
void runtime·racecall(void(*f)(void), ...);
// checks if the address has shadow (i.e. heap or data/bss)
+#pragma textflag NOSPLIT
static bool
isvalidaddr(uintptr addr)
{
if(addr >= runtime·racearenastart && addr < runtime·racearenaend)
return true;
- if(addr >= (uintptr)noptrdata && addr < (uintptr)enoptrbss)
+ if(addr >= (uintptr)runtime·noptrdata && addr < (uintptr)runtime·enoptrbss)
return true;
return false;
}
+#pragma textflag NOSPLIT
uintptr
runtime·raceinit(void)
{
@@ -84,18 +102,20 @@ runtime·raceinit(void)
runtime·throw("raceinit: race build must use cgo");
runtime·racecall(__tsan_init, &racectx, runtime·racesymbolizethunk);
// Round data segment to page boundaries, because it's used in mmap().
- start = (uintptr)noptrdata & ~(PageSize-1);
- size = ROUND((uintptr)enoptrbss - start, PageSize);
+ start = (uintptr)runtime·noptrdata & ~(PageSize-1);
+ size = ROUND((uintptr)runtime·enoptrbss - start, PageSize);
runtime·racecall(__tsan_map_shadow, start, size);
return racectx;
}
+#pragma textflag NOSPLIT
void
runtime·racefini(void)
{
runtime·racecall(__tsan_fini);
}
+#pragma textflag NOSPLIT
void
runtime·racemapshadow(void *addr, uintptr size)
{
@@ -106,30 +126,45 @@ runtime·racemapshadow(void *addr, uintptr size)
runtime·racecall(__tsan_map_shadow, addr, size);
}
+#pragma textflag NOSPLIT
void
runtime·racemalloc(void *p, uintptr sz)
{
runtime·racecall(__tsan_malloc, p, sz);
}
+#pragma textflag NOSPLIT
uintptr
runtime·racegostart(void *pc)
{
uintptr racectx;
+ G *spawng;
- runtime·racecall(__tsan_go_start, g->racectx, &racectx, pc);
+ if(g->m->curg != nil)
+ spawng = g->m->curg;
+ else
+ spawng = g;
+
+ runtime·racecall(__tsan_go_start, spawng->racectx, &racectx, pc);
return racectx;
}
+#pragma textflag NOSPLIT
void
runtime·racegoend(void)
{
runtime·racecall(__tsan_go_end, g->racectx);
}
+#pragma textflag NOSPLIT
void
runtime·racewriterangepc(void *addr, uintptr sz, void *callpc, void *pc)
{
+ if(g != g->m->curg) {
+ // The call is coming from manual instrumentation of Go code running on g0/gsignal.
+ // Not interesting.
+ return;
+ }
if(callpc != nil)
runtime·racefuncenter(callpc);
runtime·racewriterangepc1(addr, sz, pc);
@@ -137,9 +172,15 @@ runtime·racewriterangepc(void *addr, uintptr sz, void *callpc, void *pc)
runtime·racefuncexit();
}
+#pragma textflag NOSPLIT
void
runtime·racereadrangepc(void *addr, uintptr sz, void *callpc, void *pc)
{
+ if(g != g->m->curg) {
+ // The call is coming from manual instrumentation of Go code running on g0/gsignal.
+ // Not interesting.
+ return;
+ }
if(callpc != nil)
runtime·racefuncenter(callpc);
runtime·racereadrangepc1(addr, sz, pc);
@@ -147,6 +188,7 @@ runtime·racereadrangepc(void *addr, uintptr sz, void *callpc, void *pc)
runtime·racefuncexit();
}
+#pragma textflag NOSPLIT
void
runtime·racewriteobjectpc(void *addr, Type *t, void *callpc, void *pc)
{
@@ -159,6 +201,7 @@ runtime·racewriteobjectpc(void *addr, Type *t, void *callpc, void *pc)
runtime·racewritepc(addr, callpc, pc);
}
+#pragma textflag NOSPLIT
void
runtime·racereadobjectpc(void *addr, Type *t, void *callpc, void *pc)
{
@@ -171,12 +214,14 @@ runtime·racereadobjectpc(void *addr, Type *t, void *callpc, void *pc)
runtime·racereadpc(addr, callpc, pc);
}
+#pragma textflag NOSPLIT
void
runtime·raceacquire(void *addr)
{
runtime·raceacquireg(g, addr);
}
+#pragma textflag NOSPLIT
void
runtime·raceacquireg(G *gp, void *addr)
{
@@ -185,6 +230,7 @@ runtime·raceacquireg(G *gp, void *addr)
runtime·racecall(__tsan_acquire, gp->racectx, addr);
}
+#pragma textflag NOSPLIT
void
runtime·racerelease(void *addr)
{
@@ -193,6 +239,7 @@ runtime·racerelease(void *addr)
runtime·racereleaseg(g, addr);
}
+#pragma textflag NOSPLIT
void
runtime·racereleaseg(G *gp, void *addr)
{
@@ -201,12 +248,14 @@ runtime·racereleaseg(G *gp, void *addr)
runtime·racecall(__tsan_release, gp->racectx, addr);
}
+#pragma textflag NOSPLIT
void
runtime·racereleasemerge(void *addr)
{
runtime·racereleasemergeg(g, addr);
}
+#pragma textflag NOSPLIT
void
runtime·racereleasemergeg(G *gp, void *addr)
{
@@ -215,6 +264,7 @@ runtime·racereleasemergeg(G *gp, void *addr)
runtime·racecall(__tsan_release_merge, gp->racectx, addr);
}
+#pragma textflag NOSPLIT
void
runtime·racefingo(void)
{
@@ -222,6 +272,7 @@ runtime·racefingo(void)
}
// func RaceAcquire(addr unsafe.Pointer)
+#pragma textflag NOSPLIT
void
runtime·RaceAcquire(void *addr)
{
@@ -229,6 +280,7 @@ runtime·RaceAcquire(void *addr)
}
// func RaceRelease(addr unsafe.Pointer)
+#pragma textflag NOSPLIT
void
runtime·RaceRelease(void *addr)
{
@@ -236,70 +288,27 @@ runtime·RaceRelease(void *addr)
}
// func RaceReleaseMerge(addr unsafe.Pointer)
+#pragma textflag NOSPLIT
void
runtime·RaceReleaseMerge(void *addr)
{
runtime·racereleasemerge(addr);
}
-// func RaceSemacquire(s *uint32)
-void
-runtime·RaceSemacquire(uint32 *s)
-{
- runtime·semacquire(s, false);
-}
-
-// func RaceSemrelease(s *uint32)
-void
-runtime·RaceSemrelease(uint32 *s)
-{
- runtime·semrelease(s);
-}
-
// func RaceDisable()
+#pragma textflag NOSPLIT
void
runtime·RaceDisable(void)
{
- g->raceignore++;
+ if(g->raceignore++ == 0)
+ runtime·racecall(__tsan_go_ignore_sync_begin, g->racectx);
}
// func RaceEnable()
+#pragma textflag NOSPLIT
void
runtime·RaceEnable(void)
{
- g->raceignore--;
-}
-
-typedef struct SymbolizeContext SymbolizeContext;
-struct SymbolizeContext
-{
- uintptr pc;
- int8* func;
- int8* file;
- uintptr line;
- uintptr off;
- uintptr res;
-};
-
-// Callback from C into Go, runs on g0.
-void
-runtime·racesymbolize(SymbolizeContext *ctx)
-{
- Func *f;
- String file;
-
- f = runtime·findfunc(ctx->pc);
- if(f == nil) {
- ctx->func = "??";
- ctx->file = "-";
- ctx->line = 0;
- ctx->off = ctx->pc;
- ctx->res = 1;
- return;
- }
- ctx->func = runtime·funcname(f);
- ctx->line = runtime·funcline(f, ctx->pc, &file);
- ctx->file = (int8*)file.str; // assume zero-terminated
- ctx->off = ctx->pc - f->entry;
- ctx->res = 1;
+ if(--g->raceignore == 0)
+ runtime·racecall(__tsan_go_ignore_sync_end, g->racectx);
}
diff --git a/src/pkg/runtime/race.go b/src/pkg/runtime/race.go
index 3707549a3f..c7573517dc 100644
--- a/src/pkg/runtime/race.go
+++ b/src/pkg/runtime/race.go
@@ -38,10 +38,88 @@ func raceReadObjectPC(t *_type, addr unsafe.Pointer, callerpc, pc uintptr) {
if kind == kindArray || kind == kindStruct {
// for composite objects we have to read every address
// because a write might happen to any subobject.
- racereadrangepc(addr, int(t.size), callerpc, pc)
+ racereadrangepc(addr, t.size, callerpc, pc)
} else {
// for non-composite objects we can read just the start
// address, as any write must write the first byte.
racereadpc(addr, callerpc, pc)
}
}
+
+func raceWriteObjectPC(t *_type, addr unsafe.Pointer, callerpc, pc uintptr) {
+ kind := t.kind & kindMask
+ if kind == kindArray || kind == kindStruct {
+ // for composite objects we have to write every address
+ // because a write might happen to any subobject.
+ racewriterangepc(addr, t.size, callerpc, pc)
+ } else {
+ // for non-composite objects we can write just the start
+ // address, as any write must write the first byte.
+ racewritepc(addr, callerpc, pc)
+ }
+}
+
+//go:noescape
+func racereadpc(addr unsafe.Pointer, callpc, pc uintptr)
+
+//go:noescape
+func racewritepc(addr unsafe.Pointer, callpc, pc uintptr)
+
+//go:noescape
+func racereadrangepc(addr unsafe.Pointer, len uintptr, callpc, pc uintptr)
+
+//go:noescape
+func racewriterangepc(addr unsafe.Pointer, len uintptr, callpc, pc uintptr)
+
+//go:noescape
+func raceacquire(addr unsafe.Pointer)
+
+//go:noescape
+func racerelease(addr unsafe.Pointer)
+
+//go:noescape
+func raceacquireg(gp *g, addr unsafe.Pointer)
+
+//go:noescape
+func racereleaseg(gp *g, addr unsafe.Pointer)
+
+func racefingo()
+
+//go:noescape
+func racemalloc(p unsafe.Pointer, size uintptr)
+
+//go:noescape
+func racereleasemerge(addr unsafe.Pointer)
+
+type symbolizeContext struct {
+ pc uintptr
+ fn *byte
+ file *byte
+ line uintptr
+ off uintptr
+ res uintptr
+}
+
+var qq = [...]byte{'?', '?', 0}
+var dash = [...]byte{'-', 0}
+
+// Callback from C into Go, runs on g0.
+func racesymbolize(ctx *symbolizeContext) {
+ f := findfunc(ctx.pc)
+ if f == nil {
+ ctx.fn = &qq[0]
+ ctx.file = &dash[0]
+ ctx.line = 0
+ ctx.off = ctx.pc
+ ctx.res = 1
+ return
+ }
+
+ ctx.fn = funcname(f)
+ var file string
+ ctx.line = uintptr(funcline(f, ctx.pc, &file))
+ ctx.file = &bytes(file)[0] // assume NUL-terminated
+ ctx.off = ctx.pc - f.entry
+ ctx.res = 1
+ return
+}
diff --git a/src/pkg/runtime/race/README b/src/pkg/runtime/race/README
index 6a4259141e..7f185359fe 100644
--- a/src/pkg/runtime/race/README
+++ b/src/pkg/runtime/race/README
@@ -9,4 +9,4 @@ $ ./buildgo.sh
Tested with gcc 4.6.1 and 4.7.0. On Windows it's built with 64-bit MinGW.
-Current runtime is built on rev 210365.
+Current runtime is built on rev 215000.
diff --git a/src/pkg/runtime/race/race_darwin_amd64.syso b/src/pkg/runtime/race/race_darwin_amd64.syso
index 9061ce0aa1..81b48c6c94 100644
--- a/src/pkg/runtime/race/race_darwin_amd64.syso
+++ b/src/pkg/runtime/race/race_darwin_amd64.syso
Binary files differ
diff --git a/src/pkg/runtime/race/race_freebsd_amd64.syso b/src/pkg/runtime/race/race_freebsd_amd64.syso
index b25d868f48..5bbe322299 100644
--- a/src/pkg/runtime/race/race_freebsd_amd64.syso
+++ b/src/pkg/runtime/race/race_freebsd_amd64.syso
Binary files differ
diff --git a/src/pkg/runtime/race/race_linux_amd64.syso b/src/pkg/runtime/race/race_linux_amd64.syso
index 8fd77ae58b..49bf08ef38 100644
--- a/src/pkg/runtime/race/race_linux_amd64.syso
+++ b/src/pkg/runtime/race/race_linux_amd64.syso
Binary files differ
diff --git a/src/pkg/runtime/race/race_windows_amd64.syso b/src/pkg/runtime/race/race_windows_amd64.syso
index 3ea80a6657..a4eae9bdd9 100644
--- a/src/pkg/runtime/race/race_windows_amd64.syso
+++ b/src/pkg/runtime/race/race_windows_amd64.syso
Binary files differ
diff --git a/src/pkg/runtime/race/testdata/atomic_test.go b/src/pkg/runtime/race/testdata/atomic_test.go
index fc569b96cb..232744b3dd 100644
--- a/src/pkg/runtime/race/testdata/atomic_test.go
+++ b/src/pkg/runtime/race/testdata/atomic_test.go
@@ -225,8 +225,7 @@ func TestNoRaceAtomicStoreCASUint64(t *testing.T) {
x = 1
}
-// Races with non-atomic loads are not detected.
-func TestRaceFailingAtomicStoreLoad(t *testing.T) {
+func TestRaceAtomicStoreLoad(t *testing.T) {
c := make(chan bool)
var a uint64
go func() {
@@ -248,8 +247,7 @@ func TestRaceAtomicLoadStore(t *testing.T) {
<-c
}
-// Races with non-atomic loads are not detected.
-func TestRaceFailingAtomicAddLoad(t *testing.T) {
+func TestRaceAtomicAddLoad(t *testing.T) {
c := make(chan bool)
var a uint64
go func() {
diff --git a/src/pkg/runtime/race0.c b/src/pkg/runtime/race0.c
deleted file mode 100644
index eddb0be79f..0000000000
--- a/src/pkg/runtime/race0.c
+++ /dev/null
@@ -1,124 +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.
-
-// Stub implementation of the race detector API.
-// +build !race
-
-#include "runtime.h"
-
-uintptr
-runtime·raceinit(void)
-{
- return 0;
-}
-
-void
-runtime·racefini(void)
-{
-}
-
-
-void
-runtime·racemapshadow(void *addr, uintptr size)
-{
- USED(addr);
- USED(size);
-}
-
-void
-runtime·racewritepc(void *addr, void *callpc, void *pc)
-{
- USED(addr);
- USED(callpc);
- USED(pc);
-}
-
-void
-runtime·racereadpc(void *addr, void *callpc, void *pc)
-{
- USED(addr);
- USED(callpc);
- USED(pc);
-}
-
-void
-runtime·racewriterangepc(void *addr, uintptr sz, void *callpc, void *pc)
-{
- USED(addr);
- USED(sz);
- USED(callpc);
- USED(pc);
-}
-
-void
-runtime·racereadrangepc(void *addr, uintptr sz, void *callpc, void *pc)
-{
- USED(addr);
- USED(sz);
- USED(callpc);
- USED(pc);
-}
-
-void
-runtime·raceacquire(void *addr)
-{
- USED(addr);
-}
-
-void
-runtime·raceacquireg(G *gp, void *addr)
-{
- USED(gp);
- USED(addr);
-}
-
-void
-runtime·racerelease(void *addr)
-{
- USED(addr);
-}
-
-void
-runtime·racereleaseg(G *gp, void *addr)
-{
- USED(gp);
- USED(addr);
-}
-
-void
-runtime·racereleasemerge(void *addr)
-{
- USED(addr);
-}
-
-void
-runtime·racereleasemergeg(G *gp, void *addr)
-{
- USED(gp);
- USED(addr);
-}
-
-void
-runtime·racefingo(void)
-{
-}
-
-void
-runtime·racemalloc(void *p, uintptr sz)
-{
- USED(p);
- USED(sz);
-}
-
-uintptr
-runtime·racegostart(void *pc)
-{
- USED(pc);
- return 0;
-}
-
-void
-runtime·racegoend(void)
-{
-}
diff --git a/src/pkg/runtime/race0.go b/src/pkg/runtime/race0.go
index f228c6d7b4..5d90cc859a 100644
--- a/src/pkg/runtime/race0.go
+++ b/src/pkg/runtime/race0.go
@@ -14,5 +14,24 @@ import (
const raceenabled = false
-func raceReadObjectPC(t *_type, addr unsafe.Pointer, callerpc, pc uintptr) {
-}
+// Because raceenabled is false, none of these functions should be called.
+
+func raceReadObjectPC(t *_type, addr unsafe.Pointer, callerpc, pc uintptr) { gothrow("race") }
+func raceWriteObjectPC(t *_type, addr unsafe.Pointer, callerpc, pc uintptr) { gothrow("race") }
+func raceinit() { gothrow("race") }
+func racefini() { gothrow("race") }
+func racemapshadow(addr unsafe.Pointer, size uintptr) { gothrow("race") }
+func racewritepc(addr unsafe.Pointer, callerpc, pc uintptr) { gothrow("race") }
+func racereadpc(addr unsafe.Pointer, callerpc, pc uintptr) { gothrow("race") }
+func racereadrangepc(addr unsafe.Pointer, sz, callerpc, pc uintptr) { gothrow("race") }
+func racewriterangepc(addr unsafe.Pointer, sz, callerpc, pc uintptr) { gothrow("race") }
+func raceacquire(addr unsafe.Pointer) { gothrow("race") }
+func raceacquireg(gp *g, addr unsafe.Pointer) { gothrow("race") }
+func racerelease(addr unsafe.Pointer) { gothrow("race") }
+func racereleaseg(gp *g, addr unsafe.Pointer) { gothrow("race") }
+func racereleasemerge(addr unsafe.Pointer) { gothrow("race") }
+func racereleasemergeg(gp *g, addr unsafe.Pointer) { gothrow("race") }
+func racefingo() { gothrow("race") }
+func racemalloc(p unsafe.Pointer, sz uintptr) { gothrow("race") }
+func racegostart(pc uintptr) uintptr { gothrow("race"); return 0 }
+func racegoend() { gothrow("race") }
diff --git a/src/pkg/runtime/race_amd64.s b/src/pkg/runtime/race_amd64.s
index 210f5d4ad7..bdea28c7c0 100644
--- a/src/pkg/runtime/race_amd64.s
+++ b/src/pkg/runtime/race_amd64.s
@@ -6,7 +6,7 @@
#include "zasm_GOOS_GOARCH.h"
#include "funcdata.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// The following thunks allow calling the gcc-compiled race runtime directly
// from Go code without going all the way through cgo.
@@ -79,7 +79,7 @@ TEXT runtime·RaceWrite(SB), NOSPLIT, $0-8
TEXT runtime·racewritepc(SB), NOSPLIT, $0-24
MOVQ addr+0(FP), RARG1
MOVQ callpc+8(FP), RARG2
- MOVQ cp+16(FP), RARG3
+ MOVQ pc+16(FP), RARG3
// void __tsan_write_pc(ThreadState *thr, void *addr, void *callpc, void *pc);
MOVQ $__tsan_write_pc(SB), AX
JMP racecalladdr<>(SB)
@@ -144,9 +144,11 @@ TEXT racecalladdr<>(SB), NOSPLIT, $0-0
CMPQ RARG1, runtime·racearenaend(SB)
JB racecalladdr_call
racecalladdr_data:
- CMPQ RARG1, $noptrdata(SB)
+ MOVQ $runtime·noptrdata(SB), R13
+ CMPQ RARG1, R13
JB racecalladdr_ret
- CMPQ RARG1, $enoptrbss(SB)
+ MOVQ $runtime·enoptrbss(SB), R13
+ CMPQ RARG1, R13
JAE racecalladdr_ret
racecalladdr_call:
MOVQ AX, AX // w/o this 6a miscompiles this function
@@ -178,6 +180,141 @@ TEXT runtime·racefuncexit(SB), NOSPLIT, $0-0
MOVQ $__tsan_func_exit(SB), AX
JMP racecall<>(SB)
+// Atomic operations for sync/atomic package.
+
+// Load
+TEXT sync∕atomic·LoadInt32(SB), NOSPLIT, $0-0
+ MOVQ $__tsan_go_atomic32_load(SB), AX
+ CALL racecallatomic<>(SB)
+ RET
+
+TEXT sync∕atomic·LoadInt64(SB), NOSPLIT, $0-0
+ MOVQ $__tsan_go_atomic64_load(SB), AX
+ CALL racecallatomic<>(SB)
+ RET
+
+TEXT sync∕atomic·LoadUint32(SB), NOSPLIT, $0-0
+ JMP sync∕atomic·LoadInt32(SB)
+
+TEXT sync∕atomic·LoadUint64(SB), NOSPLIT, $0-0
+ JMP sync∕atomic·LoadInt64(SB)
+
+TEXT sync∕atomic·LoadUintptr(SB), NOSPLIT, $0-0
+ JMP sync∕atomic·LoadInt64(SB)
+
+TEXT sync∕atomic·LoadPointer(SB), NOSPLIT, $0-0
+ JMP sync∕atomic·LoadInt64(SB)
+
+// Store
+TEXT sync∕atomic·StoreInt32(SB), NOSPLIT, $0-0
+ MOVQ $__tsan_go_atomic32_store(SB), AX
+ CALL racecallatomic<>(SB)
+ RET
+
+TEXT sync∕atomic·StoreInt64(SB), NOSPLIT, $0-0
+ MOVQ $__tsan_go_atomic64_store(SB), AX
+ CALL racecallatomic<>(SB)
+ RET
+
+TEXT sync∕atomic·StoreUint32(SB), NOSPLIT, $0-0
+ JMP sync∕atomic·StoreInt32(SB)
+
+TEXT sync∕atomic·StoreUint64(SB), NOSPLIT, $0-0
+ JMP sync∕atomic·StoreInt64(SB)
+
+TEXT sync∕atomic·StoreUintptr(SB), NOSPLIT, $0-0
+ JMP sync∕atomic·StoreInt64(SB)
+
+TEXT sync∕atomic·StorePointer(SB), NOSPLIT, $0-0
+ JMP sync∕atomic·StoreInt64(SB)
+
+// Swap
+TEXT sync∕atomic·SwapInt32(SB), NOSPLIT, $0-0
+ MOVQ $__tsan_go_atomic32_exchange(SB), AX
+ CALL racecallatomic<>(SB)
+ RET
+
+TEXT sync∕atomic·SwapInt64(SB), NOSPLIT, $0-0
+ MOVQ $__tsan_go_atomic64_exchange(SB), AX
+ CALL racecallatomic<>(SB)
+ RET
+
+TEXT sync∕atomic·SwapUint32(SB), NOSPLIT, $0-0
+ JMP sync∕atomic·SwapInt32(SB)
+
+TEXT sync∕atomic·SwapUint64(SB), NOSPLIT, $0-0
+ JMP sync∕atomic·SwapInt64(SB)
+
+TEXT sync∕atomic·SwapUintptr(SB), NOSPLIT, $0-0
+ JMP sync∕atomic·SwapInt64(SB)
+
+TEXT sync∕atomic·SwapPointer(SB), NOSPLIT, $0-0
+ JMP sync∕atomic·SwapInt64(SB)
+
+// Add
+TEXT sync∕atomic·AddInt32(SB), NOSPLIT, $0-0
+ MOVQ $__tsan_go_atomic32_fetch_add(SB), AX
+ CALL racecallatomic<>(SB)
+ MOVL add+8(FP), AX // convert fetch_add to add_fetch
+ ADDL AX, ret+16(FP)
+ RET
+
+TEXT sync∕atomic·AddInt64(SB), NOSPLIT, $0-0
+ MOVQ $__tsan_go_atomic64_fetch_add(SB), AX
+ CALL racecallatomic<>(SB)
+ MOVQ add+8(FP), AX // convert fetch_add to add_fetch
+ ADDQ AX, ret+16(FP)
+ RET
+
+TEXT sync∕atomic·AddUint32(SB), NOSPLIT, $0-0
+ JMP sync∕atomic·AddInt32(SB)
+
+TEXT sync∕atomic·AddUint64(SB), NOSPLIT, $0-0
+ JMP sync∕atomic·AddInt64(SB)
+
+TEXT sync∕atomic·AddUintptr(SB), NOSPLIT, $0-0
+ JMP sync∕atomic·AddInt64(SB)
+
+TEXT sync∕atomic·AddPointer(SB), NOSPLIT, $0-0
+ JMP sync∕atomic·AddInt64(SB)
+
+// CompareAndSwap
+TEXT sync∕atomic·CompareAndSwapInt32(SB), NOSPLIT, $0-0
+ MOVQ $__tsan_go_atomic32_compare_exchange(SB), AX
+ CALL racecallatomic<>(SB)
+ RET
+
+TEXT sync∕atomic·CompareAndSwapInt64(SB), NOSPLIT, $0-0
+ MOVQ $__tsan_go_atomic64_compare_exchange(SB), AX
+ CALL racecallatomic<>(SB)
+ RET
+
+TEXT sync∕atomic·CompareAndSwapUint32(SB), NOSPLIT, $0-0
+ JMP sync∕atomic·CompareAndSwapInt32(SB)
+
+TEXT sync∕atomic·CompareAndSwapUint64(SB), NOSPLIT, $0-0
+ JMP sync∕atomic·CompareAndSwapInt64(SB)
+
+TEXT sync∕atomic·CompareAndSwapUintptr(SB), NOSPLIT, $0-0
+ JMP sync∕atomic·CompareAndSwapInt64(SB)
+
+TEXT sync∕atomic·CompareAndSwapPointer(SB), NOSPLIT, $0-0
+ JMP sync∕atomic·CompareAndSwapInt64(SB)
+
+// Generic atomic operation implementation.
+// AX already contains target function.
+TEXT racecallatomic<>(SB), NOSPLIT, $0-0
+ // Trigger SIGSEGV early.
+ MOVQ 16(SP), R12
+ MOVL (R12), R12
+ get_tls(R12)
+ MOVQ g(R12), R14
+ MOVQ g_racectx(R14), RARG0 // goroutine context
+ MOVQ 8(SP), RARG1 // caller pc
+ MOVQ (SP), RARG2 // pc
+ LEAQ 16(SP), RARG3 // arguments
+ JMP racecall<>(SB)
+
// void runtime·racecall(void(*f)(...), ...)
// Calls C function f from race runtime and passes up to 4 arguments to it.
// The arguments are never heap-object-preserving pointers, so we pretend there are no arguments.
diff --git a/src/pkg/runtime/rdebug.go b/src/pkg/runtime/rdebug.go
new file mode 100644
index 0000000000..e5e691122c
--- /dev/null
+++ b/src/pkg/runtime/rdebug.go
@@ -0,0 +1,37 @@
+// 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
+
+func setMaxStack(in int) (out int) {
+ out = int(maxstacksize)
+ maxstacksize = uintptr(in)
+ return out
+}
+
+func setGCPercent(in int32) (out int32) {
+ mp := acquirem()
+ mp.scalararg[0] = uintptr(int(in))
+ onM(setgcpercent_m)
+ out = int32(int(mp.scalararg[0]))
+ releasem(mp)
+ return out
+}
+
+func setPanicOnFault(new bool) (old bool) {
+ mp := acquirem()
+ old = mp.curg.paniconfault
+ mp.curg.paniconfault = new
+ releasem(mp)
+ return old
+}
+
+func setMaxThreads(in int) (out int) {
+ mp := acquirem()
+ mp.scalararg[0] = uintptr(in)
+ onM(setmaxthreads_m)
+ out = int(mp.scalararg[0])
+ releasem(mp)
+ return out
+}
diff --git a/src/pkg/runtime/rdebug.goc b/src/pkg/runtime/rdebug.goc
deleted file mode 100644
index 042b30ace9..0000000000
--- a/src/pkg/runtime/rdebug.goc
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package runtime∕debug
-#include "runtime.h"
-#include "arch_GOARCH.h"
-#include "malloc.h"
-#include "stack.h"
-
-func setMaxStack(in int) (out int) {
- out = runtime·maxstacksize;
- runtime·maxstacksize = in;
-}
-
-func setGCPercent(in int) (out int) {
- out = runtime·setgcpercent(in);
-}
-
-func setMaxThreads(in int) (out int) {
- out = runtime·setmaxthreads(in);
-}
-
-func SetPanicOnFault(enabled bool) (old bool) {
- old = g->paniconfault;
- g->paniconfault = enabled;
-}
diff --git a/src/pkg/runtime/rt0_android_arm.s b/src/pkg/runtime/rt0_android_arm.s
index 3eecbbddc5..6b65fb47b1 100644
--- a/src/pkg/runtime/rt0_android_arm.s
+++ b/src/pkg/runtime/rt0_android_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT _rt0_arm_android(SB),NOSPLIT,$-4
MOVW (R13), R0 // argc
diff --git a/src/pkg/runtime/rt0_darwin_386.s b/src/pkg/runtime/rt0_darwin_386.s
index 4f85250c2d..4c8c92dda8 100644
--- a/src/pkg/runtime/rt0_darwin_386.s
+++ b/src/pkg/runtime/rt0_darwin_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT _rt0_386_darwin(SB),NOSPLIT,$8
MOVL 8(SP), AX
@@ -13,4 +13,4 @@ TEXT _rt0_386_darwin(SB),NOSPLIT,$8
INT $3
TEXT main(SB),NOSPLIT,$0
- JMP _rt0_go(SB)
+ JMP runtime·rt0_go(SB)
diff --git a/src/pkg/runtime/rt0_darwin_amd64.s b/src/pkg/runtime/rt0_darwin_amd64.s
index 8d2962b03a..452d854558 100644
--- a/src/pkg/runtime/rt0_darwin_amd64.s
+++ b/src/pkg/runtime/rt0_darwin_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT _rt0_amd64_darwin(SB),NOSPLIT,$-8
LEAQ 8(SP), SI // argv
@@ -11,5 +11,5 @@ TEXT _rt0_amd64_darwin(SB),NOSPLIT,$-8
JMP AX
TEXT main(SB),NOSPLIT,$-8
- MOVQ $_rt0_go(SB), AX
+ MOVQ $runtime·rt0_go(SB), AX
JMP AX
diff --git a/src/pkg/runtime/rt0_dragonfly_386.s b/src/pkg/runtime/rt0_dragonfly_386.s
index b857f60391..548ba796a0 100644
--- a/src/pkg/runtime/rt0_dragonfly_386.s
+++ b/src/pkg/runtime/rt0_dragonfly_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT _rt0_386_dragonfly(SB),NOSPLIT,$8
MOVL 8(SP), AX
@@ -13,4 +13,4 @@ TEXT _rt0_386_dragonfly(SB),NOSPLIT,$8
INT $3
TEXT main(SB),NOSPLIT,$0
- JMP _rt0_go(SB)
+ JMP runtime·rt0_go(SB)
diff --git a/src/pkg/runtime/rt0_dragonfly_amd64.s b/src/pkg/runtime/rt0_dragonfly_amd64.s
index fc7e745983..fb56618d8f 100644
--- a/src/pkg/runtime/rt0_dragonfly_amd64.s
+++ b/src/pkg/runtime/rt0_dragonfly_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT _rt0_amd64_dragonfly(SB),NOSPLIT,$-8
LEAQ 8(DI), SI // argv
@@ -11,5 +11,5 @@ TEXT _rt0_amd64_dragonfly(SB),NOSPLIT,$-8
JMP AX
TEXT main(SB),NOSPLIT,$-8
- MOVQ $_rt0_go(SB), AX
+ MOVQ $runtime·rt0_go(SB), AX
JMP AX
diff --git a/src/pkg/runtime/rt0_freebsd_386.s b/src/pkg/runtime/rt0_freebsd_386.s
index 758f7d2685..cd7a915f84 100644
--- a/src/pkg/runtime/rt0_freebsd_386.s
+++ b/src/pkg/runtime/rt0_freebsd_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT _rt0_386_freebsd(SB),NOSPLIT,$8
MOVL 8(SP), AX
@@ -13,4 +13,4 @@ TEXT _rt0_386_freebsd(SB),NOSPLIT,$8
INT $3
TEXT main(SB),NOSPLIT,$0
- JMP _rt0_go(SB)
+ JMP runtime·rt0_go(SB)
diff --git a/src/pkg/runtime/rt0_freebsd_amd64.s b/src/pkg/runtime/rt0_freebsd_amd64.s
index 3cf7163b5e..7989f7c3e9 100644
--- a/src/pkg/runtime/rt0_freebsd_amd64.s
+++ b/src/pkg/runtime/rt0_freebsd_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT _rt0_amd64_freebsd(SB),NOSPLIT,$-8
LEAQ 8(DI), SI // argv
@@ -11,5 +11,5 @@ TEXT _rt0_amd64_freebsd(SB),NOSPLIT,$-8
JMP AX
TEXT main(SB),NOSPLIT,$-8
- MOVQ $_rt0_go(SB), AX
+ MOVQ $runtime·rt0_go(SB), AX
JMP AX
diff --git a/src/pkg/runtime/rt0_freebsd_arm.s b/src/pkg/runtime/rt0_freebsd_arm.s
index 56219f8999..f31252698e 100644
--- a/src/pkg/runtime/rt0_freebsd_arm.s
+++ b/src/pkg/runtime/rt0_freebsd_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// FreeBSD and Linux use the same linkage to main
@@ -10,9 +10,9 @@ TEXT _rt0_arm_freebsd(SB),NOSPLIT,$-4
MOVW (R13), R0 // argc
MOVW $4(R13), R1 // argv
MOVM.DB.W [R0-R1], (R13)
- B _rt0_go(SB)
+ B runtime·rt0_go(SB)
TEXT main(SB),NOSPLIT,$-4
MOVM.DB.W [R0-R1], (R13)
- MOVW $_rt0_go(SB), R4
+ MOVW $runtime·rt0_go(SB), R4
B (R4)
diff --git a/src/pkg/runtime/rt0_linux_386.s b/src/pkg/runtime/rt0_linux_386.s
index c6f4159ce3..74ddc94da9 100644
--- a/src/pkg/runtime/rt0_linux_386.s
+++ b/src/pkg/runtime/rt0_linux_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT _rt0_386_linux(SB),NOSPLIT,$8
MOVL 8(SP), AX
@@ -14,7 +14,7 @@ TEXT _rt0_386_linux(SB),NOSPLIT,$8
INT $3
TEXT main(SB),NOSPLIT,$0
- JMP _rt0_go(SB)
+ JMP runtime·rt0_go(SB)
TEXT _fallback_vdso(SB),NOSPLIT,$0
INT $0x80
diff --git a/src/pkg/runtime/rt0_linux_amd64.s b/src/pkg/runtime/rt0_linux_amd64.s
index a887ced8f0..985426acc4 100644
--- a/src/pkg/runtime/rt0_linux_amd64.s
+++ b/src/pkg/runtime/rt0_linux_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT _rt0_amd64_linux(SB),NOSPLIT,$-8
LEAQ 8(SP), SI // argv
@@ -11,5 +11,5 @@ TEXT _rt0_amd64_linux(SB),NOSPLIT,$-8
JMP AX
TEXT main(SB),NOSPLIT,$-8
- MOVQ $_rt0_go(SB), AX
+ MOVQ $runtime·rt0_go(SB), AX
JMP AX
diff --git a/src/pkg/runtime/rt0_linux_arm.s b/src/pkg/runtime/rt0_linux_arm.s
index 309fa2f79d..8af3d3505e 100644
--- a/src/pkg/runtime/rt0_linux_arm.s
+++ b/src/pkg/runtime/rt0_linux_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT _rt0_arm_linux(SB),NOSPLIT,$-4
MOVW (R13), R0 // argc
@@ -56,7 +56,7 @@ TEXT _rt0_arm_linux1(SB),NOSPLIT,$-4
SUB $4, R13 // fake a stack frame for runtime·setup_auxv
BL runtime·setup_auxv(SB)
ADD $4, R13
- B _rt0_go(SB)
+ B runtime·rt0_go(SB)
TEXT bad_abi<>(SB),NOSPLIT,$-4
// give diagnosis and exit
diff --git a/src/pkg/runtime/rt0_nacl_386.s b/src/pkg/runtime/rt0_nacl_386.s
index 8b713548fe..d4ba06306a 100644
--- a/src/pkg/runtime/rt0_nacl_386.s
+++ b/src/pkg/runtime/rt0_nacl_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// NaCl entry has:
// 0(FP) - arg block == SP+8
@@ -19,4 +19,4 @@ TEXT _rt0_386_nacl(SB),NOSPLIT,$8
INT $3
TEXT main(SB),NOSPLIT,$0
- JMP _rt0_go(SB)
+ JMP runtime·rt0_go(SB)
diff --git a/src/pkg/runtime/rt0_nacl_amd64p32.s b/src/pkg/runtime/rt0_nacl_amd64p32.s
index 502d2e2bfc..d8703dc0f0 100644
--- a/src/pkg/runtime/rt0_nacl_amd64p32.s
+++ b/src/pkg/runtime/rt0_nacl_amd64p32.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// NaCl entry on 32-bit x86 has DI pointing at the arg block, which contains:
//
@@ -27,4 +27,4 @@ TEXT main(SB),NOSPLIT,$0
// Uncomment for fake time like on Go Playground.
//MOVQ $1257894000000000000, AX
//MOVQ AX, runtime·timens(SB)
- JMP _rt0_go(SB)
+ JMP runtime·rt0_go(SB)
diff --git a/src/pkg/runtime/rt0_nacl_arm.s b/src/pkg/runtime/rt0_nacl_arm.s
index df84d5d02b..eadb4782dd 100644
--- a/src/pkg/runtime/rt0_nacl_arm.s
+++ b/src/pkg/runtime/rt0_nacl_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// NaCl entry has:
// 0(FP) - 0
@@ -17,4 +17,4 @@ TEXT _rt0_arm_nacl(SB),NOSPLIT,$-4
B main(SB)
TEXT main(SB),NOSPLIT,$0
- B _rt0_go(SB)
+ B runtime·rt0_go(SB)
diff --git a/src/pkg/runtime/rt0_netbsd_386.s b/src/pkg/runtime/rt0_netbsd_386.s
index eb348fcee1..70b8532538 100644
--- a/src/pkg/runtime/rt0_netbsd_386.s
+++ b/src/pkg/runtime/rt0_netbsd_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT _rt0_386_netbsd(SB),NOSPLIT,$8
MOVL 8(SP), AX
@@ -13,4 +13,4 @@ TEXT _rt0_386_netbsd(SB),NOSPLIT,$8
INT $3
TEXT main(SB),NOSPLIT,$0
- JMP _rt0_go(SB)
+ JMP runtime·rt0_go(SB)
diff --git a/src/pkg/runtime/rt0_netbsd_amd64.s b/src/pkg/runtime/rt0_netbsd_amd64.s
index c8e3fb18c3..fad56614e5 100644
--- a/src/pkg/runtime/rt0_netbsd_amd64.s
+++ b/src/pkg/runtime/rt0_netbsd_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT _rt0_amd64_netbsd(SB),NOSPLIT,$-8
LEAQ 8(SP), SI // argv
@@ -11,5 +11,5 @@ TEXT _rt0_amd64_netbsd(SB),NOSPLIT,$-8
JMP AX
TEXT main(SB),NOSPLIT,$-8
- MOVQ $_rt0_go(SB), AX
+ MOVQ $runtime·rt0_go(SB), AX
JMP AX
diff --git a/src/pkg/runtime/rt0_netbsd_arm.s b/src/pkg/runtime/rt0_netbsd_arm.s
index 36effc3c51..bad66e06cf 100644
--- a/src/pkg/runtime/rt0_netbsd_arm.s
+++ b/src/pkg/runtime/rt0_netbsd_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// FreeBSD/NetBSD and Linux use the same linkage to main
@@ -10,4 +10,4 @@ TEXT _rt0_arm_netbsd(SB),NOSPLIT,$-4
MOVW (R13), R0 // argc
MOVW $4(R13), R1 // argv
MOVM.DB.W [R0-R1], (R13)
- B _rt0_go(SB)
+ B runtime·rt0_go(SB)
diff --git a/src/pkg/runtime/rt0_openbsd_386.s b/src/pkg/runtime/rt0_openbsd_386.s
index 9e80f69be2..f25d2e1cf0 100644
--- a/src/pkg/runtime/rt0_openbsd_386.s
+++ b/src/pkg/runtime/rt0_openbsd_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT _rt0_386_openbsd(SB),NOSPLIT,$8
MOVL 8(SP), AX
@@ -13,4 +13,4 @@ TEXT _rt0_386_openbsd(SB),NOSPLIT,$8
INT $3
TEXT main(SB),NOSPLIT,$0
- JMP _rt0_go(SB)
+ JMP runtime·rt0_go(SB)
diff --git a/src/pkg/runtime/rt0_openbsd_amd64.s b/src/pkg/runtime/rt0_openbsd_amd64.s
index b1ad403b70..58fe666391 100644
--- a/src/pkg/runtime/rt0_openbsd_amd64.s
+++ b/src/pkg/runtime/rt0_openbsd_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT _rt0_amd64_openbsd(SB),NOSPLIT,$-8
LEAQ 8(SP), SI // argv
@@ -11,5 +11,5 @@ TEXT _rt0_amd64_openbsd(SB),NOSPLIT,$-8
JMP AX
TEXT main(SB),NOSPLIT,$-8
- MOVQ $_rt0_go(SB), AX
+ MOVQ $runtime·rt0_go(SB), AX
JMP AX
diff --git a/src/pkg/runtime/rt0_plan9_386.s b/src/pkg/runtime/rt0_plan9_386.s
index a8ae50841d..7e2887b857 100644
--- a/src/pkg/runtime/rt0_plan9_386.s
+++ b/src/pkg/runtime/rt0_plan9_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT _rt0_386_plan9(SB),NOSPLIT,$12
MOVL AX, _tos(SB)
@@ -14,7 +14,7 @@ TEXT _rt0_386_plan9(SB),NOSPLIT,$12
MOVL AX, 0(SP)
LEAL inargv+0(FP), AX
MOVL AX, 4(SP)
- CALL _rt0_go(SB)
+ CALL runtime·rt0_go(SB)
DATA runtime·isplan9(SB)/4, $1
GLOBL runtime·isplan9(SB), $4
diff --git a/src/pkg/runtime/rt0_plan9_amd64.s b/src/pkg/runtime/rt0_plan9_amd64.s
index 96d00584df..a372a0ba8b 100644
--- a/src/pkg/runtime/rt0_plan9_amd64.s
+++ b/src/pkg/runtime/rt0_plan9_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT _rt0_amd64_plan9(SB),NOSPLIT,$24
MOVQ AX, _tos(SB)
@@ -11,7 +11,7 @@ TEXT _rt0_amd64_plan9(SB),NOSPLIT,$24
MOVL $1, _nprivates(SB)
MOVL inargc-8(FP), DI
LEAQ inargv+0(FP), SI
- MOVQ $_rt0_go(SB), AX
+ MOVQ $runtime·rt0_go(SB), AX
JMP AX
DATA runtime·isplan9(SB)/4, $1
diff --git a/src/pkg/runtime/rt0_solaris_amd64.s b/src/pkg/runtime/rt0_solaris_amd64.s
index 4aca991f08..92a9fc2952 100644
--- a/src/pkg/runtime/rt0_solaris_amd64.s
+++ b/src/pkg/runtime/rt0_solaris_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT _rt0_amd64_solaris(SB),NOSPLIT,$-8
LEAQ 8(SP), SI // argv
@@ -11,7 +11,7 @@ TEXT _rt0_amd64_solaris(SB),NOSPLIT,$-8
JMP AX
TEXT main(SB),NOSPLIT,$-8
- MOVQ $_rt0_go(SB), AX
+ MOVQ $runtime·rt0_go(SB), AX
JMP AX
DATA runtime·issolaris(SB)/4, $1
diff --git a/src/pkg/runtime/rt0_windows_386.s b/src/pkg/runtime/rt0_windows_386.s
index 594e2cd343..00604372f1 100644
--- a/src/pkg/runtime/rt0_windows_386.s
+++ b/src/pkg/runtime/rt0_windows_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT _rt0_386_windows(SB),NOSPLIT,$12
MOVL 12(SP), AX
@@ -13,7 +13,7 @@ TEXT _rt0_386_windows(SB),NOSPLIT,$12
JMP main(SB)
TEXT main(SB),NOSPLIT,$0
- JMP _rt0_go(SB)
+ JMP runtime·rt0_go(SB)
DATA runtime·iswindows(SB)/4, $1
diff --git a/src/pkg/runtime/rt0_windows_amd64.s b/src/pkg/runtime/rt0_windows_amd64.s
index 32e18b02ba..890a570d1d 100644
--- a/src/pkg/runtime/rt0_windows_amd64.s
+++ b/src/pkg/runtime/rt0_windows_amd64.s
@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
#include "zasm_GOOS_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT _rt0_amd64_windows(SB),NOSPLIT,$-8
LEAQ 8(SP), SI // argv
@@ -12,7 +12,7 @@ TEXT _rt0_amd64_windows(SB),NOSPLIT,$-8
JMP AX
TEXT main(SB),NOSPLIT,$-8
- MOVQ $_rt0_go(SB), AX
+ MOVQ $runtime·rt0_go(SB), AX
JMP AX
DATA runtime·iswindows(SB)/4, $1
diff --git a/src/pkg/runtime/runtime.c b/src/pkg/runtime/runtime.c
index 31b853c87a..42ce1dadfb 100644
--- a/src/pkg/runtime/runtime.c
+++ b/src/pkg/runtime/runtime.c
@@ -5,14 +5,14 @@
#include "runtime.h"
#include "stack.h"
#include "arch_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// Keep a cached value to make gotraceback fast,
// since we call it on every call to gentraceback.
// The cached value is a uint32 in which the low bit
// is the "crash" setting and the top 31 bits are the
// gotraceback value.
-static uint32 traceback_cache = ~(uint32)0;
+static uint32 traceback_cache = 2<<1;
// The GOTRACEBACK environment variable controls the
// behavior of a Go program that is crashing and exiting.
@@ -20,32 +20,17 @@ static uint32 traceback_cache = ~(uint32)0;
// GOTRACEBACK=1 default behavior - show tracebacks but exclude runtime frames
// GOTRACEBACK=2 show tracebacks including runtime frames
// GOTRACEBACK=crash show tracebacks including runtime frames, then crash (core dump etc)
+#pragma textflag NOSPLIT
int32
runtime·gotraceback(bool *crash)
{
- byte *p;
- uint32 x;
-
if(crash != nil)
*crash = false;
if(g->m->traceback != 0)
return g->m->traceback;
- x = runtime·atomicload(&traceback_cache);
- if(x == ~(uint32)0) {
- p = runtime·getenv("GOTRACEBACK");
- if(p == nil)
- p = (byte*)"";
- if(p[0] == '\0')
- x = 1<<1;
- else if(runtime·strcmp(p, (byte*)"crash") == 0)
- x = (2<<1) | 1;
- else
- x = runtime·atoi(p)<<1;
- runtime·atomicstore(&traceback_cache, x);
- }
if(crash != nil)
- *crash = x&1;
- return x>>1;
+ *crash = traceback_cache&1;
+ return traceback_cache>>1;
}
int32
@@ -111,7 +96,7 @@ runtime·goargs(void)
if(Windows)
return;
- s = runtime·malloc(argc*sizeof s[0]);
+ s = runtime·mallocgc(argc*sizeof s[0], nil, 0);
for(i=0; i<argc; i++)
s[i] = runtime·gostringnocopy(argv[i]);
os·Args.array = (byte*)s;
@@ -128,14 +113,19 @@ runtime·goenvs_unix(void)
for(n=0; argv[argc+1+n] != 0; n++)
;
- s = runtime·malloc(n*sizeof s[0]);
+ s = runtime·mallocgc(n*sizeof s[0], nil, 0);
for(i=0; i<n; i++)
s[i] = runtime·gostringnocopy(argv[argc+1+i]);
syscall·envs.array = (byte*)s;
syscall·envs.len = n;
syscall·envs.cap = n;
+}
- traceback_cache = ~(uint32)0;
+#pragma textflag NOSPLIT
+Slice
+runtime·environ()
+{
+ return syscall·envs;
}
int32
@@ -277,49 +267,7 @@ runtime·check(void)
runtime·throw("FixedStack is not power-of-2");
}
-uint32
-runtime·fastrand1(void)
-{
- uint32 x;
-
- x = g->m->fastrand;
- x += x;
- if(x & 0x80000000L)
- x ^= 0x88888eefUL;
- g->m->fastrand = x;
- return x;
-}
-
-static Lock ticksLock;
-static int64 ticks;
-
-int64
-runtime·tickspersecond(void)
-{
- int64 res, t0, t1, c0, c1;
-
- res = (int64)runtime·atomicload64((uint64*)&ticks);
- if(res != 0)
- return ticks;
- runtime·lock(&ticksLock);
- res = ticks;
- if(res == 0) {
- t0 = runtime·nanotime();
- c0 = runtime·cputicks();
- runtime·usleep(100*1000);
- t1 = runtime·nanotime();
- c1 = runtime·cputicks();
- if(t1 == t0)
- t1++;
- res = (c1-c0)*1000*1000*1000/(t1-t0);
- if(res == 0)
- res++;
- runtime·atomicstore64((uint64*)&ticks, res);
- }
- runtime·unlock(&ticksLock);
- return res;
-}
-
+#pragma dataflag NOPTR
DebugVars runtime·debug;
static struct {
@@ -332,6 +280,7 @@ static struct {
{"gcdead", &runtime·debug.gcdead},
{"scheddetail", &runtime·debug.scheddetail},
{"schedtrace", &runtime·debug.schedtrace},
+ {"scavenge", &runtime·debug.scavenge},
};
void
@@ -354,6 +303,16 @@ runtime·parsedebugvars(void)
break;
p++;
}
+
+ p = runtime·getenv("GOTRACEBACK");
+ if(p == nil)
+ p = (byte*)"";
+ if(p[0] == '\0')
+ traceback_cache = 1<<1;
+ else if(runtime·strcmp(p, (byte*)"crash") == 0)
+ traceback_cache = (2<<1) | 1;
+ else
+ traceback_cache = runtime·atoi(p)<<1;
}
// Poor mans 64-bit division.
@@ -382,3 +341,51 @@ runtime·timediv(int64 v, int32 div, int32 *rem)
*rem = v;
return res;
}
+
+// Helpers for Go. Must be NOSPLIT, must only call NOSPLIT functions, and must not block.
+
+#pragma textflag NOSPLIT
+G*
+runtime·getg(void)
+{
+ return g;
+}
+
+#pragma textflag NOSPLIT
+M*
+runtime·acquirem(void)
+{
+ g->m->locks++;
+ return g->m;
+}
+
+#pragma textflag NOSPLIT
+void
+runtime·releasem(M *mp)
+{
+ mp->locks--;
+ if(mp->locks == 0 && g->preempt) {
+ // restore the preemption request in case we've cleared it in newstack
+ g->stackguard0 = StackPreempt;
+ }
+}
+
+#pragma textflag NOSPLIT
+MCache*
+runtime·gomcache(void)
+{
+ return g->m->mcache;
+}
+
+#pragma textflag NOSPLIT
+Slice
+reflect·typelinks(void)
+{
+ extern Type *runtime·typelink[], *runtime·etypelink[];
+ Slice ret;
+
+ ret.array = (byte*)runtime·typelink;
+ ret.len = runtime·etypelink - runtime·typelink;
+ ret.cap = ret.len;
+ return ret;
+}
diff --git a/src/pkg/runtime/runtime.go b/src/pkg/runtime/runtime.go
new file mode 100644
index 0000000000..d5b31559a2
--- /dev/null
+++ b/src/pkg/runtime/runtime.go
@@ -0,0 +1,37 @@
+// 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
+
+var ticks struct {
+ lock mutex
+ val uint64
+}
+
+// Note: Called by runtime/pprof in addition to runtime code.
+func tickspersecond() int64 {
+ r := int64(atomicload64(&ticks.val))
+ if r != 0 {
+ return r
+ }
+ lock(&ticks.lock)
+ r = int64(ticks.val)
+ if r == 0 {
+ t0 := nanotime()
+ c0 := cputicks()
+ usleep(100 * 1000)
+ t1 := nanotime()
+ c1 := cputicks()
+ if t1 == t0 {
+ t1++
+ }
+ r = (c1 - c0) * 1000 * 1000 * 1000 / (t1 - t0)
+ if r == 0 {
+ r++
+ }
+ atomicstore64(&ticks.val, uint64(r))
+ }
+ unlock(&ticks.lock)
+ return r
+}
diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h
index e6354d7e9c..02563fd36c 100644
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -22,17 +22,10 @@ typedef int64 intptr;
typedef int64 intgo; // Go's int
typedef uint64 uintgo; // Go's uint
#else
-// Normally, "int" == "long int" == 32 bits.
-// However, the C compiler uses this distinction
-// to disambiguate true 32 bit ints (e.g. int32)
-// from 32/64 bit ints (e.g. uintptr) so that it
-// can generate the corresponding go type correctly.
-typedef signed long int int32_x;
-typedef unsigned long int uint32_x;
-typedef uint32_x uintptr;
-typedef int32_x intptr;
-typedef int32_x intgo; // Go's int
-typedef uint32_x uintgo; // Go's uint
+typedef uint32 uintptr;
+typedef int32 intptr;
+typedef int32 intgo; // Go's int
+typedef uint32 uintgo; // Go's uint
#endif
#ifdef _64BITREG
@@ -63,7 +56,8 @@ typedef uint8 byte;
typedef struct Func Func;
typedef struct G G;
typedef struct Gobuf Gobuf;
-typedef struct Lock Lock;
+typedef struct SudoG SudoG;
+typedef struct Mutex Mutex;
typedef struct M M;
typedef struct P P;
typedef struct Note Note;
@@ -91,8 +85,6 @@ typedef struct Complex64 Complex64;
typedef struct Complex128 Complex128;
typedef struct LibCall LibCall;
typedef struct WinCallbackContext WinCallbackContext;
-typedef struct Timers Timers;
-typedef struct Timer Timer;
typedef struct GCStats GCStats;
typedef struct LFNode LFNode;
typedef struct ParFor ParFor;
@@ -100,6 +92,7 @@ typedef struct ParForThread ParForThread;
typedef struct CgoMal CgoMal;
typedef struct PollDesc PollDesc;
typedef struct DebugVars DebugVars;
+typedef struct ForceGCState ForceGCState;
/*
* Per-CPU declaration.
@@ -127,13 +120,25 @@ enum
// If you add to this list, add to the list
// of "okay during garbage collection" status
// in mgc0.c too.
- Gidle,
- Grunnable,
- Grunning,
- Gsyscall,
- Gwaiting,
- Gmoribund_unused, // currently unused, but hardcoded in gdb scripts
- Gdead,
+ Gidle, // 0
+ Grunnable, // 1 runnable and on a run queue
+ Grunning, // 2
+ Gsyscall, // 3
+ Gwaiting, // 4
+ Gmoribund_unused, // 5 currently unused, but hardcoded in gdb scripts
+ Gdead, // 6
+ Genqueue, // 7 Only the Gscanenqueue is used.
+ Gcopystack, // 8 in this state when newstack is moving the stack
+ // the following encode that the GC is scanning the stack and what to do when it is done
+ Gscan = 0x1000, // atomicstatus&~Gscan = the non-scan state,
+ // Gscanidle = Gscan + Gidle, // Not used. Gidle only used with newly malloced gs
+ Gscanrunnable = Gscan + Grunnable, // 0x1001 When scanning complets make Grunnable (it is already on run queue)
+ Gscanrunning = Gscan + Grunning, // 0x1002 Used to tell preemption newstack routine to scan preempted stack.
+ Gscansyscall = Gscan + Gsyscall, // 0x1003 When scanning completes make is Gsyscall
+ Gscanwaiting = Gscan + Gwaiting, // 0x1004 When scanning completes make it Gwaiting
+ // Gscanmoribund_unused, // not possible
+ // Gscandead, // not possible
+ Gscanenqueue = Gscan + Genqueue, // When scanning completes make it Grunnable and put on runqueue
};
enum
{
@@ -156,7 +161,7 @@ enum
/*
* structures
*/
-struct Lock
+struct Mutex
{
// Futex-based impl treats it as uint32 key,
// while sema-based impl as M* waitm.
@@ -217,6 +222,19 @@ struct Gobuf
uintreg ret;
uintptr lr;
};
+// Known to compiler.
+// Changes here must also be made in src/cmd/gc/select.c's selecttype.
+struct SudoG
+{
+ G* g;
+ uint32* selectdone;
+ SudoG* next;
+ SudoG* prev;
+ void* elem; // data element
+ int64 releasetime;
+ int32 nrelease; // -1 for acquire
+ SudoG* waitlink; // G.waiting list
+};
struct GCStats
{
// the struct must consist of only uint64's,
@@ -230,7 +248,7 @@ struct GCStats
struct LibCall
{
- void (*fn)(void*);
+ void* fn;
uintptr n; // number of parameters
void* args; // parameters
uintptr r1; // return values
@@ -250,11 +268,10 @@ struct WinCallbackContext
struct G
{
// stackguard0 can be set to StackPreempt as opposed to stackguard
- uintptr stackguard0; // cannot move - also known to linker, libmach, runtime/cgo
+ uintptr stackguard0; // cannot move - also known to liblink, libmach, runtime/cgo
uintptr stackbase; // cannot move - also known to libmach, runtime/cgo
- uint32 panicwrap; // cannot move - also known to linker
+ Panic* panic; // cannot move - also known to liblink
Defer* defer;
- Panic* panic;
Gobuf sched;
uintptr syscallstack; // if status==Gsyscall, syscallstack = stackbase to use during gc
uintptr syscallsp; // if status==Gsyscall, syscallsp = sched.sp to use during gc
@@ -264,27 +281,29 @@ struct G
uintptr stack0;
uintptr stacksize;
void* param; // passed parameter on wakeup
- int16 status;
+ uint32 atomicstatus;
int64 goid;
int64 waitsince; // approx time when the G become blocked
- int8* waitreason; // if status==Gwaiting
+ String waitreason; // if status==Gwaiting
G* schedlink;
- bool ispanic;
- bool issystem; // do not output in stack dump
- bool isbackground; // ignore in deadlock detector
+ bool issystem; // do not output in stack dump, ignore in deadlock detector
bool preempt; // preemption signal, duplicates stackguard0 = StackPreempt
bool paniconfault; // panic (instead of crash) on unexpected fault address
+ bool preemptscan; // preempted g does scan for GC
+ bool gcworkdone; // debug: cleared at begining of gc work phase cycle, set by gcphasework, tested at end of cycle
+ bool throwsplit; // must not split stack
int8 raceignore; // ignore race detection events
M* m; // for debuggers, but offset not hard-coded
M* lockedm;
int32 sig;
int32 writenbuf;
- byte* writebuf;
+ Slice writebuf;
uintptr sigcode0;
uintptr sigcode1;
uintptr sigpc;
uintptr gopc; // pc of go statement that created this goroutine
uintptr racectx;
+ SudoG *waiting; // sudog structures this G is waiting on (that have a valid elem ptr)
uintptr end[];
};
@@ -375,7 +394,7 @@ struct M
struct P
{
- Lock;
+ Mutex lock;
int32 id;
uint32 status; // one of Pidle/Prunning/...
@@ -402,6 +421,12 @@ struct P
byte pad[64];
};
+enum {
+ // The max value of GOMAXPROCS.
+ // There are no fundamental restrictions on the value.
+ MaxGomaxprocs = 1<<8,
+};
+
// 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.
@@ -422,10 +447,8 @@ struct Stktop
uintptr stackbase;
Gobuf gobuf;
uint32 argsize;
- uint32 panicwrap;
uint8* argp; // pointer to arguments in old frame
- bool panic; // is this frame the top of a panic?
};
struct SigTab
{
@@ -447,7 +470,7 @@ enum
// Layout of in-memory per-function information prepared by linker
// See http://golang.org/s/go12symtab.
// Keep in sync with linker and with ../../libmach/sym.c
-// and with package debug/gosym.
+// and with package debug/gosym and with symtab.go in package runtime.
struct Func
{
uintptr entry; // start pc
@@ -504,35 +527,6 @@ enum {
};
#endif
-struct Timers
-{
- Lock;
- G *timerproc;
- bool sleeping;
- bool rescheduling;
- Note waitnote;
- Timer **t;
- int32 len;
- int32 cap;
-};
-
-// Package time knows the layout of this structure.
-// If this struct changes, adjust ../time/sleep.go:/runtimeTimer.
-// For GOOS=nacl, package syscall knows the layout of this structure.
-// If this struct changes, adjust ../syscall/net_nacl.go:/runtimeTimer.
-struct Timer
-{
- int32 i; // heap index
-
- // Timer wakes up at when, and then at when+period, ... (period > 0 only)
- // each time calling f(now, arg) in the timer goroutine, so f must be
- // a well-behaved function and not block.
- int64 when;
- int64 period;
- FuncVal *fv;
- Eface arg;
-};
-
// Lock-free stack node.
struct LFNode
{
@@ -579,8 +573,27 @@ struct DebugVars
int32 gcdead;
int32 scheddetail;
int32 schedtrace;
+ int32 scavenge;
+};
+
+// Indicates to write barrier and sychronization task to preform.
+enum
+{ // Synchronization Write barrier
+ GCoff, // stop and start nop
+ GCquiesce, // stop and start nop
+ GCstw, // stop the ps nop
+ GCmark, // scan the stacks and start no white to black
+ GCsweep, // stop and start nop
};
+struct ForceGCState
+{
+ Mutex lock;
+ G* g;
+ uint32 idle;
+};
+
+extern uint32 runtime·gcphase;
extern bool runtime·precisestack;
extern bool runtime·copystack;
@@ -601,93 +614,20 @@ enum {
Structrnd = sizeof(uintreg),
};
-/*
- * type algorithms - known to compiler
- */
-enum
-{
- AMEM,
- AMEM0,
- AMEM8,
- AMEM16,
- AMEM32,
- AMEM64,
- AMEM128,
- ANOEQ,
- ANOEQ0,
- ANOEQ8,
- ANOEQ16,
- ANOEQ32,
- ANOEQ64,
- ANOEQ128,
- ASTRING,
- AINTER,
- ANILINTER,
- ASLICE,
- AFLOAT32,
- AFLOAT64,
- ACPLX64,
- ACPLX128,
- Amax
-};
-typedef struct Alg Alg;
-struct Alg
-{
- FuncVal* hash;
- void (*equal)(bool*, uintptr, void*, void*);
- void (*print)(uintptr, void*);
- void (*copy)(uintptr, void*, void*);
-};
-
-extern Alg runtime·algarray[Amax];
-
byte* runtime·startup_random_data;
uint32 runtime·startup_random_data_len;
-void runtime·get_random_data(byte**, int32*);
enum {
// hashinit wants this many random bytes
HashRandomBytes = 32
};
-void runtime·hashinit(void);
-
-uintptr runtime·memhash(void*, uintptr, uintptr);
-uintptr runtime·nohash(void*, uintptr, uintptr);
-uintptr runtime·strhash(void*, uintptr, uintptr);
-uintptr runtime·interhash(void*, uintptr, uintptr);
-uintptr runtime·nilinterhash(void*, uintptr, uintptr);
-uintptr runtime·f32hash(void*, uintptr, uintptr);
-uintptr runtime·f64hash(void*, uintptr, uintptr);
-uintptr runtime·c64hash(void*, uintptr, uintptr);
-uintptr runtime·c128hash(void*, uintptr, uintptr);
-uintptr runtime·aeshash(void*, uintptr, uintptr);
-uintptr runtime·aeshash32(void*, uintptr, uintptr);
-uintptr runtime·aeshash64(void*, uintptr, uintptr);
-uintptr runtime·aeshashstr(void*, uintptr, uintptr);
-
-void runtime·memequal(bool*, uintptr, void*, void*);
-void runtime·noequal(bool*, uintptr, void*, void*);
-void runtime·strequal(bool*, uintptr, void*, void*);
-void runtime·interequal(bool*, uintptr, void*, void*);
-void runtime·nilinterequal(bool*, uintptr, void*, void*);
-
-bool runtime·memeq(void*, void*, uintptr);
-
-void runtime·memprint(uintptr, void*);
-void runtime·strprint(uintptr, void*);
-void runtime·interprint(uintptr, void*);
-void runtime·nilinterprint(uintptr, void*);
-
-void runtime·memcopy(uintptr, void*, void*);
-void runtime·memcopy8(uintptr, void*, void*);
-void runtime·memcopy16(uintptr, void*, void*);
-void runtime·memcopy32(uintptr, void*, void*);
-void runtime·memcopy64(uintptr, void*, void*);
-void runtime·memcopy128(uintptr, void*, void*);
-void runtime·strcopy(uintptr, void*, void*);
-void runtime·algslicecopy(uintptr, void*, void*);
-void runtime·intercopy(uintptr, void*, void*);
-void runtime·nilintercopy(uintptr, void*, void*);
+
+uint32 runtime·readgstatus(G*);
+void runtime·casgstatus(G*, uint32, uint32);
+void runtime·quiesce(G*);
+bool runtime·stopg(G*);
+void runtime·restartg(G*);
+void runtime·gcphasework(G*);
/*
* deferred subroutine calls
@@ -696,25 +636,23 @@ struct Defer
{
int32 siz;
bool special; // not part of defer frame
- byte* argp; // where args were copied from
- byte* pc;
+ uintptr argp; // where args were copied from
+ uintptr pc;
FuncVal* fn;
Defer* link;
void* args[1]; // padded to actual size
};
// argp used in Defer structs when there is no argp.
-// TODO(rsc): Maybe we could use nil instead, but we've always used -1
-// and I don't want to change this days before the Go 1.3 release.
-#define NoArgs ((byte*)-1)
+#define NoArgs ((uintptr)-1)
/*
* panics
*/
struct Panic
{
+ uintptr argp; // pointer to arguments of deferred call run during panic; cannot move - known to liblink
Eface arg; // argument to panic
- uintptr stackbase; // g->stackbase in panic
Panic* link; // link to earlier panic
Defer* defer; // current executing defer
bool recovered; // whether this panic is over
@@ -733,12 +671,12 @@ struct Stkframe
uintptr lr; // program counter at caller aka link register
uintptr sp; // stack pointer at pc
uintptr fp; // stack pointer at caller aka frame pointer
- byte* varp; // top of local variables
- byte* argp; // pointer to function arguments
+ uintptr varp; // top of local variables
+ uintptr argp; // pointer to function arguments
uintptr arglen; // number of bytes at argp
};
-int32 runtime·gentraceback(uintptr, uintptr, uintptr, G*, int32, uintptr*, int32, bool(*)(Stkframe*, void*), void*, bool);
+intgo runtime·gentraceback(uintptr, uintptr, uintptr, G*, intgo, uintptr*, intgo, bool(**)(Stkframe*, void*), void*, bool);
void runtime·traceback(uintptr pc, uintptr sp, uintptr lr, G* gp);
void runtime·tracebackothers(G*);
bool runtime·haszeroargs(uintptr pc);
@@ -755,10 +693,11 @@ enum
extern String runtime·emptystring;
extern uintptr runtime·zerobase;
extern G** runtime·allg;
+extern Slice runtime·allgs; // []*G
extern uintptr runtime·allglen;
extern G* runtime·lastg;
extern M* runtime·allm;
-extern P** runtime·allp;
+extern P* runtime·allp[MaxGomaxprocs+1];
extern int32 runtime·gomaxprocs;
extern uint32 runtime·needextram;
extern uint32 runtime·panicking;
@@ -771,6 +710,8 @@ extern uint32 runtime·cpuid_ecx;
extern uint32 runtime·cpuid_edx;
extern DebugVars runtime·debug;
extern uintptr runtime·maxstacksize;
+extern Note runtime·signote;
+extern ForceGCState runtime·forcegc;
/*
* common functions and data
@@ -825,11 +766,10 @@ void runtime·panicstring(int8*);
bool runtime·canpanic(G*);
void runtime·prints(int8*);
void runtime·printf(int8*, ...);
-int32 runtime·snprintf(byte*, int32, int8*, ...);
+void runtime·snprintf(byte*, int32, int8*, ...);
byte* runtime·mchr(byte*, byte, byte*);
int32 runtime·mcmp(byte*, byte*, uintptr);
void runtime·memmove(void*, void*, uintptr);
-void* runtime·mal(uintptr);
String runtime·catstring(String, String);
String runtime·gostring(byte*);
String runtime·gostringn(byte*, intgo);
@@ -846,7 +786,7 @@ int32 runtime·read(int32, void*, int32);
int32 runtime·write(uintptr, void*, int32); // use uintptr to accommodate windows.
int32 runtime·close(int32);
int32 runtime·mincore(void*, uintptr, byte*);
-void runtime·jmpdefer(FuncVal*, void*);
+void runtime·jmpdefer(FuncVal*, uintptr);
void runtime·exit1(int32);
void runtime·ready(G*);
byte* runtime·getenv(int8*);
@@ -873,18 +813,14 @@ void runtime·shrinkstack(G*);
MCache* runtime·allocmcache(void);
void runtime·freemcache(MCache*);
void runtime·mallocinit(void);
-void runtime·chaninit(void);
-bool runtime·ifaceeq_c(Iface, Iface);
-bool runtime·efaceeq_c(Eface, Eface);
-uintptr runtime·ifacehash(Iface, uintptr);
-uintptr runtime·efacehash(Eface, uintptr);
-void* runtime·malloc(uintptr size);
+void runtime·gcinit(void);
+void* runtime·mallocgc(uintptr size, Type* typ, uint32 flag);
void runtime·runpanic(Panic*);
uintptr runtime·getcallersp(void*);
int32 runtime·mcount(void);
int32 runtime·gcount(void);
-void runtime·mcall(void(*)(G*));
-void runtime·onM(void(*)(void));
+void runtime·mcall(void(**)(G*));
+void runtime·onM(void(**)(void));
uint32 runtime·fastrand1(void);
void runtime·rewindmorestack(Gobuf*);
int32 runtime·timediv(int64, int32, int32*);
@@ -907,27 +843,29 @@ void runtime·atomicstore64(uint64 volatile*, uint64);
uint64 runtime·atomicload64(uint64 volatile*);
void* runtime·atomicloadp(void* volatile*);
void runtime·atomicstorep(void* volatile*, void*);
+void runtime·atomicor8(byte volatile*, byte);
void runtime·setg(G*);
void runtime·newextram(void);
void runtime·exit(int32);
void runtime·breakpoint(void);
void runtime·gosched(void);
-void runtime·gosched0(G*);
+void runtime·gosched_m(G*);
void runtime·schedtrace(bool);
-void runtime·park(bool(*)(G*, void*), void*, int8*);
-void runtime·parkunlock(Lock*, int8*);
-void runtime·tsleep(int64, int8*);
+void runtime·park(bool(*)(G*, void*), void*, String);
+void runtime·parkunlock(Mutex*, String);
+void runtime·tsleep(int64, String);
M* runtime·newm(void);
void runtime·goexit(void);
void runtime·asmcgocall(void (*fn)(void*), void*);
+int32 runtime·asmcgocall_errno(void (*fn)(void*), void*);
void runtime·entersyscall(void);
void runtime·entersyscallblock(void);
void runtime·exitsyscall(void);
G* runtime·newproc1(FuncVal*, byte*, int32, int32, void*);
bool runtime·sigsend(int32 sig);
-int32 runtime·callers(int32, uintptr*, int32);
-int32 runtime·gcallers(G*, int32, uintptr*, int32);
+intgo runtime·callers(intgo, uintptr*, intgo);
+intgo runtime·gcallers(G*, intgo, uintptr*, intgo);
int64 runtime·nanotime(void); // monotonic time
int64 runtime·unixnanotime(void); // real time, can skip
void runtime·dopanic(int32);
@@ -936,32 +874,24 @@ void runtime·freezetheworld(void);
void runtime·unwindstack(G*, byte*);
void runtime·sigprof(uint8 *pc, uint8 *sp, uint8 *lr, G *gp, M *mp);
void runtime·resetcpuprofiler(int32);
-void runtime·setcpuprofilerate(void(*)(uintptr*, int32), int32);
+void runtime·setcpuprofilerate(int32);
void runtime·usleep(uint32);
int64 runtime·cputicks(void);
int64 runtime·tickspersecond(void);
-void runtime·blockevent(int64, int32);
-extern int64 runtime·blockprofilerate;
-void runtime·addtimer(Timer*);
-bool runtime·deltimer(Timer*);
+void runtime·blockevent(int64, intgo);
G* runtime·netpoll(bool);
-void runtime·netpollinit(void);
-int32 runtime·netpollopen(uintptr, PollDesc*);
-int32 runtime·netpollclose(uintptr);
void runtime·netpollready(G**, PollDesc*, int32);
uintptr runtime·netpollfd(PollDesc*);
-void runtime·netpollarm(PollDesc*, int32);
void** runtime·netpolluser(PollDesc*);
bool runtime·netpollclosing(PollDesc*);
void runtime·netpolllock(PollDesc*);
void runtime·netpollunlock(PollDesc*);
void runtime·crash(void);
void runtime·parsedebugvars(void);
-void _rt0_go(void);
void* runtime·funcdata(Func*, int32);
-int32 runtime·setmaxthreads(int32);
+void runtime·setmaxthreads_m(void);
G* runtime·timejump(void);
-void runtime·iterate_itabs(void (*callback)(Itab*));
+void runtime·iterate_itabs(void (**callback)(Itab*));
void runtime·iterate_finq(void (*callback)(FuncVal*, byte*, uintptr, Type*, PtrType*));
#pragma varargck argpos runtime·printf 1
@@ -988,10 +918,10 @@ extern uint32 runtime·worldsema;
* 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 Lock is unlocked (no need to initialize each lock).
+ * a zeroed Mutex is unlocked (no need to initialize each lock).
*/
-void runtime·lock(Lock*);
-void runtime·unlock(Lock*);
+void runtime·lock(Mutex*);
+void runtime·unlock(Mutex*);
/*
* sleep and wakeup on one-time events.
@@ -1032,7 +962,7 @@ void runtime·futexsleep(uint32*, uint32, int64);
void runtime·futexwakeup(uint32*, uint32);
/*
- * Lock-free stack.
+ * Mutex-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.
@@ -1065,26 +995,24 @@ void runtime·madvise(byte*, uintptr, int32);
void runtime·memclr(byte*, uintptr);
void runtime·setcallerpc(void*, void*);
void* runtime·getcallerpc(void*);
-void runtime·printbool_c(bool);
-void runtime·printbyte_c(int8);
-void runtime·printfloat_c(float64);
-void runtime·printint_c(int64);
-void runtime·printiface_c(Iface);
-void runtime·printeface_c(Eface);
-void runtime·printstring_c(String);
-void runtime·printpc_c(void*);
-void runtime·printpointer_c(void*);
-void runtime·printuint_c(uint64);
-void runtime·printhex_c(uint64);
-void runtime·printslice_c(Slice);
-void runtime·printcomplex_c(Complex128);
+void runtime·printbool(bool);
+void runtime·printbyte(int8);
+void runtime·printfloat(float64);
+void runtime·printint(int64);
+void runtime·printiface(Iface);
+void runtime·printeface(Eface);
+void runtime·printstring(String);
+void runtime·printpc(void*);
+void runtime·printpointer(void*);
+void runtime·printuint(uint64);
+void runtime·printhex(uint64);
+void runtime·printslice(Slice);
+void runtime·printcomplex(Complex128);
/*
* runtime go-called
*/
-void runtime·newstackcall(FuncVal*, byte*, uint32);
-void reflect·call(FuncVal*, byte*, uint32, uint32);
-void runtime·panic(Eface);
+void runtime·gopanic(Eface);
void runtime·panicindex(void);
void runtime·panicslice(void);
void runtime·panicdivide(void);
@@ -1128,7 +1056,6 @@ void runtime·procyield(uint32);
void runtime·osyield(void);
void runtime·lockOSThread(void);
void runtime·unlockOSThread(void);
-bool runtime·lockedOSThread(void);
bool runtime·showframe(Func*, G*);
void runtime·printcreatedby(G*);
diff --git a/src/pkg/runtime/runtime1.goc b/src/pkg/runtime/runtime1.goc
deleted file mode 100644
index a95a4f944d..0000000000
--- a/src/pkg/runtime/runtime1.goc
+++ /dev/null
@@ -1,128 +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
-#include "runtime.h"
-#include "arch_GOARCH.h"
-#include "type.h"
-
-func GOMAXPROCS(n int) (ret int) {
- ret = runtime·gomaxprocsfunc(n);
-}
-
-func NumCPU() (ret int) {
- ret = runtime·ncpu;
-}
-
-func NumCgoCall() (ret int64) {
- M *mp;
-
- ret = 0;
- for(mp=runtime·atomicloadp(&runtime·allm); mp; mp=mp->alllink)
- ret += mp->ncgocall;
-}
-
-func newParFor(nthrmax uint32) (desc *ParFor) {
- desc = runtime·parforalloc(nthrmax);
-}
-
-func parForSetup(desc *ParFor, nthr uint32, n uint32, ctx *byte, wait bool, body *byte) {
- runtime·parforsetup(desc, nthr, n, ctx, wait, *(void(**)(ParFor*, uint32))body);
-}
-
-func parForDo(desc *ParFor) {
- runtime·parfordo(desc);
-}
-
-func parForIters(desc *ParFor, tid uintptr) (start uintptr, end uintptr) {
- runtime·parforiters(desc, tid, &start, &end);
-}
-
-func gogoBytes() (x int32) {
- x = RuntimeGogoBytes;
-}
-
-func typestring(e Eface) (s String) {
- s = *e.type->string;
-}
-
-func golockedOSThread() (ret bool) {
- ret = runtime·lockedOSThread();
-}
-
-func NumGoroutine() (ret int) {
- ret = runtime·gcount();
-}
-
-func getgoroot() (out String) {
- byte *p;
-
- p = runtime·getenv("GOROOT");
- out = runtime·gostringnocopy(p);
-}
-
-/*
- * We assume that all architectures turn faults and the like
- * into apparent calls to runtime.sigpanic. If we see a "call"
- * to runtime.sigpanic, we do not back up the PC to find the
- * line number of the CALL instruction, because there is no CALL.
- */
-void runtime·sigpanic(void);
-
-func Caller(skip int) (retpc uintptr, retfile String, retline int, retbool bool) {
- Func *f, *g;
- uintptr pc;
- uintptr rpc[2];
-
- /*
- * Ask for two PCs: the one we were asked for
- * and what it called, so that we can see if it
- * "called" sigpanic.
- */
- retpc = 0;
- if(runtime·callers(1+skip-1, rpc, 2) < 2) {
- retfile = runtime·emptystring;
- retline = 0;
- retbool = false;
- } else if((f = runtime·findfunc(rpc[1])) == nil) {
- retfile = runtime·emptystring;
- retline = 0;
- retbool = true; // have retpc at least
- } else {
- retpc = rpc[1];
- pc = retpc;
- g = runtime·findfunc(rpc[0]);
- if(pc > f->entry && (g == nil || g->entry != (uintptr)runtime·sigpanic))
- pc--;
- retline = runtime·funcline(f, pc, &retfile);
- retbool = true;
- }
-}
-
-func Callers(skip int, pc Slice) (retn int) {
- // runtime.callers uses pc.array==nil as a signal
- // to print a stack trace. Pick off 0-length pc here
- // so that we don't let a nil pc slice get to it.
- if(pc.len == 0)
- retn = 0;
- else
- retn = runtime·callers(skip, (uintptr*)pc.array, pc.len);
-}
-
-func runtime∕pprof·runtime_cyclesPerSecond() (res int64) {
- res = runtime·tickspersecond();
-}
-
-func sync·runtime_procPin() (p int) {
- M *mp;
-
- mp = g->m;
- // Disable preemption.
- mp->locks++;
- p = mp->p->id;
-}
-
-func sync·runtime_procUnpin() {
- g->m->locks--;
-}
diff --git a/src/pkg/runtime/select.go b/src/pkg/runtime/select.go
new file mode 100644
index 0000000000..6d2531e7f8
--- /dev/null
+++ b/src/pkg/runtime/select.go
@@ -0,0 +1,636 @@
+// 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
+
+// This file contains the implementation of Go select statements.
+
+import "unsafe"
+
+const (
+ debugSelect = false
+)
+
+var (
+ chansendpc = funcPC(chansend)
+ chanrecvpc = funcPC(chanrecv)
+)
+
+func selectsize(size uintptr) uintptr {
+ selsize := unsafe.Sizeof(_select{}) +
+ (size-1)*unsafe.Sizeof(_select{}.scase[0]) +
+ size*unsafe.Sizeof(*_select{}.lockorder) +
+ size*unsafe.Sizeof(*_select{}.pollorder)
+ return round(selsize, _Int64Align)
+}
+
+func newselect(sel *_select, selsize int64, size int32) {
+ if selsize != int64(selectsize(uintptr(size))) {
+ print("runtime: bad select size ", selsize, ", want ", selectsize(uintptr(size)), "\n")
+ gothrow("bad select size")
+ }
+ sel.tcase = uint16(size)
+ sel.ncase = 0
+ sel.lockorder = (**hchan)(add(unsafe.Pointer(&sel.scase), uintptr(size)*unsafe.Sizeof(_select{}.scase[0])))
+ sel.pollorder = (*uint16)(add(unsafe.Pointer(sel.lockorder), uintptr(size)*unsafe.Sizeof(*_select{}.lockorder)))
+
+ if debugSelect {
+ print("newselect s=", sel, " size=", size, "\n")
+ }
+}
+
+//go:nosplit
+func selectsend(sel *_select, c *hchan, elem unsafe.Pointer) (selected bool) {
+ // nil cases do not compete
+ if c != nil {
+ selectsendImpl(sel, c, getcallerpc(unsafe.Pointer(&sel)), elem, uintptr(unsafe.Pointer(&selected))-uintptr(unsafe.Pointer(&sel)))
+ }
+ return
+}
+
+// cut in half to give stack a chance to split
+func selectsendImpl(sel *_select, c *hchan, pc uintptr, elem unsafe.Pointer, so uintptr) {
+ i := sel.ncase
+ if i >= sel.tcase {
+ gothrow("selectsend: too many cases")
+ }
+ sel.ncase = i + 1
+ cas := (*scase)(add(unsafe.Pointer(&sel.scase), uintptr(i)*unsafe.Sizeof(sel.scase[0])))
+
+ cas.pc = pc
+ cas._chan = c
+ cas.so = uint16(so)
+ cas.kind = _CaseSend
+ cas.elem = elem
+
+ if debugSelect {
+ print("selectsend s=", sel, " pc=", hex(cas.pc), " chan=", cas._chan, " so=", cas.so, "\n")
+ }
+}
+
+//go:nosplit
+func selectrecv(sel *_select, c *hchan, elem unsafe.Pointer) (selected bool) {
+ // nil cases do not compete
+ if c != nil {
+ selectrecvImpl(sel, c, getcallerpc(unsafe.Pointer(&sel)), elem, nil, uintptr(unsafe.Pointer(&selected))-uintptr(unsafe.Pointer(&sel)))
+ }
+ return
+}
+
+//go:nosplit
+func selectrecv2(sel *_select, c *hchan, elem unsafe.Pointer, received *bool) (selected bool) {
+ // nil cases do not compete
+ if c != nil {
+ selectrecvImpl(sel, c, getcallerpc(unsafe.Pointer(&sel)), elem, received, uintptr(unsafe.Pointer(&selected))-uintptr(unsafe.Pointer(&sel)))
+ }
+ return
+}
+
+func selectrecvImpl(sel *_select, c *hchan, pc uintptr, elem unsafe.Pointer, received *bool, so uintptr) {
+ i := sel.ncase
+ if i >= sel.tcase {
+ gothrow("selectrecv: too many cases")
+ }
+ sel.ncase = i + 1
+ cas := (*scase)(add(unsafe.Pointer(&sel.scase), uintptr(i)*unsafe.Sizeof(sel.scase[0])))
+ cas.pc = pc
+ cas._chan = c
+ cas.so = uint16(so)
+ cas.kind = _CaseRecv
+ cas.elem = elem
+ cas.receivedp = received
+
+ if debugSelect {
+ print("selectrecv s=", sel, " pc=", hex(cas.pc), " chan=", cas._chan, " so=", cas.so, "\n")
+ }
+}
+
+//go:nosplit
+func selectdefault(sel *_select) (selected bool) {
+ selectdefaultImpl(sel, getcallerpc(unsafe.Pointer(&sel)), uintptr(unsafe.Pointer(&selected))-uintptr(unsafe.Pointer(&sel)))
+ return
+}
+
+func selectdefaultImpl(sel *_select, callerpc uintptr, so uintptr) {
+ i := sel.ncase
+ if i >= sel.tcase {
+ gothrow("selectdefault: too many cases")
+ }
+ sel.ncase = i + 1
+ cas := (*scase)(add(unsafe.Pointer(&sel.scase), uintptr(i)*unsafe.Sizeof(sel.scase[0])))
+ cas.pc = callerpc
+ cas._chan = nil
+ cas.so = uint16(so)
+ cas.kind = _CaseDefault
+
+ if debugSelect {
+ print("selectdefault s=", sel, " pc=", hex(cas.pc), " so=", cas.so, "\n")
+ }
+}
+
+func sellock(sel *_select) {
+ lockslice := sliceStruct{unsafe.Pointer(sel.lockorder), int(sel.ncase), int(sel.ncase)}
+ lockorder := *(*[]*hchan)(unsafe.Pointer(&lockslice))
+ var c *hchan
+ for _, c0 := range lockorder {
+ if c0 != nil && c0 != c {
+ c = c0
+ lock(&c.lock)
+ }
+ }
+}
+
+func selunlock(sel *_select) {
+ // We must be very careful here to not touch sel after we have unlocked
+ // the last lock, because sel can be freed right after the last unlock.
+ // Consider the following situation.
+ // First M calls runtime·park() in runtime·selectgo() passing the sel.
+ // Once runtime·park() has unlocked the last lock, another M makes
+ // the G that calls select runnable again and schedules it for execution.
+ // When the G runs on another M, it locks all the locks and frees sel.
+ // Now if the first M touches sel, it will access freed memory.
+ n := int(sel.ncase)
+ r := 0
+ lockslice := sliceStruct{unsafe.Pointer(sel.lockorder), n, n}
+ lockorder := *(*[]*hchan)(unsafe.Pointer(&lockslice))
+ // skip the default case
+ if n > 0 && lockorder[0] == nil {
+ r = 1
+ }
+ for i := n - 1; i >= r; i-- {
+ c := lockorder[i]
+ if i > 0 && c == lockorder[i-1] {
+ continue // will unlock it on the next iteration
+ }
+ unlock(&c.lock)
+ }
+}
+
+func selparkcommit(gp *g, sel *_select) bool {
+ selunlock(sel)
+ return true
+}
+
+func block() {
+ gopark(nil, nil, "select (no cases)") // forever
+}
+
+// overwrites return pc on stack to signal which case of the select
+// to run, so cannot appear at the top of a split stack.
+//go:nosplit
+func selectgo(sel *_select) {
+ pc, offset := selectgoImpl(sel)
+ *(*bool)(add(unsafe.Pointer(&sel), uintptr(offset))) = true
+ setcallerpc(unsafe.Pointer(&sel), pc)
+}
+
+// selectgoImpl returns scase.pc and scase.so for the select
+// case which fired.
+func selectgoImpl(sel *_select) (uintptr, uint16) {
+ if debugSelect {
+ print("select: sel=", sel, "\n")
+ }
+
+ scaseslice := sliceStruct{unsafe.Pointer(&sel.scase), int(sel.ncase), int(sel.ncase)}
+ scases := *(*[]scase)(unsafe.Pointer(&scaseslice))
+
+ var t0 int64
+ if blockprofilerate > 0 {
+ t0 = cputicks()
+ for i := 0; i < int(sel.ncase); i++ {
+ scases[i].releasetime = -1
+ }
+ }
+
+ // The compiler rewrites selects that statically have
+ // only 0 or 1 cases plus default into simpler constructs.
+ // The only way we can end up with such small sel.ncase
+ // values here is for a larger select in which most channels
+ // have been nilled out. The general code handles those
+ // cases correctly, and they are rare enough not to bother
+ // optimizing (and needing to test).
+
+ // generate permuted order
+ pollslice := sliceStruct{unsafe.Pointer(sel.pollorder), int(sel.ncase), int(sel.ncase)}
+ pollorder := *(*[]uint16)(unsafe.Pointer(&pollslice))
+ for i := 0; i < int(sel.ncase); i++ {
+ pollorder[i] = uint16(i)
+ }
+ for i := 1; i < int(sel.ncase); i++ {
+ o := pollorder[i]
+ j := int(fastrand1()) % (i + 1)
+ pollorder[i] = pollorder[j]
+ pollorder[j] = o
+ }
+
+ // sort the cases by Hchan address to get the locking order.
+ // simple heap sort, to guarantee n log n time and constant stack footprint.
+ lockslice := sliceStruct{unsafe.Pointer(sel.lockorder), int(sel.ncase), int(sel.ncase)}
+ lockorder := *(*[]*hchan)(unsafe.Pointer(&lockslice))
+ for i := 0; i < int(sel.ncase); i++ {
+ j := i
+ c := scases[j]._chan
+ for j > 0 && lockorder[(j-1)/2].sortkey() < c.sortkey() {
+ k := (j - 1) / 2
+ lockorder[j] = lockorder[k]
+ j = k
+ }
+ lockorder[j] = c
+ }
+ for i := int(sel.ncase) - 1; i >= 0; i-- {
+ c := lockorder[i]
+ lockorder[i] = lockorder[0]
+ j := 0
+ for {
+ k := j*2 + 1
+ if k >= i {
+ break
+ }
+ if k+1 < i && lockorder[k].sortkey() < lockorder[k+1].sortkey() {
+ k++
+ }
+ if c.sortkey() < lockorder[k].sortkey() {
+ lockorder[j] = lockorder[k]
+ j = k
+ continue
+ }
+ break
+ }
+ lockorder[j] = c
+ }
+ /*
+ for i := 0; i+1 < int(sel.ncase); i++ {
+ if lockorder[i].sortkey() > lockorder[i+1].sortkey() {
+ print("i=", i, " x=", lockorder[i], " y=", lockorder[i+1], "\n")
+ gothrow("select: broken sort")
+ }
+ }
+ */
+
+ // lock all the channels involved in the select
+ sellock(sel)
+
+ var (
+ gp *g
+ done uint32
+ sg *sudog
+ c *hchan
+ k *scase
+ sglist *sudog
+ sgnext *sudog
+ )
+
+loop:
+ // pass 1 - look for something already waiting
+ var dfl *scase
+ var cas *scase
+ for i := 0; i < int(sel.ncase); i++ {
+ cas = &scases[pollorder[i]]
+ c = cas._chan
+
+ switch cas.kind {
+ case _CaseRecv:
+ if c.dataqsiz > 0 {
+ if c.qcount > 0 {
+ goto asyncrecv
+ }
+ } else {
+ sg = c.sendq.dequeue()
+ if sg != nil {
+ goto syncrecv
+ }
+ }
+ if c.closed != 0 {
+ goto rclose
+ }
+
+ case _CaseSend:
+ if raceenabled {
+ racereadpc(unsafe.Pointer(c), cas.pc, chansendpc)
+ }
+ if c.closed != 0 {
+ goto sclose
+ }
+ if c.dataqsiz > 0 {
+ if c.qcount < c.dataqsiz {
+ goto asyncsend
+ }
+ } else {
+ sg = c.recvq.dequeue()
+ if sg != nil {
+ goto syncsend
+ }
+ }
+
+ case _CaseDefault:
+ dfl = cas
+ }
+ }
+
+ if dfl != nil {
+ selunlock(sel)
+ cas = dfl
+ goto retc
+ }
+
+ // pass 2 - enqueue on all chans
+ gp = getg()
+ done = 0
+ for i := 0; i < int(sel.ncase); i++ {
+ cas = &scases[pollorder[i]]
+ c = cas._chan
+ sg := acquireSudog()
+ sg.g = gp
+ // Note: selectdone is adjusted for stack copies in stack.c:adjustsudogs
+ sg.selectdone = (*uint32)(noescape(unsafe.Pointer(&done)))
+ sg.elem = cas.elem
+ sg.releasetime = 0
+ if t0 != 0 {
+ sg.releasetime = -1
+ }
+ sg.waitlink = gp.waiting
+ gp.waiting = sg
+
+ switch cas.kind {
+ case _CaseRecv:
+ c.recvq.enqueue(sg)
+
+ case _CaseSend:
+ c.sendq.enqueue(sg)
+ }
+ }
+
+ // wait for someone to wake us up
+ gp.param = nil
+ gopark(unsafe.Pointer(funcPC(selparkcommit)), unsafe.Pointer(sel), "select")
+
+ // someone woke us up
+ sellock(sel)
+ sg = (*sudog)(gp.param)
+
+ // pass 3 - dequeue from unsuccessful chans
+ // otherwise they stack up on quiet channels
+ // record the successful case, if any.
+ // We singly-linked up the SudoGs in case order, so when
+ // iterating through the linked list they are in reverse order.
+ cas = nil
+ sglist = gp.waiting
+ gp.waiting = nil
+ for i := int(sel.ncase) - 1; i >= 0; i-- {
+ k = &scases[pollorder[i]]
+ if sglist.releasetime > 0 {
+ k.releasetime = sglist.releasetime
+ }
+ if sg == sglist {
+ cas = k
+ } else {
+ c = k._chan
+ if k.kind == _CaseSend {
+ c.sendq.dequeueg(gp)
+ } else {
+ c.recvq.dequeueg(gp)
+ }
+ }
+ sgnext = sglist.waitlink
+ releaseSudog(sglist)
+ sglist = sgnext
+ }
+
+ if cas == nil {
+ goto loop
+ }
+
+ c = cas._chan
+
+ if c.dataqsiz > 0 {
+ gothrow("selectgo: shouldn't happen")
+ }
+
+ if debugSelect {
+ print("wait-return: sel=", sel, " c=", c, " cas=", cas, " kind=", cas.kind, "\n")
+ }
+
+ if cas.kind == _CaseRecv {
+ if cas.receivedp != nil {
+ *cas.receivedp = true
+ }
+ }
+
+ if raceenabled {
+ if cas.kind == _CaseRecv && cas.elem != nil {
+ raceWriteObjectPC(c.elemtype, cas.elem, cas.pc, chanrecvpc)
+ } else if cas.kind == _CaseSend {
+ raceReadObjectPC(c.elemtype, cas.elem, cas.pc, chansendpc)
+ }
+ }
+
+ selunlock(sel)
+ goto retc
+
+asyncrecv:
+ // can receive from buffer
+ if raceenabled {
+ if cas.elem != nil {
+ raceWriteObjectPC(c.elemtype, cas.elem, cas.pc, chanrecvpc)
+ }
+ raceacquire(chanbuf(c, c.recvx))
+ racerelease(chanbuf(c, c.recvx))
+ }
+ if cas.receivedp != nil {
+ *cas.receivedp = true
+ }
+ if cas.elem != nil {
+ memmove(cas.elem, chanbuf(c, c.recvx), uintptr(c.elemsize))
+ }
+ memclr(chanbuf(c, c.recvx), uintptr(c.elemsize))
+ c.recvx++
+ if c.recvx == c.dataqsiz {
+ c.recvx = 0
+ }
+ c.qcount--
+ sg = c.sendq.dequeue()
+ if sg != nil {
+ gp = sg.g
+ selunlock(sel)
+ if sg.releasetime != 0 {
+ sg.releasetime = cputicks()
+ }
+ goready(gp)
+ } else {
+ selunlock(sel)
+ }
+ goto retc
+
+asyncsend:
+ // can send to buffer
+ if raceenabled {
+ raceacquire(chanbuf(c, c.sendx))
+ racerelease(chanbuf(c, c.sendx))
+ raceReadObjectPC(c.elemtype, cas.elem, cas.pc, chansendpc)
+ }
+ memmove(chanbuf(c, c.sendx), cas.elem, uintptr(c.elemsize))
+ c.sendx++
+ if c.sendx == c.dataqsiz {
+ c.sendx = 0
+ }
+ c.qcount++
+ sg = c.recvq.dequeue()
+ if sg != nil {
+ gp = sg.g
+ selunlock(sel)
+ if sg.releasetime != 0 {
+ sg.releasetime = cputicks()
+ }
+ goready(gp)
+ } else {
+ selunlock(sel)
+ }
+ goto retc
+
+syncrecv:
+ // can receive from sleeping sender (sg)
+ if raceenabled {
+ if cas.elem != nil {
+ raceWriteObjectPC(c.elemtype, cas.elem, cas.pc, chanrecvpc)
+ }
+ racesync(c, sg)
+ }
+ selunlock(sel)
+ if debugSelect {
+ print("syncrecv: sel=", sel, " c=", c, "\n")
+ }
+ if cas.receivedp != nil {
+ *cas.receivedp = true
+ }
+ if cas.elem != nil {
+ memmove(cas.elem, sg.elem, uintptr(c.elemsize))
+ }
+ gp = sg.g
+ gp.param = unsafe.Pointer(sg)
+ if sg.releasetime != 0 {
+ sg.releasetime = cputicks()
+ }
+ goready(gp)
+ goto retc
+
+rclose:
+ // read at end of closed channel
+ selunlock(sel)
+ if cas.receivedp != nil {
+ *cas.receivedp = false
+ }
+ if cas.elem != nil {
+ memclr(cas.elem, uintptr(c.elemsize))
+ }
+ if raceenabled {
+ raceacquire(unsafe.Pointer(c))
+ }
+ goto retc
+
+syncsend:
+ // can send to sleeping receiver (sg)
+ if raceenabled {
+ raceReadObjectPC(c.elemtype, cas.elem, cas.pc, chansendpc)
+ racesync(c, sg)
+ }
+ selunlock(sel)
+ if debugSelect {
+ print("syncsend: sel=", sel, " c=", c, "\n")
+ }
+ if sg.elem != nil {
+ memmove(sg.elem, cas.elem, uintptr(c.elemsize))
+ }
+ gp = sg.g
+ gp.param = unsafe.Pointer(sg)
+ if sg.releasetime != 0 {
+ sg.releasetime = cputicks()
+ }
+ goready(gp)
+
+retc:
+ if cas.releasetime > 0 {
+ blockevent(cas.releasetime-t0, 2)
+ }
+ return cas.pc, cas.so
+
+sclose:
+ // send on closed channel
+ selunlock(sel)
+ panic("send on closed channel")
+}
+
+func (c *hchan) sortkey() uintptr {
+ // TODO(khr): if we have a moving garbage collector, we'll need to
+ // change this function.
+ return uintptr(unsafe.Pointer(c))
+}
+
+// A runtimeSelect is a single case passed to rselect.
+// This must match ../reflect/value.go:/runtimeSelect
+type runtimeSelect struct {
+ dir selectDir
+ typ unsafe.Pointer // channel type (not used here)
+ ch *hchan // channel
+ val unsafe.Pointer // ptr to data (SendDir) or ptr to receive buffer (RecvDir)
+}
+
+// These values must match ../reflect/value.go:/SelectDir.
+type selectDir int
+
+const (
+ _ selectDir = iota
+ selectSend // case Chan <- Send
+ selectRecv // case <-Chan:
+ selectDefault // default
+)
+
+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 := (*_select)(gomallocgc(size, nil, flagNoScan))
+ newselect(sel, int64(size), int32(len(cases)))
+ r := new(bool)
+ for i := range cases {
+ rc := &cases[i]
+ switch rc.dir {
+ case selectDefault:
+ selectdefaultImpl(sel, uintptr(i), 0)
+ case selectSend:
+ if rc.ch == nil {
+ break
+ }
+ selectsendImpl(sel, rc.ch, uintptr(i), rc.val, 0)
+ case selectRecv:
+ if rc.ch == nil {
+ break
+ }
+ selectrecvImpl(sel, rc.ch, uintptr(i), rc.val, r, 0)
+ }
+ }
+
+ pc, _ := selectgoImpl(sel)
+ chosen = int(pc)
+ recvOK = *r
+ return
+}
+
+func (q *waitq) dequeueg(gp *g) {
+ var prevsgp *sudog
+ l := &q.first
+ for {
+ sgp := *l
+ if sgp == nil {
+ return
+ }
+ if sgp.g == gp {
+ *l = sgp.next
+ if q.last == sgp {
+ q.last = prevsgp
+ }
+ return
+ }
+ l = &sgp.next
+ prevsgp = sgp
+ }
+}
diff --git a/src/pkg/runtime/sema.go b/src/pkg/runtime/sema.go
new file mode 100644
index 0000000000..87ba5463b6
--- /dev/null
+++ b/src/pkg/runtime/sema.go
@@ -0,0 +1,266 @@
+// 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.
+
+// Semaphore implementation exposed to Go.
+// Intended use is provide a sleep and wakeup
+// primitive that can be used in the contended case
+// of other synchronization primitives.
+// Thus it targets the same goal as Linux's futex,
+// but it has much simpler semantics.
+//
+// That is, don't think of these as semaphores.
+// Think of them as a way to implement sleep and wakeup
+// such that every sleep is paired with a single wakeup,
+// even if, due to races, the wakeup happens before the sleep.
+//
+// See Mullender and Cox, ``Semaphores in Plan 9,''
+// http://swtch.com/semaphore.pdf
+
+package runtime
+
+import "unsafe"
+
+// Asynchronous semaphore for sync.Mutex.
+
+type semaRoot struct {
+ lock mutex
+ head *sudog
+ tail *sudog
+ nwait uint32 // Number of waiters. Read w/o the lock.
+}
+
+// Prime to not correlate with any user patterns.
+const semTabSize = 251
+
+var semtable [semTabSize]struct {
+ root semaRoot
+ pad [cacheLineSize - unsafe.Sizeof(semaRoot{})]byte
+}
+
+// Called from sync/net packages.
+func asyncsemacquire(addr *uint32) {
+ semacquire(addr, true)
+}
+
+func asyncsemrelease(addr *uint32) {
+ semrelease(addr)
+}
+
+// Called from runtime.
+func semacquire(addr *uint32, profile bool) {
+ // Easy case.
+ if cansemacquire(addr) {
+ return
+ }
+
+ // Harder case:
+ // increment waiter count
+ // try cansemacquire one more time, return if succeeded
+ // enqueue itself as a waiter
+ // sleep
+ // (waiter descriptor is dequeued by signaler)
+ s := acquireSudog()
+ root := semroot(addr)
+ t0 := int64(0)
+ s.releasetime = 0
+ if profile && blockprofilerate > 0 {
+ t0 = cputicks()
+ s.releasetime = -1
+ }
+ for {
+ lock(&root.lock)
+ // Add ourselves to nwait to disable "easy case" in semrelease.
+ xadd(&root.nwait, 1)
+ // Check cansemacquire to avoid missed wakeup.
+ if cansemacquire(addr) {
+ xadd(&root.nwait, -1)
+ unlock(&root.lock)
+ break
+ }
+ // Any semrelease after the cansemacquire knows we're waiting
+ // (we set nwait above), so go to sleep.
+ root.queue(addr, s)
+ goparkunlock(&root.lock, "semacquire")
+ if cansemacquire(addr) {
+ break
+ }
+ }
+ if s.releasetime > 0 {
+ blockevent(int64(s.releasetime)-t0, 3)
+ }
+ releaseSudog(s)
+}
+
+func semrelease(addr *uint32) {
+ root := semroot(addr)
+ xadd(addr, 1)
+
+ // Easy case: no waiters?
+ // This check must happen after the xadd, to avoid a missed wakeup
+ // (see loop in semacquire).
+ if atomicload(&root.nwait) == 0 {
+ return
+ }
+
+ // Harder case: search for a waiter and wake it.
+ lock(&root.lock)
+ if atomicload(&root.nwait) == 0 {
+ // The count is already consumed by another goroutine,
+ // so no need to wake up another goroutine.
+ unlock(&root.lock)
+ return
+ }
+ s := root.head
+ for ; s != nil; s = s.next {
+ if s.elem == unsafe.Pointer(addr) {
+ xadd(&root.nwait, -1)
+ root.dequeue(s)
+ break
+ }
+ }
+ unlock(&root.lock)
+ if s != nil {
+ if s.releasetime != 0 {
+ s.releasetime = cputicks()
+ }
+ goready(s.g)
+ }
+}
+
+func semroot(addr *uint32) *semaRoot {
+ return &semtable[(uintptr(unsafe.Pointer(addr))>>3)%semTabSize].root
+}
+
+func cansemacquire(addr *uint32) bool {
+ for {
+ v := atomicload(addr)
+ if v == 0 {
+ return false
+ }
+ if cas(addr, v, v-1) {
+ return true
+ }
+ }
+}
+
+func (root *semaRoot) queue(addr *uint32, s *sudog) {
+ s.g = getg()
+ s.elem = unsafe.Pointer(addr)
+ s.next = nil
+ s.prev = root.tail
+ if root.tail != nil {
+ root.tail.next = s
+ } else {
+ root.head = s
+ }
+ root.tail = s
+}
+
+func (root *semaRoot) dequeue(s *sudog) {
+ if s.next != nil {
+ s.next.prev = s.prev
+ } else {
+ root.tail = s.prev
+ }
+ if s.prev != nil {
+ s.prev.next = s.next
+ } else {
+ root.head = s.next
+ }
+ s.next = nil
+ s.prev = nil
+}
+
+// Synchronous semaphore for sync.Cond.
+type syncSema struct {
+ lock mutex
+ head *sudog
+ tail *sudog
+}
+
+// Syncsemacquire waits for a pairing syncsemrelease on the same semaphore s.
+func syncsemacquire(s *syncSema) {
+ lock(&s.lock)
+ if s.head != nil && s.head.nrelease > 0 {
+ // Have pending release, consume it.
+ var wake *sudog
+ s.head.nrelease--
+ if s.head.nrelease == 0 {
+ wake = s.head
+ s.head = wake.next
+ if s.head == nil {
+ s.tail = nil
+ }
+ }
+ unlock(&s.lock)
+ if wake != nil {
+ goready(wake.g)
+ }
+ } else {
+ // Enqueue itself.
+ w := acquireSudog()
+ w.g = getg()
+ w.nrelease = -1
+ w.next = nil
+ w.releasetime = 0
+ t0 := int64(0)
+ if blockprofilerate > 0 {
+ t0 = cputicks()
+ w.releasetime = -1
+ }
+ if s.tail == nil {
+ s.head = w
+ } else {
+ s.tail.next = w
+ }
+ s.tail = w
+ goparkunlock(&s.lock, "semacquire")
+ if t0 != 0 {
+ blockevent(int64(w.releasetime)-t0, 2)
+ }
+ releaseSudog(w)
+ }
+}
+
+// Syncsemrelease waits for n pairing syncsemacquire on the same semaphore s.
+func syncsemrelease(s *syncSema, n uint32) {
+ lock(&s.lock)
+ for n > 0 && s.head != nil && s.head.nrelease < 0 {
+ // Have pending acquire, satisfy it.
+ wake := s.head
+ s.head = wake.next
+ if s.head == nil {
+ s.tail = nil
+ }
+ if wake.releasetime != 0 {
+ wake.releasetime = cputicks()
+ }
+ goready(wake.g)
+ n--
+ }
+ if n > 0 {
+ // enqueue itself
+ w := acquireSudog()
+ w.g = getg()
+ w.nrelease = int32(n)
+ w.next = nil
+ w.releasetime = 0
+ if s.tail == nil {
+ s.head = w
+ } else {
+ s.tail.next = w
+ }
+ s.tail = w
+ goparkunlock(&s.lock, "semarelease")
+ } else {
+ unlock(&s.lock)
+ }
+}
+
+func syncsemcheck(sz uintptr) {
+ if sz != unsafe.Sizeof(syncSema{}) {
+ print("runtime: bad syncSema size - sync=", sz, " runtime=", unsafe.Sizeof(syncSema{}), "\n")
+ gothrow("bad syncSema size")
+ }
+}
diff --git a/src/pkg/runtime/sema.goc b/src/pkg/runtime/sema.goc
deleted file mode 100644
index c1e8e4e18b..0000000000
--- a/src/pkg/runtime/sema.goc
+++ /dev/null
@@ -1,294 +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.
-
-// Semaphore implementation exposed to Go.
-// Intended use is provide a sleep and wakeup
-// primitive that can be used in the contended case
-// of other synchronization primitives.
-// Thus it targets the same goal as Linux's futex,
-// but it has much simpler semantics.
-//
-// That is, don't think of these as semaphores.
-// Think of them as a way to implement sleep and wakeup
-// such that every sleep is paired with a single wakeup,
-// even if, due to races, the wakeup happens before the sleep.
-//
-// See Mullender and Cox, ``Semaphores in Plan 9,''
-// http://swtch.com/semaphore.pdf
-
-package sync
-#include "runtime.h"
-#include "arch_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
-
-typedef struct SemaWaiter SemaWaiter;
-struct SemaWaiter
-{
- uint32 volatile* addr;
- G* g;
- int64 releasetime;
- int32 nrelease; // -1 for acquire
- SemaWaiter* prev;
- SemaWaiter* next;
-};
-
-typedef struct SemaRoot SemaRoot;
-struct SemaRoot
-{
- Lock;
- SemaWaiter* head;
- SemaWaiter* tail;
- // Number of waiters. Read w/o the lock.
- uint32 volatile nwait;
-};
-
-// Prime to not correlate with any user patterns.
-#define SEMTABLESZ 251
-
-struct semtable
-{
- SemaRoot;
- uint8 pad[CacheLineSize-sizeof(SemaRoot)];
-};
-#pragma dataflag NOPTR /* mark semtable as 'no pointers', hiding from garbage collector */
-static struct semtable semtable[SEMTABLESZ];
-
-static SemaRoot*
-semroot(uint32 *addr)
-{
- return &semtable[((uintptr)addr >> 3) % SEMTABLESZ];
-}
-
-static void
-semqueue(SemaRoot *root, uint32 volatile *addr, SemaWaiter *s)
-{
- s->g = g;
- s->addr = addr;
- s->next = nil;
- s->prev = root->tail;
- if(root->tail)
- root->tail->next = s;
- else
- root->head = s;
- root->tail = s;
-}
-
-static void
-semdequeue(SemaRoot *root, SemaWaiter *s)
-{
- if(s->next)
- s->next->prev = s->prev;
- else
- root->tail = s->prev;
- if(s->prev)
- s->prev->next = s->next;
- else
- root->head = s->next;
- s->prev = nil;
- s->next = nil;
-}
-
-static int32
-cansemacquire(uint32 *addr)
-{
- uint32 v;
-
- while((v = runtime·atomicload(addr)) > 0)
- if(runtime·cas(addr, v, v-1))
- return 1;
- return 0;
-}
-
-void
-runtime·semacquire(uint32 volatile *addr, bool profile)
-{
- SemaWaiter s; // Needs to be allocated on stack, otherwise garbage collector could deallocate it
- SemaRoot *root;
- int64 t0;
-
- // Easy case.
- if(cansemacquire(addr))
- return;
-
- // Harder case:
- // increment waiter count
- // try cansemacquire one more time, return if succeeded
- // enqueue itself as a waiter
- // sleep
- // (waiter descriptor is dequeued by signaler)
- root = semroot(addr);
- t0 = 0;
- s.releasetime = 0;
- if(profile && runtime·blockprofilerate > 0) {
- t0 = runtime·cputicks();
- s.releasetime = -1;
- }
- for(;;) {
- runtime·lock(root);
- // Add ourselves to nwait to disable "easy case" in semrelease.
- runtime·xadd(&root->nwait, 1);
- // Check cansemacquire to avoid missed wakeup.
- if(cansemacquire(addr)) {
- runtime·xadd(&root->nwait, -1);
- runtime·unlock(root);
- return;
- }
- // Any semrelease after the cansemacquire knows we're waiting
- // (we set nwait above), so go to sleep.
- semqueue(root, addr, &s);
- runtime·parkunlock(root, "semacquire");
- if(cansemacquire(addr)) {
- if(t0)
- runtime·blockevent(s.releasetime - t0, 3);
- return;
- }
- }
-}
-
-void
-runtime·semrelease(uint32 volatile *addr)
-{
- SemaWaiter *s;
- SemaRoot *root;
-
- root = semroot(addr);
- runtime·xadd(addr, 1);
-
- // Easy case: no waiters?
- // This check must happen after the xadd, to avoid a missed wakeup
- // (see loop in semacquire).
- if(runtime·atomicload(&root->nwait) == 0)
- return;
-
- // Harder case: search for a waiter and wake it.
- runtime·lock(root);
- if(runtime·atomicload(&root->nwait) == 0) {
- // The count is already consumed by another goroutine,
- // so no need to wake up another goroutine.
- runtime·unlock(root);
- return;
- }
- for(s = root->head; s; s = s->next) {
- if(s->addr == addr) {
- runtime·xadd(&root->nwait, -1);
- semdequeue(root, s);
- break;
- }
- }
- runtime·unlock(root);
- if(s) {
- if(s->releasetime)
- s->releasetime = runtime·cputicks();
- runtime·ready(s->g);
- }
-}
-
-// TODO(dvyukov): move to netpoll.goc once it's used by all OSes.
-void net·runtime_Semacquire(uint32 *addr)
-{
- runtime·semacquire(addr, true);
-}
-
-void net·runtime_Semrelease(uint32 *addr)
-{
- runtime·semrelease(addr);
-}
-
-func runtime_Semacquire(addr *uint32) {
- runtime·semacquire(addr, true);
-}
-
-func runtime_Semrelease(addr *uint32) {
- runtime·semrelease(addr);
-}
-
-typedef struct SyncSema SyncSema;
-struct SyncSema
-{
- Lock;
- SemaWaiter* head;
- SemaWaiter* tail;
-};
-
-func runtime_Syncsemcheck(size uintptr) {
- if(size != sizeof(SyncSema)) {
- runtime·printf("bad SyncSema size: sync:%D runtime:%D\n", (int64)size, (int64)sizeof(SyncSema));
- runtime·throw("bad SyncSema size");
- }
-}
-
-// Syncsemacquire waits for a pairing Syncsemrelease on the same semaphore s.
-func runtime_Syncsemacquire(s *SyncSema) {
- SemaWaiter w, *wake;
- int64 t0;
-
- w.g = g;
- w.nrelease = -1;
- w.next = nil;
- w.releasetime = 0;
- t0 = 0;
- if(runtime·blockprofilerate > 0) {
- t0 = runtime·cputicks();
- w.releasetime = -1;
- }
-
- runtime·lock(s);
- if(s->head && s->head->nrelease > 0) {
- // have pending release, consume it
- wake = nil;
- s->head->nrelease--;
- if(s->head->nrelease == 0) {
- wake = s->head;
- s->head = wake->next;
- if(s->head == nil)
- s->tail = nil;
- }
- runtime·unlock(s);
- if(wake)
- runtime·ready(wake->g);
- } else {
- // enqueue itself
- if(s->tail == nil)
- s->head = &w;
- else
- s->tail->next = &w;
- s->tail = &w;
- runtime·parkunlock(s, "semacquire");
- if(t0)
- runtime·blockevent(w.releasetime - t0, 2);
- }
-}
-
-// Syncsemrelease waits for n pairing Syncsemacquire on the same semaphore s.
-func runtime_Syncsemrelease(s *SyncSema, n uint32) {
- SemaWaiter w, *wake;
-
- w.g = g;
- w.nrelease = (int32)n;
- w.next = nil;
- w.releasetime = 0;
-
- runtime·lock(s);
- while(w.nrelease > 0 && s->head && s->head->nrelease < 0) {
- // have pending acquire, satisfy it
- wake = s->head;
- s->head = wake->next;
- if(s->head == nil)
- s->tail = nil;
- if(wake->releasetime)
- wake->releasetime = runtime·cputicks();
- runtime·ready(wake->g);
- w.nrelease--;
- }
- if(w.nrelease > 0) {
- // enqueue itself
- if(s->tail == nil)
- s->head = &w;
- else
- s->tail->next = &w;
- s->tail = &w;
- runtime·parkunlock(s, "semarelease");
- } else
- runtime·unlock(s);
-}
diff --git a/src/pkg/runtime/signal.c b/src/pkg/runtime/signal.c
new file mode 100644
index 0000000000..0674bfb22d
--- /dev/null
+++ b/src/pkg/runtime/signal.c
@@ -0,0 +1,25 @@
+// 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.
+
+#include "runtime.h"
+
+void
+runtime·sigenable_m(void)
+{
+ uint32 s;
+
+ s = g->m->scalararg[0];
+ g->m->scalararg[0] = 0;
+ runtime·sigenable(s);
+}
+
+void
+runtime·sigdisable_m(void)
+{
+ uint32 s;
+
+ s = g->m->scalararg[0];
+ g->m->scalararg[0] = 0;
+ runtime·sigdisable(s);
+}
diff --git a/src/pkg/runtime/signal_arm.c b/src/pkg/runtime/signal_arm.c
index 1f9a2325d1..3571cf3ac6 100644
--- a/src/pkg/runtime/signal_arm.c
+++ b/src/pkg/runtime/signal_arm.c
@@ -76,7 +76,6 @@ runtime·sighandler(int32 sig, Siginfo *info, void *ctxt, G *gp)
SIG_LR(info, ctxt) = gp->sigpc;
// In case we are panicking from external C code
SIG_R10(info, ctxt) = (uintptr)gp;
- SIG_R9(info, ctxt) = (uintptr)g->m;
SIG_PC(info, ctxt) = (uintptr)runtime·sigpanic;
return;
}
diff --git a/src/pkg/runtime/signal_nacl_amd64p32.h b/src/pkg/runtime/signal_nacl_amd64p32.h
index c58593a291..f62305cb52 100644
--- a/src/pkg/runtime/signal_nacl_amd64p32.h
+++ b/src/pkg/runtime/signal_nacl_amd64p32.h
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#define SIG_REGS(ctxt) (((ExcContext*)(ctxt))->regs64)
+#define SIG_REGS(ctxt) (((ExcContext*)(ctxt))->regs.regs64)
#define SIG_RAX(info, ctxt) (SIG_REGS(ctxt).rax)
#define SIG_RBX(info, ctxt) (SIG_REGS(ctxt).rbx)
diff --git a/src/pkg/runtime/signal_unix.c b/src/pkg/runtime/signal_unix.c
index 4d582d3f8c..0e33ece494 100644
--- a/src/pkg/runtime/signal_unix.c
+++ b/src/pkg/runtime/signal_unix.c
@@ -93,7 +93,7 @@ runtime·resetcpuprofiler(int32 hz)
}
void
-os·sigpipe(void)
+runtime·sigpipe(void)
{
runtime·setsig(SIGPIPE, SIG_DFL, false);
runtime·raise(SIGPIPE);
diff --git a/src/pkg/runtime/signal_unix.go b/src/pkg/runtime/signal_unix.go
new file mode 100644
index 0000000000..ba77b6e7be
--- /dev/null
+++ b/src/pkg/runtime/signal_unix.go
@@ -0,0 +1,13 @@
+// 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 darwin dragonfly freebsd linux netbsd openbsd solaris
+
+package runtime
+
+func sigpipe()
+
+func os_sigpipe() {
+ onM(sigpipe)
+}
diff --git a/src/pkg/runtime/sigqueue.go b/src/pkg/runtime/sigqueue.go
new file mode 100644
index 0000000000..2d9c24d2d2
--- /dev/null
+++ b/src/pkg/runtime/sigqueue.go
@@ -0,0 +1,173 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements runtime support for signal handling.
+//
+// Most synchronization primitives are not available from
+// the signal handler (it cannot block, allocate memory, or use locks)
+// so the handler communicates with a processing goroutine
+// via struct sig, below.
+//
+// sigsend is called by the signal handler to queue a new signal.
+// signal_recv is called by the Go program to receive a newly queued signal.
+// Synchronization between sigsend and signal_recv is based on the sig.state
+// variable. It can be in 3 states: sigIdle, sigReceiving and sigSending.
+// sigReceiving means that signal_recv is blocked on sig.Note and there are no
+// new pending signals.
+// sigSending means that sig.mask *may* contain new pending signals,
+// signal_recv can't be blocked in this state.
+// sigIdle means that there are no new pending signals and signal_recv is not blocked.
+// Transitions between states are done atomically with CAS.
+// When signal_recv is unblocked, it resets sig.Note and rechecks sig.mask.
+// If several sigsends and signal_recv execute concurrently, it can lead to
+// unnecessary rechecks of sig.mask, but it cannot lead to missed signals
+// nor deadlocks.
+
+package runtime
+
+import "unsafe"
+
+var sig struct {
+ note note
+ mask [(_NSIG + 31) / 32]uint32
+ wanted [(_NSIG + 31) / 32]uint32
+ recv [(_NSIG + 31) / 32]uint32
+ state uint32
+ inuse bool
+}
+
+const (
+ sigIdle = iota
+ sigReceiving
+ sigSending
+)
+
+// Called from sighandler to send a signal back out of the signal handling thread.
+// Reports whether the signal was sent. If not, the caller typically crashes the program.
+func sigsend(s int32) bool {
+ bit := uint32(1) << uint(s&31)
+ if !sig.inuse || s < 0 || int(s) >= 32*len(sig.wanted) || sig.wanted[s/32]&bit == 0 {
+ return false
+ }
+
+ // Add signal to outgoing queue.
+ for {
+ mask := sig.mask[s/32]
+ if mask&bit != 0 {
+ return true // signal already in queue
+ }
+ if cas(&sig.mask[s/32], mask, mask|bit) {
+ break
+ }
+ }
+
+ // Notify receiver that queue has new bit.
+Send:
+ for {
+ switch atomicload(&sig.state) {
+ default:
+ gothrow("sigsend: inconsistent state")
+ case sigIdle:
+ if cas(&sig.state, sigIdle, sigSending) {
+ break Send
+ }
+ case sigSending:
+ // notification already pending
+ break Send
+ case sigReceiving:
+ if cas(&sig.state, sigReceiving, sigIdle) {
+ notewakeup(&sig.note)
+ break Send
+ }
+ }
+ }
+
+ return true
+}
+
+// Called to receive the next queued signal.
+// Must only be called from a single goroutine at a time.
+func signal_recv() uint32 {
+ for {
+ // Serve any signals from local copy.
+ for i := uint32(0); i < _NSIG; i++ {
+ if sig.recv[i/32]&(1<<(i&31)) != 0 {
+ sig.recv[i/32] &^= 1 << (i & 31)
+ return i
+ }
+ }
+
+ // Wait for updates to be available from signal sender.
+ Receive:
+ for {
+ switch atomicload(&sig.state) {
+ default:
+ gothrow("signal_recv: inconsistent state")
+ case sigIdle:
+ if cas(&sig.state, sigIdle, sigReceiving) {
+ notetsleepg(&sig.note, -1)
+ noteclear(&sig.note)
+ break Receive
+ }
+ case sigSending:
+ if cas(&sig.state, sigSending, sigIdle) {
+ break Receive
+ }
+ }
+ }
+
+ // Incorporate updates from sender into local copy.
+ for i := range sig.mask {
+ sig.recv[i] = xchg(&sig.mask[i], 0)
+ }
+ }
+}
+
+// Must only be called from a single goroutine at a time.
+func signal_enable(s uint32) {
+ if !sig.inuse {
+ // The first call to signal_enable is for us
+ // to use for initialization. It does not pass
+ // signal information in m.
+ sig.inuse = true // enable reception of signals; cannot disable
+ noteclear(&sig.note)
+ return
+ }
+
+ if int(s) >= len(sig.wanted)*32 {
+ return
+ }
+ sig.wanted[s/32] |= 1 << (s & 31)
+ sigenable_go(s)
+}
+
+// Must only be called from a single goroutine at a time.
+func signal_disable(s uint32) {
+ if int(s) >= len(sig.wanted)*32 {
+ return
+ }
+ sig.wanted[s/32] &^= 1 << (s & 31)
+ sigdisable_go(s)
+}
+
+// This runs on a foreign stack, without an m or a g. No stack split.
+//go:nosplit
+func badsignal(sig uintptr) {
+ cgocallback(unsafe.Pointer(funcPC(sigsend)), noescape(unsafe.Pointer(&sig)), unsafe.Sizeof(sig))
+}
+
+func sigenable_m()
+func sigdisable_m()
+
+func sigenable_go(s uint32) {
+ g := getg()
+ g.m.scalararg[0] = uintptr(s)
+ onM(sigenable_m)
+}
+
+func sigdisable_go(s uint32) {
+ g := getg()
+ g.m.scalararg[0] = uintptr(s)
+ onM(sigdisable_m)
+}
diff --git a/src/pkg/runtime/sigqueue.goc b/src/pkg/runtime/sigqueue.goc
deleted file mode 100644
index 376e77a2e4..0000000000
--- a/src/pkg/runtime/sigqueue.goc
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file implements runtime support for signal handling.
-//
-// Most synchronization primitives are not available from
-// the signal handler (it cannot block, allocate memory, or use locks)
-// so the handler communicates with a processing goroutine
-// via struct sig, below.
-//
-// sigsend() is called by the signal handler to queue a new signal.
-// signal_recv() is called by the Go program to receive a newly queued signal.
-// Synchronization between sigsend() and signal_recv() is based on the sig.state
-// variable. It can be in 3 states: 0, HASWAITER and HASSIGNAL.
-// HASWAITER means that signal_recv() is blocked on sig.Note and there are no
-// new pending signals.
-// HASSIGNAL means that sig.mask *may* contain new pending signals,
-// signal_recv() can't be blocked in this state.
-// 0 means that there are no new pending signals and signal_recv() is not blocked.
-// Transitions between states are done atomically with CAS.
-// When signal_recv() is unblocked, it resets sig.Note and rechecks sig.mask.
-// If several sigsend()'s and signal_recv() execute concurrently, it can lead to
-// unnecessary rechecks of sig.mask, but must not lead to missed signals
-// nor deadlocks.
-
-package runtime
-#include "runtime.h"
-#include "defs_GOOS_GOARCH.h"
-#include "os_GOOS.h"
-#include "cgocall.h"
-#include "../../cmd/ld/textflag.h"
-
-#pragma textflag NOPTR
-static struct {
- Note;
- uint32 mask[(NSIG+31)/32];
- uint32 wanted[(NSIG+31)/32];
- uint32 recv[(NSIG+31)/32];
- uint32 state;
- bool inuse;
-} sig;
-
-enum {
- HASWAITER = 1,
- HASSIGNAL = 2,
-};
-
-// Called from sighandler to send a signal back out of the signal handling thread.
-bool
-runtime·sigsend(int32 s)
-{
- uint32 bit, mask, old, new;
-
- if(!sig.inuse || s < 0 || s >= 32*nelem(sig.wanted) || !(sig.wanted[s/32]&(1U<<(s&31))))
- return false;
- bit = 1 << (s&31);
- for(;;) {
- mask = sig.mask[s/32];
- if(mask & bit)
- break; // signal already in queue
- if(runtime·cas(&sig.mask[s/32], mask, mask|bit)) {
- // Added to queue.
- // Only send a wakeup if the receiver needs a kick.
- for(;;) {
- old = runtime·atomicload(&sig.state);
- if(old == HASSIGNAL)
- break;
- if(old == HASWAITER)
- new = 0;
- else // if(old == 0)
- new = HASSIGNAL;
- if(runtime·cas(&sig.state, old, new)) {
- if (old == HASWAITER)
- runtime·notewakeup(&sig);
- break;
- }
- }
- break;
- }
- }
- return true;
-}
-
-// Called to receive the next queued signal.
-// Must only be called from a single goroutine at a time.
-func signal_recv() (m uint32) {
- uint32 i, old, new;
-
- for(;;) {
- // Serve from local copy if there are bits left.
- for(i=0; i<NSIG; i++) {
- if(sig.recv[i/32]&(1U<<(i&31))) {
- sig.recv[i/32] ^= 1U<<(i&31);
- m = i;
- goto done;
- }
- }
-
- // Check and update sig.state.
- for(;;) {
- old = runtime·atomicload(&sig.state);
- if(old == HASWAITER)
- runtime·throw("inconsistent state in signal_recv");
- if(old == HASSIGNAL)
- new = 0;
- else // if(old == 0)
- new = HASWAITER;
- if(runtime·cas(&sig.state, old, new)) {
- if (new == HASWAITER) {
- runtime·notetsleepg(&sig, -1);
- runtime·noteclear(&sig);
- }
- break;
- }
- }
-
- // Get a new local copy.
- for(i=0; i<nelem(sig.mask); i++) {
- for(;;) {
- m = sig.mask[i];
- if(runtime·cas(&sig.mask[i], m, 0))
- break;
- }
- sig.recv[i] = m;
- }
- }
-
-done:;
- // goc requires that we fall off the end of functions
- // that return values instead of using our own return
- // statements.
-}
-
-// Must only be called from a single goroutine at a time.
-func signal_enable(s uint32) {
- if(!sig.inuse) {
- // The first call to signal_enable is for us
- // to use for initialization. It does not pass
- // signal information in m.
- sig.inuse = true; // enable reception of signals; cannot disable
- runtime·noteclear(&sig);
- return;
- }
-
- if(s >= nelem(sig.wanted)*32)
- return;
- sig.wanted[s/32] |= 1U<<(s&31);
- runtime·sigenable(s);
-}
-
-// Must only be called from a single goroutine at a time.
-func signal_disable(s uint32) {
- if(s >= nelem(sig.wanted)*32)
- return;
- sig.wanted[s/32] &= ~(1U<<(s&31));
- runtime·sigdisable(s);
-}
-
-// This runs on a foreign stack, without an m or a g. No stack split.
-#pragma textflag NOSPLIT
-void
-runtime·badsignal(uintptr sig)
-{
- runtime·cgocallback((void (*)(void))runtime·sigsend, &sig, sizeof(sig));
-}
diff --git a/src/pkg/runtime/slice.go b/src/pkg/runtime/slice.go
index e01ea2d7f5..3b88927c64 100644
--- a/src/pkg/runtime/slice.go
+++ b/src/pkg/runtime/slice.go
@@ -47,10 +47,8 @@ func growslice(t *slicetype, old sliceStruct, n int64) sliceStruct {
}
if raceenabled {
- callerpc := gogetcallerpc(unsafe.Pointer(&t))
- fn := growslice
- pc := **(**uintptr)(unsafe.Pointer(&fn))
- racereadrangepc(old.array, old.len*int(t.elem.size), callerpc, pc)
+ callerpc := getcallerpc(unsafe.Pointer(&t))
+ racereadrangepc(old.array, uintptr(old.len*int(t.elem.size)), callerpc, funcPC(growslice))
}
et := t.elem
@@ -104,11 +102,10 @@ func slicecopy(to sliceStruct, fm sliceStruct, width uintptr) int {
}
if raceenabled {
- callerpc := gogetcallerpc(unsafe.Pointer(&to))
- fn := slicecopy
- pc := **(**uintptr)(unsafe.Pointer(&fn))
- racewriterangepc(to.array, n*int(width), callerpc, pc)
- racereadrangepc(fm.array, n*int(width), callerpc, pc)
+ callerpc := getcallerpc(unsafe.Pointer(&to))
+ pc := funcPC(slicecopy)
+ racewriterangepc(to.array, uintptr(n*int(width)), callerpc, pc)
+ racereadrangepc(fm.array, uintptr(n*int(width)), callerpc, pc)
}
size := uintptr(n) * width
@@ -132,10 +129,9 @@ func slicestringcopy(to []byte, fm string) int {
}
if raceenabled {
- callerpc := gogetcallerpc(unsafe.Pointer(&to))
- fn := slicestringcopy
- pc := **(**uintptr)(unsafe.Pointer(&fn))
- racewriterangepc(unsafe.Pointer(&to[0]), n, callerpc, pc)
+ callerpc := getcallerpc(unsafe.Pointer(&to))
+ pc := funcPC(slicestringcopy)
+ racewriterangepc(unsafe.Pointer(&to[0]), uintptr(n), callerpc, pc)
}
memmove(unsafe.Pointer(&to[0]), unsafe.Pointer((*stringStruct)(unsafe.Pointer(&fm)).str), uintptr(n))
diff --git a/src/pkg/runtime/softfloat_arm.c b/src/pkg/runtime/softfloat_arm.c
index 6b37160114..3f3f33a19e 100644
--- a/src/pkg/runtime/softfloat_arm.c
+++ b/src/pkg/runtime/softfloat_arm.c
@@ -7,7 +7,7 @@
// It uses true little-endian doubles, while the 7500 used mixed-endian.
#include "runtime.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
#define CPSR 14
#define FLAGS_N (1U << 31)
@@ -100,6 +100,8 @@ static const uint8 conditions[10/2] = {
(FLAGS_Z >> 28), // 8: HI (C set and Z clear), 9: LS (C clear and Z set)
};
+#define FAULT (0x80000000U) // impossible PC offset
+
// returns number of words that the fp instruction
// is occupying, 0 if next instruction isn't float.
static uint32
@@ -221,6 +223,11 @@ stage1: // load/store regn is cpureg, regm is 8bit offset
case 0xed900a00: // single load
addr = (uint32*)(regs[regn] + regm);
+ if((uintptr)addr < 4096) {
+ if(trace)
+ runtime·printf("*** load @%p => fault\n", addr);
+ return FAULT;
+ }
m->freglo[regd] = addr[0];
if(trace)
@@ -230,6 +237,11 @@ stage1: // load/store regn is cpureg, regm is 8bit offset
case 0xed900b00: // double load
addr = (uint32*)(regs[regn] + regm);
+ if((uintptr)addr < 4096) {
+ if(trace)
+ runtime·printf("*** double load @%p => fault\n", addr);
+ return FAULT;
+ }
m->freglo[regd] = addr[0];
m->freghi[regd] = addr[1];
@@ -240,6 +252,11 @@ stage1: // load/store regn is cpureg, regm is 8bit offset
case 0xed800a00: // single store
addr = (uint32*)(regs[regn] + regm);
+ if((uintptr)addr < 4096) {
+ if(trace)
+ runtime·printf("*** store @%p => fault\n", addr);
+ return FAULT;
+ }
addr[0] = m->freglo[regd];
if(trace)
@@ -249,6 +266,11 @@ stage1: // load/store regn is cpureg, regm is 8bit offset
case 0xed800b00: // double store
addr = (uint32*)(regs[regn] + regm);
+ if((uintptr)addr < 4096) {
+ if(trace)
+ runtime·printf("*** double store @%p => fault\n", addr);
+ return FAULT;
+ }
addr[0] = m->freglo[regd];
addr[1] = m->freghi[regd];
@@ -606,20 +628,60 @@ struct Sfregs
uint32 cspr;
};
+static void sfloat2(void);
+void _sfloatpanic(void);
+
#pragma textflag NOSPLIT
uint32*
-runtime·_sfloat2(uint32 *lr, Sfregs regs)
+runtime·_sfloat2(uint32 *pc, Sfregs regs)
{
- uint32 skip;
+ void (*fn)(void);
+
+ g->m->ptrarg[0] = pc;
+ g->m->ptrarg[1] = &regs;
+ fn = sfloat2;
+ runtime·onM(&fn);
+ pc = g->m->ptrarg[0];
+ g->m->ptrarg[0] = nil;
+ return pc;
+}
- skip = stepflt(lr, (uint32*)&regs.r0);
- if(skip == 0) {
- runtime·printf("sfloat2 %p %x\n", lr, *lr);
+static void
+sfloat2(void)
+{
+ uint32 *pc;
+ G *curg;
+ Sfregs *regs;
+ int32 skip;
+ bool first;
+
+ pc = g->m->ptrarg[0];
+ regs = g->m->ptrarg[1];
+ g->m->ptrarg[0] = nil;
+ g->m->ptrarg[1] = nil;
+
+ first = true;
+ while(skip = stepflt(pc, (uint32*)&regs->r0)) {
+ first = false;
+ if(skip == FAULT) {
+ // Encountered bad address in store/load.
+ // Record signal information and return to assembly
+ // trampoline that fakes the call.
+ enum { SIGSEGV = 11 };
+ curg = g->m->curg;
+ curg->sig = SIGSEGV;
+ curg->sigcode0 = 0;
+ curg->sigcode1 = 0;
+ curg->sigpc = (uint32)pc;
+ pc = (uint32*)_sfloatpanic;
+ break;
+ }
+ pc += skip;
+ }
+ if(first) {
+ runtime·printf("sfloat2 %p %x\n", pc, *pc);
fabort(); // not ok to fail first instruction
}
-
- lr += skip;
- while(skip = stepflt(lr, (uint32*)&regs.r0))
- lr += skip;
- return lr;
+
+ g->m->ptrarg[0] = pc;
}
diff --git a/src/pkg/runtime/stack.c b/src/pkg/runtime/stack.c
index 88f24408ba..b85a844840 100644
--- a/src/pkg/runtime/stack.c
+++ b/src/pkg/runtime/stack.c
@@ -11,7 +11,7 @@
#include "type.h"
#include "race.h"
#include "mgc0.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
enum
{
@@ -25,6 +25,8 @@ enum
StackFaultOnFree = 0, // old stacks are mapped noaccess to detect use after free
StackCache = 1,
+
+ StackCopyAlways = 0, // expect to be able to copy stacks 100% of the time
};
// Global pool of spans that have free stacks.
@@ -32,7 +34,7 @@ enum
// order = log_2(size/FixedStack)
// There is a free list for each order.
static MSpan stackpool[NumStackOrders];
-static Lock stackpoolmu;
+static Mutex stackpoolmu;
// TODO: one lock per order?
void
@@ -206,7 +208,7 @@ runtime·stackalloc(G *gp, uint32 n)
gp->stacksize += n;
if(runtime·debug.efence || StackFromSystem) {
- v = runtime·SysAlloc(ROUND(n, PageSize), &mstats.stacks_sys);
+ v = runtime·sysAlloc(ROUND(n, PageSize), &mstats.stacks_sys);
if(v == nil)
runtime·throw("out of memory (stackalloc)");
return v;
@@ -223,9 +225,11 @@ runtime·stackalloc(G *gp, uint32 n)
n2 >>= 1;
}
c = g->m->mcache;
- if(c == nil) {
- // This can happen in the guts of exitsyscall or
+ if(c == nil || g->m->gcing || g->m->helpgc) {
+ // c == nil can happen in the guts of exitsyscall or
// procresize. Just get a stack from the global pool.
+ // Also don't touch stackcache during gc
+ // as it's flushed concurrently.
runtime·lock(&stackpoolmu);
x = poolalloc(order);
runtime·unlock(&stackpoolmu);
@@ -266,8 +270,10 @@ runtime·stackfree(G *gp, void *v, Stktop *top)
n = (uintptr)(top+1) - (uintptr)v;
if(n & (n-1))
runtime·throw("stack not a power of 2");
- if(StackDebug >= 1)
+ if(StackDebug >= 1) {
runtime·printf("stackfree %p %d\n", v, (int32)n);
+ runtime·memclr(v, n); // for testing, clobber stack data
+ }
gp->stacksize -= n;
if(runtime·debug.efence || StackFromSystem) {
if(runtime·debug.efence || StackFaultOnFree)
@@ -285,7 +291,7 @@ runtime·stackfree(G *gp, void *v, Stktop *top)
}
x = (MLink*)v;
c = g->m->mcache;
- if(c == nil) {
+ if(c == nil || g->m->gcing || g->m->helpgc) {
runtime·lock(&stackpoolmu);
poolfree(x, order);
runtime·unlock(&stackpoolmu);
@@ -320,6 +326,9 @@ runtime·oldstack(void)
int64 goid;
int32 oldstatus;
+ if(StackCopyAlways)
+ runtime·throw("unexpected call to oldstack");
+
gp = g->m->curg;
top = (Stktop*)gp->stackbase;
if(top == nil)
@@ -333,15 +342,20 @@ runtime·oldstack(void)
top->gobuf.pc, top->gobuf.sp, top->gobuf.lr, (uintptr)g->m->cret, (uintptr)argsize);
}
- // gp->status is usually Grunning, but it could be Gsyscall if a stack overflow
- // happens during a function call inside entersyscall.
- oldstatus = gp->status;
-
gp->sched = top->gobuf;
gp->sched.ret = g->m->cret;
g->m->cret = 0; // drop reference
- gp->status = Gwaiting;
- gp->waitreason = "stack unsplit";
+ // gp->status is usually Grunning, but it could be Gsyscall if a stack overflow
+ // happens during a function call inside entersyscall.
+
+ oldstatus = runtime·readgstatus(gp);
+ oldstatus &= ~Gscan;
+ if(oldstatus != Grunning && oldstatus != Gsyscall) {
+ runtime·printf("runtime: oldstack status=%d\n", oldstatus);
+ runtime·throw("oldstack");
+ }
+ runtime·casgstatus(gp, oldstatus, Gcopystack);
+ gp->waitreason = runtime·gostringnocopy((byte*)"stack unsplit");
if(argsize > 0) {
sp -= argsize;
@@ -357,10 +371,8 @@ runtime·oldstack(void)
gp->stackbase = top->stackbase;
gp->stackguard = top->stackguard;
gp->stackguard0 = gp->stackguard;
- gp->panicwrap = top->panicwrap;
runtime·stackfree(gp, old, top);
-
- gp->status = oldstatus;
+ runtime·casgstatus(gp, Gcopystack, oldstatus); // oldstatus is Grunning or Gsyscall
runtime·gogo(&gp->sched);
}
@@ -397,6 +409,7 @@ struct CopyableInfo {
};
void runtime·main(void);
+void runtime·switchtoM(void(*)(void));
static bool
checkframecopy(Stkframe *frame, void *arg)
@@ -410,7 +423,7 @@ checkframecopy(Stkframe *frame, void *arg)
if(StackDebug >= 2)
runtime·printf(" checking %s frame=[%p,%p] stk=[%p,%p]\n", runtime·funcname(f), frame->sp, frame->fp, cinfo->stk, cinfo->base);
// if we're not in the segment any more, return immediately.
- if(frame->varp < cinfo->stk || frame->varp >= cinfo->base) {
+ if((byte*)frame->varp < cinfo->stk || (byte*)frame->varp >= cinfo->base) {
if(StackDebug >= 2)
runtime·printf(" <next segment>\n");
return false; // stop traceback
@@ -422,18 +435,25 @@ checkframecopy(Stkframe *frame, void *arg)
cinfo->frames++;
return false; // stop traceback
}
- if(frame->varp != (byte*)frame->sp) { // not in prologue (and has at least one local or outarg)
+ if(f->entry == (uintptr)runtime·switchtoM) {
+ // A special routine at the bottom of stack of a goroutine that does onM call.
+ // We will allow it to be copied even though we don't
+ // have full GC info for it (because it is written in asm).
+ cinfo->frames++;
+ return true;
+ }
+ if((byte*)frame->varp != (byte*)frame->sp) { // not in prologue (and has at least one local or outarg)
stackmap = runtime·funcdata(f, FUNCDATA_LocalsPointerMaps);
if(stackmap == nil) {
cinfo->frames = -1;
- if(StackDebug >= 1)
- runtime·printf("copystack: no locals info for %s\n", runtime·funcname(f));
+ if(StackDebug >= 1 || StackCopyAlways)
+ runtime·printf("runtime: copystack: no locals info for %s\n", runtime·funcname(f));
return false;
}
if(stackmap->n <= 0) {
cinfo->frames = -1;
- if(StackDebug >= 1)
- runtime·printf("copystack: locals size info only for %s\n", runtime·funcname(f));
+ if(StackDebug >= 1 || StackCopyAlways)
+ runtime·printf("runtime: copystack: locals size info only for %s\n", runtime·funcname(f));
return false;
}
}
@@ -441,8 +461,8 @@ checkframecopy(Stkframe *frame, void *arg)
stackmap = runtime·funcdata(f, FUNCDATA_ArgsPointerMaps);
if(stackmap == nil) {
cinfo->frames = -1;
- if(StackDebug >= 1)
- runtime·printf("copystack: no arg info for %s\n", runtime·funcname(f));
+ if(StackDebug >= 1 || StackCopyAlways)
+ runtime·printf("runtime: copystack: no arg info for %s\n", runtime·funcname(f));
return false;
}
}
@@ -461,6 +481,7 @@ copyabletopsegment(G *gp)
Func *f;
FuncVal *fn;
StackMap *stackmap;
+ bool (*cb)(Stkframe*, void*);
if(gp->stackbase == 0)
runtime·throw("stackbase == 0");
@@ -470,10 +491,14 @@ copyabletopsegment(G *gp)
// Check that each frame is copyable. As a side effect,
// count the frames.
- runtime·gentraceback(~(uintptr)0, ~(uintptr)0, 0, gp, 0, nil, 0x7fffffff, checkframecopy, &cinfo, false);
+ cb = checkframecopy;
+ runtime·gentraceback(~(uintptr)0, ~(uintptr)0, 0, gp, 0, nil, 0x7fffffff, &cb, &cinfo, false);
if(StackDebug >= 1 && cinfo.frames != -1)
runtime·printf("copystack: %d copyable frames\n", cinfo.frames);
+ if(cinfo.frames == -1)
+ return -1;
+
// Check to make sure all Defers are copyable
for(d = gp->defer; d != nil; d = d->link) {
if(cinfo.stk <= (byte*)d && (byte*)d < cinfo.base) {
@@ -482,14 +507,17 @@ copyabletopsegment(G *gp)
// For now, this only happens with the Defer in runtime.main.
continue;
}
- if(d->argp < cinfo.stk || cinfo.base <= d->argp)
+ if((byte*)d->argp < cinfo.stk || cinfo.base <= (byte*)d->argp)
break; // a defer for the next segment
fn = d->fn;
if(fn == nil) // See issue 8047
continue;
f = runtime·findfunc((uintptr)fn->fn);
- if(f == nil)
+ if(f == nil) {
+ if(StackDebug >= 1 || StackCopyAlways)
+ runtime·printf("runtime: copystack: no func for deferred pc %p\n", fn->fn);
return -1;
+ }
// Check to make sure we have an args pointer map for the defer's args.
// We only need the args map, but we check
@@ -497,11 +525,17 @@ copyabletopsegment(G *gp)
// isn't provided it means the ptr map came from C and
// C (particularly, cgo) lies to us. See issue 7695.
stackmap = runtime·funcdata(f, FUNCDATA_ArgsPointerMaps);
- if(stackmap == nil || stackmap->n <= 0)
+ if(stackmap == nil || stackmap->n <= 0) {
+ if(StackDebug >= 1 || StackCopyAlways)
+ runtime·printf("runtime: copystack: no arg info for deferred %s\n", runtime·funcname(f));
return -1;
+ }
stackmap = runtime·funcdata(f, FUNCDATA_LocalsPointerMaps);
- if(stackmap == nil || stackmap->n <= 0)
+ if(stackmap == nil || stackmap->n <= 0) {
+ if(StackDebug >= 1 || StackCopyAlways)
+ runtime·printf("runtime: copystack: no local info for deferred %s\n", runtime·funcname(f));
return -1;
+ }
if(cinfo.stk <= (byte*)fn && (byte*)fn < cinfo.base) {
// FuncVal is on the stack. Again, its copyableness
@@ -568,22 +602,11 @@ adjustpointers(byte **scanp, BitVector *bv, AdjustInfo *adjinfo, Func *f)
break;
case BitsMultiWord:
switch(bv->bytedata[(i+1) / (8 / BitsPerPointer)] >> ((i+1) * BitsPerPointer & 7) & 3) {
- case BitsString:
- // string referents are never on the stack, never need to be adjusted
- i++; // skip len
- break;
- case BitsSlice:
- p = scanp[i];
- if(minp <= p && p < maxp) {
- if(StackDebug >= 3)
- runtime·printf("adjust slice %p\n", p);
- scanp[i] = p + delta;
- }
- i += 2; // skip len, cap
- break;
+ default:
+ runtime·throw("unexpected garbage collection bits");
case BitsEface:
t = (Type*)scanp[i];
- if(t != nil && (t->size > PtrSize || (t->kind & KindNoPointers) == 0)) {
+ if(t != nil && ((t->kind & KindDirectIface) == 0 || (t->kind & KindNoPointers) == 0)) {
p = scanp[i+1];
if(minp <= p && p < maxp) {
if(StackDebug >= 3)
@@ -600,7 +623,7 @@ adjustpointers(byte **scanp, BitVector *bv, AdjustInfo *adjinfo, Func *f)
if(tab != nil) {
t = tab->type;
//runtime·printf(" type=%p\n", t);
- if(t->size > PtrSize || (t->kind & KindNoPointers) == 0) {
+ if((t->kind & KindDirectIface) == 0 || (t->kind & KindNoPointers) == 0) {
p = scanp[i+1];
if(minp <= p && p < maxp) {
if(StackDebug >= 3)
@@ -634,7 +657,8 @@ adjustframe(Stkframe *frame, void *arg)
f = frame->fn;
if(StackDebug >= 2)
runtime·printf(" adjusting %s frame=[%p,%p] pc=%p continpc=%p\n", runtime·funcname(f), frame->sp, frame->fp, frame->pc, frame->continpc);
- if(f->entry == (uintptr)runtime·main)
+ if(f->entry == (uintptr)runtime·main ||
+ f->entry == (uintptr)runtime·switchtoM)
return true;
targetpc = frame->continpc;
if(targetpc == 0) {
@@ -648,7 +672,7 @@ adjustframe(Stkframe *frame, void *arg)
pcdata = 0; // in prologue
// adjust local pointers
- if(frame->varp != (byte*)frame->sp) {
+ if((byte*)frame->varp != (byte*)frame->sp) {
stackmap = runtime·funcdata(f, FUNCDATA_LocalsPointerMaps);
if(stackmap == nil)
runtime·throw("no locals info");
@@ -662,8 +686,10 @@ adjustframe(Stkframe *frame, void *arg)
// adjust inargs and outargs
if(frame->arglen != 0) {
stackmap = runtime·funcdata(f, FUNCDATA_ArgsPointerMaps);
- if(stackmap == nil)
+ if(stackmap == nil) {
+ runtime·printf("size %d\n", (int32)frame->arglen);
runtime·throw("no arg info");
+ }
bv = runtime·stackmapdata(stackmap, pcdata);
if(StackDebug >= 3)
runtime·printf(" args\n");
@@ -692,12 +718,12 @@ adjustdefers(G *gp, AdjustInfo *adjinfo)
if(adjinfo->oldstk <= (byte*)d && (byte*)d < adjinfo->oldbase) {
// The Defer record is on the stack. Its fields will
// get adjusted appropriately.
- // This only happens for runtime.main now, but a compiler
- // optimization could do more of this.
+ // This only happens for runtime.main and runtime.gopanic now,
+ // but a compiler optimization could do more of this.
*dp = (Defer*)((byte*)d + adjinfo->delta);
continue;
}
- if(d->argp < adjinfo->oldstk || adjinfo->oldbase <= d->argp)
+ if((byte*)d->argp < adjinfo->oldstk || adjinfo->oldbase <= (byte*)d->argp)
break; // a defer for the next segment
fn = d->fn;
if(fn == nil) {
@@ -730,6 +756,40 @@ adjustdefers(G *gp, AdjustInfo *adjinfo)
}
}
+static void
+adjustpanics(G *gp, AdjustInfo *adjinfo)
+{
+ Panic *p, **l;
+
+ // only the topmost panic is on the current stack
+ for(l = &gp->panic; (p = *l) != nil; ) {
+ if(adjinfo->oldstk <= (byte*)p && (byte*)p < adjinfo->oldbase)
+ *l = (Panic*)((byte*)p + adjinfo->delta);
+ l = &p->link;
+
+ if(adjinfo->oldstk <= (byte*)p->argp && (byte*)p->argp < adjinfo->oldbase)
+ p->argp += adjinfo->delta;
+ }
+}
+
+static void
+adjustsudogs(G *gp, AdjustInfo *adjinfo)
+{
+ SudoG *s;
+ byte *e;
+
+ // the data elements pointed to by a SudoG structure
+ // might be in the stack.
+ for(s = gp->waiting; s != nil; s = s->waitlink) {
+ e = s->elem;
+ if(adjinfo->oldstk <= e && e < adjinfo->oldbase)
+ s->elem = e + adjinfo->delta;
+ e = (byte*)s->selectdone;
+ if(adjinfo->oldstk <= e && e < adjinfo->oldbase)
+ s->selectdone = (uint32*)(e + adjinfo->delta);
+ }
+}
+
// Copies the top stack segment of gp to a new stack segment of a
// different size. The top segment must contain nframes frames.
static void
@@ -739,6 +799,8 @@ copystack(G *gp, uintptr nframes, uintptr newsize)
uintptr oldsize, used;
AdjustInfo adjinfo;
Stktop *oldtop, *newtop;
+ uint32 oldstatus;
+ bool (*cb)(Stkframe*, void*);
if(gp->syscallstack != 0)
runtime·throw("can't handle stack copy in syscall yet");
@@ -763,15 +825,23 @@ copystack(G *gp, uintptr nframes, uintptr newsize)
adjinfo.oldstk = oldstk;
adjinfo.oldbase = oldbase;
adjinfo.delta = newbase - oldbase;
- runtime·gentraceback(~(uintptr)0, ~(uintptr)0, 0, gp, 0, nil, nframes, adjustframe, &adjinfo, false);
+ cb = adjustframe;
+ runtime·gentraceback(~(uintptr)0, ~(uintptr)0, 0, gp, 0, nil, nframes, &cb, &adjinfo, false);
// adjust other miscellaneous things that have pointers into stacks.
adjustctxt(gp, &adjinfo);
adjustdefers(gp, &adjinfo);
+ adjustpanics(gp, &adjinfo);
+ adjustsudogs(gp, &adjinfo);
// copy the stack (including Stktop) to the new location
runtime·memmove(newbase - used, oldbase - used, used);
-
+ oldstatus = runtime·readgstatus(gp);
+ oldstatus &= ~Gscan;
+ if (oldstatus == Gwaiting || oldstatus == Grunnable)
+ runtime·casgstatus(gp, oldstatus, Gcopystack); // oldstatus is Gwaiting or Grunnable
+ else
+ runtime·throw("copystack: bad status, not Gwaiting or Grunnable");
// Swap out old stack for new one
gp->stackbase = (uintptr)newtop;
gp->stackguard = (uintptr)newstk + StackGuard;
@@ -780,6 +850,8 @@ copystack(G *gp, uintptr nframes, uintptr newsize)
gp->stack0 = (uintptr)newstk;
gp->sched.sp = (uintptr)(newbase - used);
+ runtime·casgstatus(gp, Gcopystack, oldstatus); // oldstatus is Gwaiting or Grunnable
+
// free old stack
runtime·stackfree(gp, oldstk, oldtop);
}
@@ -796,23 +868,24 @@ runtime·round2(int32 x)
return 1 << s;
}
-// Called from runtime·newstackcall or from runtime·morestack when a new
-// stack segment is needed. Allocate a new stack big enough for
-// m->moreframesize bytes, copy m->moreargsize bytes to the new frame,
-// and then act as though runtime·lessstack called the function at
-// m->morepc.
+// Called from runtime·morestack when a new stack segment is needed.
+// Allocate a new stack big enough for m->moreframesize bytes,
+// copy m->moreargsize bytes to the new frame,
+// and then act as though runtime·lessstack called the function at m->morepc.
+//
+// g->atomicstatus will be Grunning, Gsyscall or Gscanrunning, Gscansyscall upon entry.
+// If the GC is trying to stop this g then it will set preemptscan to true.
void
runtime·newstack(void)
{
int32 framesize, argsize, oldstatus, oldsize, newsize, nframes;
- Stktop *top, *oldtop;
+ Stktop *top;
byte *stk, *oldstk, *oldbase;
uintptr sp;
uintptr *src, *dst, *dstend;
G *gp;
Gobuf label, morebuf;
void *moreargp;
- bool newstackcall;
if(g->m->forkstackguard)
runtime·throw("split stack after fork");
@@ -820,14 +893,20 @@ runtime·newstack(void)
runtime·printf("runtime: newstack called from g=%p\n"
"\tm=%p m->curg=%p m->g0=%p m->gsignal=%p\n",
g->m->morebuf.g, g->m, g->m->curg, g->m->g0, g->m->gsignal);
+ morebuf = g->m->morebuf;
+ runtime·traceback(morebuf.pc, morebuf.sp, morebuf.lr, morebuf.g);
runtime·throw("runtime: wrong goroutine in newstack");
}
+ if(g->throwsplit)
+ runtime·throw("runtime: stack split at bad time");
+
+ // The goroutine must be executing in order to call newstack, so the possible states are
+ // Grunning and Gsyscall (and, due to GC, also Gscanrunning and Gscansyscall).
// gp->status is usually Grunning, but it could be Gsyscall if a stack overflow
// happens during a function call inside entersyscall.
gp = g->m->curg;
- oldstatus = gp->status;
-
+ oldstatus = runtime·readgstatus(gp) & ~Gscan;
framesize = g->m->moreframesize;
argsize = g->m->moreargsize;
moreargp = g->m->moreargp;
@@ -836,22 +915,18 @@ runtime·newstack(void)
g->m->morebuf.pc = (uintptr)nil;
g->m->morebuf.lr = (uintptr)nil;
g->m->morebuf.sp = (uintptr)nil;
- gp->status = Gwaiting;
- gp->waitreason = "stack growth";
- newstackcall = framesize==1;
- if(newstackcall)
- framesize = 0;
- // For newstackcall the context already points to beginning of runtime·newstackcall.
- if(!newstackcall)
- runtime·rewindmorestack(&gp->sched);
+ runtime·casgstatus(gp, oldstatus, Gwaiting); // oldstatus is not in a Gscan status
+ gp->waitreason = runtime·gostringnocopy((byte*)"stack growth");
+
+ runtime·rewindmorestack(&gp->sched);
if(gp->stackbase == 0)
runtime·throw("nil stackbase");
sp = gp->sched.sp;
if(thechar == '6' || thechar == '8') {
// The call to morestack cost a word.
- sp -= sizeof(uintptr);
+ sp -= sizeof(uintreg);
}
if(StackDebug >= 1 || sp < gp->stackguard - StackGuard) {
runtime·printf("runtime: newstack framesize=%p argsize=%p sp=%p stack=[%p, %p]\n"
@@ -862,6 +937,7 @@ runtime·newstack(void)
gp->sched.pc, gp->sched.sp, gp->sched.lr, gp->sched.ctxt);
}
if(sp < gp->stackguard - StackGuard) {
+ runtime·printf("runtime: gp=%p, gp->status=%d, oldstatus=%d\n ", (void*)gp, runtime·readgstatus(gp), oldstatus);
runtime·printf("runtime: split stack overflow: %p < %p\n", sp, gp->stackguard - StackGuard);
runtime·throw("runtime: split stack overflow");
}
@@ -878,18 +954,27 @@ runtime·newstack(void)
runtime·throw("runtime: g is running but p is not");
if(oldstatus == Gsyscall && g->m->locks == 0)
runtime·throw("runtime: stack growth during syscall");
+ if(oldstatus == Grunning && gp->preemptscan) {
+ runtime·gcphasework(gp);
+ runtime·casgstatus(gp, Gwaiting, Grunning);
+ gp->stackguard0 = gp->stackguard;
+ gp->preempt = false;
+ gp->preemptscan = false; // Tells the GC premption was successful.
+ runtime·gogo(&gp->sched); // never return
+ }
+
// Be conservative about where we preempt.
// We are interested in preempting user Go code, not runtime code.
if(oldstatus != Grunning || g->m->locks || g->m->mallocing || g->m->gcing || g->m->p->status != Prunning) {
// Let the goroutine keep running for now.
// gp->preempt is set, so it will be preempted next time.
gp->stackguard0 = gp->stackguard;
- gp->status = oldstatus;
+ runtime·casgstatus(gp, Gwaiting, oldstatus); // oldstatus is Gsyscall or Grunning
runtime·gogo(&gp->sched); // never return
}
// Act like goroutine called runtime.Gosched.
- gp->status = oldstatus;
- runtime·gosched0(gp); // never return
+ runtime·casgstatus(gp, Gwaiting, oldstatus); // oldstatus is Gsyscall or Grunning
+ runtime·gosched_m(gp); // never return
}
// If every frame on the top segment is copyable, allocate a bigger segment
@@ -903,6 +988,8 @@ runtime·newstack(void)
oldbase = (byte*)gp->stackbase + sizeof(Stktop);
oldsize = oldbase - oldstk;
newsize = oldsize * 2;
+ // Note that the concurrent GC might be scanning the stack as we try to replace it.
+ // copystack takes care of the appropriate coordination with the stack scanner.
copystack(gp, nframes, newsize);
if(StackDebug >= 1)
runtime·printf("stack grow done\n");
@@ -910,13 +997,16 @@ runtime·newstack(void)
runtime·printf("runtime: goroutine stack exceeds %D-byte limit\n", (uint64)runtime·maxstacksize);
runtime·throw("stack overflow");
}
- gp->status = oldstatus;
+ runtime·casgstatus(gp, Gwaiting, oldstatus); // oldstatus is Gsyscall or Grunning
runtime·gogo(&gp->sched);
}
// TODO: if stack is uncopyable because we're in C code, patch return value at
// end of C code to trigger a copy as soon as C code exits. That way, we'll
// have stack available if we get this deep again.
}
+
+ if(StackCopyAlways)
+ runtime·throw("split stack not allowed");
// allocate new segment.
framesize += argsize;
@@ -942,20 +1032,6 @@ runtime·newstack(void)
top->argp = moreargp;
top->argsize = argsize;
- // copy flag from panic
- top->panic = gp->ispanic;
- gp->ispanic = false;
-
- // if this isn't a panic, maybe we're splitting the stack for a panic.
- // if we're splitting in the top frame, propagate the panic flag
- // forward so that recover will know we're in a panic.
- oldtop = (Stktop*)top->stackbase;
- if(oldtop != nil && oldtop->panic && top->argp == (byte*)oldtop - oldtop->argsize - gp->panicwrap)
- top->panic = true;
-
- top->panicwrap = gp->panicwrap;
- gp->panicwrap = 0;
-
gp->stackbase = (uintptr)top;
gp->stackguard = (uintptr)stk + StackGuard;
gp->stackguard0 = gp->stackguard;
@@ -969,6 +1045,7 @@ runtime·newstack(void)
while(dst < dstend)
*dst++ = *src++;
}
+
if(thechar == '5' || thechar == '9') {
// caller would have saved its LR below args.
sp -= sizeof(void*);
@@ -981,13 +1058,9 @@ runtime·newstack(void)
label.sp = sp;
label.pc = (uintptr)runtime·lessstack;
label.g = g->m->curg;
- if(newstackcall)
- runtime·gostartcallfn(&label, (FuncVal*)g->m->cret);
- else {
- runtime·gostartcall(&label, (void(*)(void))gp->sched.pc, gp->sched.ctxt);
- gp->sched.ctxt = nil;
- }
- gp->status = oldstatus;
+ runtime·gostartcall(&label, (void(*)(void))gp->sched.pc, gp->sched.ctxt);
+ gp->sched.ctxt = nil;
+ runtime·casgstatus(gp, Gwaiting, oldstatus); // oldstatus is Grunning or Gsyscall
runtime·gogo(&label);
*(int32*)345 = 123; // never return
@@ -1025,7 +1098,7 @@ runtime·shrinkstack(G *gp)
if(!runtime·copystack)
return;
- if(gp->status == Gdead)
+ if(runtime·readgstatus(gp) == Gdead)
return;
if(gp->stackbase == 0)
runtime·throw("stackbase == 0");
@@ -1046,6 +1119,8 @@ runtime·shrinkstack(G *gp)
if(gp->m != nil && gp->m->libcallsp != 0)
return;
#endif
+ if(StackDebug > 0)
+ runtime·printf("shrinking stack %D->%D\n", (uint64)oldsize, (uint64)newsize);
nframes = copyabletopsegment(gp);
if(nframes == -1)
return;
diff --git a/src/pkg/runtime/stack.go b/src/pkg/runtime/stack.go
new file mode 100644
index 0000000000..f1b7d32d20
--- /dev/null
+++ b/src/pkg/runtime/stack.go
@@ -0,0 +1,13 @@
+// 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 (
+ // Goroutine preemption request.
+ // Stored into g->stackguard0 to cause split stack check failure.
+ // Must be greater than any real sp.
+ // 0xfffffade in hex.
+ stackPreempt = ^uintptr(1313)
+)
diff --git a/src/pkg/runtime/stack.h b/src/pkg/runtime/stack.h
index ee5fd351d5..7f8c43ee54 100644
--- a/src/pkg/runtime/stack.h
+++ b/src/pkg/runtime/stack.h
@@ -47,7 +47,7 @@ above checks (without allocating a full frame), which might trigger
a call to morestack. This sequence needs to fit in the bottom
section of the stack. On amd64, morestack's frame is 40 bytes, and
deferproc's frame is 56 bytes. That fits well within the
-StackGuard - StackSmall = 128 bytes at the bottom.
+StackGuard - StackSmall bytes at the bottom.
The linkers explore all possible call traces involving non-splitting
functions to make sure that this limit cannot be violated.
*/
@@ -89,7 +89,7 @@ enum {
// The stack guard is a pointer this many bytes above the
// bottom of the stack.
- StackGuard = 256 + StackSystem,
+ StackGuard = 512 + StackSystem,
// After a stack split check the SP is allowed to be this
// many bytes below the stack guard. This saves an instruction
diff --git a/src/pkg/runtime/stack_test.go b/src/pkg/runtime/stack_test.go
index 08282afd42..7b9412af42 100644
--- a/src/pkg/runtime/stack_test.go
+++ b/src/pkg/runtime/stack_test.go
@@ -6,6 +6,7 @@ package runtime_test
import (
. "runtime"
+ "strings"
"sync"
"testing"
"time"
@@ -15,7 +16,8 @@ import (
// See stack.h.
const (
StackGuard = 256
- StackLimit = 128
+ StackSmall = 64
+ StackLimit = StackGuard - StackSmall
)
// Test stack split logic by calling functions of every frame size
@@ -330,3 +332,36 @@ func TestStackCache(t *testing.T) {
}
}
}
+
+func TestStackOutput(t *testing.T) {
+ b := make([]byte, 1024)
+ stk := string(b[:Stack(b, false)])
+ if !strings.HasPrefix(stk, "goroutine ") {
+ t.Errorf("Stack (len %d):\n%s", len(stk), stk)
+ t.Errorf("Stack output should begin with \"goroutine \"")
+ }
+}
+
+func TestStackAllOutput(t *testing.T) {
+ b := make([]byte, 1024)
+ stk := string(b[:Stack(b, true)])
+ if !strings.HasPrefix(stk, "goroutine ") {
+ t.Errorf("Stack (len %d):\n%s", len(stk), stk)
+ t.Errorf("Stack output should begin with \"goroutine \"")
+ }
+}
+
+func TestStackPanic(t *testing.T) {
+ // Test that stack copying copies panics correctly. This is difficult
+ // to test because it is very unlikely that the stack will be copied
+ // in the middle of gopanic. But it can happen.
+ // To make this test effective, edit panic.go:gopanic and uncomment
+ // the GC() call just before freedefer(d).
+ defer func() {
+ if x := recover(); x == nil {
+ t.Errorf("recover failed")
+ }
+ }()
+ useStack(32)
+ panic("test panic")
+}
diff --git a/src/pkg/runtime/string.c b/src/pkg/runtime/string.c
index 60a0545a9a..4f25adf8e3 100644
--- a/src/pkg/runtime/string.c
+++ b/src/pkg/runtime/string.c
@@ -6,7 +6,7 @@
#include "arch_GOARCH.h"
#include "malloc.h"
#include "race.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
String runtime·emptystring;
@@ -90,6 +90,7 @@ runtime·gobytes(byte *p, intgo n)
return sl;
}
+#pragma textflag NOSPLIT
String
runtime·gostringnocopy(byte *str)
{
diff --git a/src/pkg/runtime/string.go b/src/pkg/runtime/string.go
index 69874e909b..31e6670a56 100644
--- a/src/pkg/runtime/string.go
+++ b/src/pkg/runtime/string.go
@@ -18,7 +18,7 @@ func concatstrings(a []string) string {
continue
}
if l+n < l {
- panic("string concatenation too long")
+ gothrow("string concatenation too long")
}
l += n
count++
@@ -57,11 +57,10 @@ func concatstring5(a [5]string) string {
func slicebytetostring(b []byte) string {
if raceenabled && len(b) > 0 {
- fn := slicebytetostring
racereadrangepc(unsafe.Pointer(&b[0]),
- len(b),
- gogetcallerpc(unsafe.Pointer(&b)),
- **(**uintptr)(unsafe.Pointer(&fn)))
+ uintptr(len(b)),
+ getcallerpc(unsafe.Pointer(&b)),
+ funcPC(slicebytetostring))
}
s, c := rawstring(len(b))
copy(c, b)
@@ -78,11 +77,10 @@ func slicebytetostringtmp(b []byte) string {
// m is a string-keyed map and k is a []byte.
if raceenabled && len(b) > 0 {
- fn := slicebytetostringtmp
racereadrangepc(unsafe.Pointer(&b[0]),
- len(b),
- gogetcallerpc(unsafe.Pointer(&b)),
- **(**uintptr)(unsafe.Pointer(&fn)))
+ uintptr(len(b)),
+ getcallerpc(unsafe.Pointer(&b)),
+ funcPC(slicebytetostringtmp))
}
return *(*string)(unsafe.Pointer(&b))
}
@@ -116,11 +114,10 @@ func stringtoslicerune(s string) []rune {
func slicerunetostring(a []rune) string {
if raceenabled && len(a) > 0 {
- fn := slicerunetostring
racereadrangepc(unsafe.Pointer(&a[0]),
- len(a)*int(unsafe.Sizeof(a[0])),
- gogetcallerpc(unsafe.Pointer(&a)),
- **(**uintptr)(unsafe.Pointer(&fn)))
+ uintptr(len(a))*unsafe.Sizeof(a[0]),
+ getcallerpc(unsafe.Pointer(&a)),
+ funcPC(slicerunetostring))
}
var dum [4]byte
size1 := 0
@@ -144,19 +141,6 @@ type stringStruct struct {
len int
}
-func cstringToGo(str uintptr) (s string) {
- i := 0
- for ; ; i++ {
- if *(*byte)(unsafe.Pointer(str + uintptr(i))) == 0 {
- break
- }
- }
- t := (*stringStruct)(unsafe.Pointer(&s))
- t.str = unsafe.Pointer(str)
- t.len = i
- return
-}
-
func intstring(v int64) string {
s, b := rawstring(4)
n := runetochar(b, rune(v))
@@ -215,7 +199,7 @@ func rawstring(size int) (s string, b []byte) {
for {
ms := maxstring
- if uintptr(size) <= uintptr(ms) || gocasx((*uintptr)(unsafe.Pointer(&maxstring)), uintptr(ms), uintptr(size)) {
+ if uintptr(size) <= uintptr(ms) || casuintptr((*uintptr)(unsafe.Pointer(&maxstring)), uintptr(ms), uintptr(size)) {
return
}
}
diff --git a/src/pkg/runtime/stubs.go b/src/pkg/runtime/stubs.go
index 30638d1af8..03f618e155 100644
--- a/src/pkg/runtime/stubs.go
+++ b/src/pkg/runtime/stubs.go
@@ -11,136 +11,230 @@ import "unsafe"
// Assembly implementations are in various files, see comments with
// each function.
-const (
- ptrSize = unsafe.Sizeof((*byte)(nil))
-)
-
-//go:noescape
-func gogetcallerpc(p unsafe.Pointer) uintptr
-
-//go:noescape
-func racereadpc(addr unsafe.Pointer, callpc, pc uintptr)
-
-//go:noescape
-func racewritepc(addr unsafe.Pointer, callpc, pc uintptr)
-
-//go:noescape
-func racereadrangepc(addr unsafe.Pointer, len int, callpc, pc uintptr)
-
-//go:noescape
-func racewriterangepc(addr unsafe.Pointer, len int, callpc, pc uintptr)
+const ptrSize = 4 << (^uintptr(0) >> 63) // unsafe.Sizeof(uintptr(0)) but an ideal const
+const regSize = 4 << (^uintreg(0) >> 63) // unsafe.Sizeof(uintreg(0)) but an ideal const
// Should be a built-in for unsafe.Pointer?
+//go:nosplit
func add(p unsafe.Pointer, x uintptr) unsafe.Pointer {
return unsafe.Pointer(uintptr(p) + x)
}
// n must be a power of 2
func roundup(p unsafe.Pointer, n uintptr) unsafe.Pointer {
- return unsafe.Pointer((uintptr(p) + n - 1) &^ (n - 1))
+ delta := -uintptr(p) & (n - 1)
+ return unsafe.Pointer(uintptr(p) + delta)
}
// in stubs.goc
+func getg() *g
func acquirem() *m
func releasem(mp *m)
+func gomcache() *mcache
+
+// mcall switches from the g to the g0 stack and invokes fn(g),
+// where g is the goroutine that made the call.
+// mcall saves g's current PC/SP in g->sched so that it can be restored later.
+// It is up to fn to arrange for that later execution, typically by recording
+// g in a data structure, causing something to call ready(g) later.
+// mcall returns to the original goroutine g later, when g has been rescheduled.
+// fn must not return at all; typically it ends by calling schedule, to let the m
+// run other goroutines.
+//
+// mcall can only be called from g stacks (not g0, not gsignal).
+//go:noescape
+func mcall(fn func(*g))
+
+// onM switches from the g to the g0 stack and invokes fn().
+// When fn returns, onM switches back to the g and returns,
+// continuing execution on the g stack.
+// If arguments must be passed to fn, they can be written to
+// g->m->ptrarg (pointers) and g->m->scalararg (non-pointers)
+// before the call and then consulted during fn.
+// Similarly, fn can pass return values back in those locations.
+// If fn is written in Go, it can be a closure, which avoids the need for
+// ptrarg and scalararg entirely.
+// After reading values out of ptrarg and scalararg it is conventional
+// to zero them to avoid (memory or information) leaks.
+//
+// If onM is called from a g0 stack, it invokes fn and returns,
+// without any stack switches.
+//
+// If onM is called from a gsignal stack, it crashes the program.
+// The implication is that functions used in signal handlers must
+// not use onM.
+//
+// NOTE(rsc): We could introduce a separate onMsignal that is
+// like onM but if called from a gsignal stack would just run fn on
+// that stack. The caller of onMsignal would be required to save the
+// old values of ptrarg/scalararg and restore them when the call
+// was finished, in case the signal interrupted an onM sequence
+// in progress on the g or g0 stacks. Until there is a clear need for this,
+// we just reject onM in signal handling contexts entirely.
+//
+//go:noescape
+func onM(fn func())
-// in asm_*.s
-func mcall(fn *byte)
-func onM(fn *byte)
-
-// C routines that run on the M stack. Call these like
-// mcall(&mcacheRefill)
-// Arguments should be passed in m->scalararg[x] and
-// m->ptrarg[x]. Return values can be passed in those
-// same slots.
-var mcacheRefill byte
-var largeAlloc byte
-var mprofMalloc byte
-var mgc2 byte
-var setFinalizer byte
-var markallocated_m byte
+func badonm() {
+ gothrow("onM called from signal goroutine")
+}
+
+// C functions that run on the M stack.
+// Call using mcall.
+func gosched_m(*g)
+func park_m(*g)
+func recovery_m(*g)
+
+// More C functions that run on the M stack.
+// Call using onM.
+func mcacheRefill_m()
+func largeAlloc_m()
+func gc_m()
+func scavenge_m()
+func setFinalizer_m()
+func removeFinalizer_m()
+func markallocated_m()
+func unrollgcprog_m()
+func unrollgcproginplace_m()
+func setgcpercent_m()
+func setmaxthreads_m()
+func ready_m()
+func deferproc_m()
+func goexit_m()
+func startpanic_m()
+func dopanic_m()
// memclr clears n bytes starting at ptr.
// in memclr_*.s
+//go:noescape
func memclr(ptr unsafe.Pointer, n uintptr)
-func racemalloc(p unsafe.Pointer, size uintptr)
-func tracealloc(p unsafe.Pointer, size uintptr, typ *_type)
-
// memmove copies n bytes from "from" to "to".
// in memmove_*.s
+//go:noescape
func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr)
-// in asm_*.s
-func fastrand2() uint32
-
const (
- gcpercentUnknown = -2
- concurrentSweep = true
+ concurrentSweep = true
)
-// in asm_*.s
-// if *p == x { *p = y; return true } else { return false }, atomically
-//go:noescape
-func gocas(p *uint32, x uint32, y uint32) bool
-
-//go:noescape
-func gocasx(p *uintptr, x uintptr, y uintptr) bool
-
-func goreadgogc() int32
-func gonanotime() int64
func gosched()
func starttheworld()
func stoptheworld()
-func clearpools()
-
-// in asm_*.s
-//go:noescape
-func gohash(a *alg, p unsafe.Pointer, size uintptr, seed uintptr) uintptr
-
-// in asm_*.s
-//go:noescape
-func goeq(alg *alg, p, q unsafe.Pointer, size uintptr) bool
+func newextram()
+func lockOSThread()
+func unlockOSThread()
// exported value for testing
var hashLoad = loadFactor
// in asm_*.s
//go:noescape
-func gomemeq(a, b unsafe.Pointer, size uintptr) bool
+func memeq(a, b unsafe.Pointer, size uintptr) bool
-// Code pointer for the nohash algorithm. Used for producing better error messages.
+// Code pointers for the nohash/noequal algorithms. Used for producing better error messages.
var nohashcode uintptr
-
-// Go version of runtime.throw.
-// in panic.c
-func gothrow(s string)
-
-func golock(x *lock)
-func gounlock(x *lock)
-func semacquire(*uint32, bool)
-func semrelease(*uint32)
-
-// Return the Go equivalent of the C Alg structure.
-// TODO: at some point Go will hold the truth for the layout
-// of runtime structures and C will be derived from it (if
-// needed at all). At that point this function can go away.
-type goalgtype struct {
- // function for hashing objects of this type
- // (ptr to object, size, seed) -> hash
- hash func(unsafe.Pointer, uintptr, uintptr) uintptr
-}
-
-func goalg(a *alg) *goalgtype {
- return (*goalgtype)(unsafe.Pointer(a))
-}
+var noequalcode uintptr
// noescape hides a pointer from escape analysis. noescape is
// the identity function but escape analysis doesn't think the
// output depends on the input. noescape is inlined and currently
// compiles down to a single xor instruction.
// USE CAREFULLY!
+//go:nosplit
func noescape(p unsafe.Pointer) unsafe.Pointer {
x := uintptr(p)
return unsafe.Pointer(x ^ 0)
}
+
+func entersyscall()
+func entersyscallblock()
+func exitsyscall()
+
+func cgocallback(fn, frame unsafe.Pointer, framesize uintptr)
+func gogo(buf *gobuf)
+func gosave(buf *gobuf)
+func read(fd int32, p unsafe.Pointer, n int32) int32
+func close(fd int32) int32
+func mincore(addr unsafe.Pointer, n uintptr, dst *byte) int32
+
+//go:noescape
+func jmpdefer(fv *funcval, argp uintptr)
+func exit1(code int32)
+func asminit()
+func setg(gg *g)
+func exit(code int32)
+func breakpoint()
+func nanotime() int64
+func usleep(usec uint32)
+func cputicks() int64
+func mmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) unsafe.Pointer
+func munmap(addr unsafe.Pointer, n uintptr)
+func madvise(addr unsafe.Pointer, n uintptr, flags int32)
+func reflectcall(fn, arg unsafe.Pointer, n uint32, retoffset uint32)
+func osyield()
+func procyield(cycles uint32)
+func cgocallback_gofunc(fv *funcval, frame unsafe.Pointer, framesize uintptr)
+func readgogc() int32
+func purgecachedstats(c *mcache)
+func gostringnocopy(b *byte) string
+func goexit()
+
+//go:noescape
+func write(fd uintptr, p unsafe.Pointer, n int32) int32
+
+//go:noescape
+func cas(ptr *uint32, old, new uint32) bool
+
+//go:noescape
+func casp(ptr *unsafe.Pointer, old, new unsafe.Pointer) bool
+
+//go:noescape
+func casuintptr(ptr *uintptr, old, new uintptr) bool
+
+//go:noescape
+func atomicstoreuintptr(ptr *uintptr, new uintptr)
+
+//go:noescape
+func atomicloaduintptr(ptr *uintptr) uintptr
+
+//go:noescape
+func atomicloaduint(ptr *uint) uint
+
+//go:noescape
+func setcallerpc(argp unsafe.Pointer, pc uintptr)
+
+//go:noescape
+func getcallerpc(argp unsafe.Pointer) uintptr
+
+//go:noescape
+func getcallersp(argp unsafe.Pointer) uintptr
+
+//go:noescape
+func asmcgocall(fn, arg unsafe.Pointer)
+
+//go:noescape
+func asmcgocall_errno(fn, arg unsafe.Pointer) int32
+
+//go:noescape
+func open(name *byte, mode, perm int32) int32
+
+//go:noescape
+func gotraceback(*bool) int32
+
+const _NoArgs = ^uintptr(0)
+
+func newstack()
+func newproc()
+func lessstack()
+func morestack()
+func mstart()
+func rt0_go()
+func sigpanic()
+
+// return0 is a stub used to return 0 from deferproc.
+// It is called at the very end of deferproc to signal
+// the calling Go function that it should not jump
+// to deferreturn.
+// in asm_*.s
+func return0()
diff --git a/src/pkg/runtime/stubs.goc b/src/pkg/runtime/stubs.goc
deleted file mode 100644
index c64e73de05..0000000000
--- a/src/pkg/runtime/stubs.goc
+++ /dev/null
@@ -1,81 +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
-#include "runtime.h"
-#include "arch_GOARCH.h"
-#include "malloc.h"
-#include "stack.h"
-#include "../../cmd/ld/textflag.h"
-
-// This file contains functions called by Go but written
-// in C. These functions are problematic for the garbage
-// collector and stack copier because we don't have
-// stack maps for them. So we must ensure that the
-// garbage collector and stack copier cannot see these
-// frames. So we impose the following invariants:
-
-// 1) Functions should be marked NOSPLIT and call
-// out to only NOSPLIT functions (recursively).
-// 2) Functions should not block.
-
-// These invariants do not hold yet but will be established once we have
-// finished converting runtime support code from C to Go.
-
-#pragma textflag NOSPLIT
-func golock(p *Lock) {
- runtime·lock(p);
-}
-#pragma textflag NOSPLIT
-func gounlock(p *Lock) {
- runtime·unlock(p);
-}
-
-#pragma textflag NOSPLIT
-func goreadgogc() (r int32) {
- r = runtime·readgogc();
-}
-
-// entry point for testing
-// TODO: mcall and run on M stack
-func gostringW(str Slice) (s String) {
- s = runtime·gostringw((uint16*)str.array);
-}
-
-#pragma textflag NOSPLIT
-func gonanotime() (r int64) {
- r = runtime·nanotime();
-}
-
-#pragma textflag NOSPLIT
-func runtime·gocas(p *uint32, x uint32, y uint32) (ret bool) {
- ret = runtime·cas(p, x, y);
-}
-
-#pragma textflag NOSPLIT
-func runtime·gocasx(p *uintptr, x uintptr, y uintptr) (ret bool) {
- ret = runtime·casp((void**)p, (void*)x, (void*)y);
-}
-
-#pragma textflag NOSPLIT
-func runtime·acquirem() (ret *M) {
- ret = g->m;
- ret->locks++;
-}
-
-#pragma textflag NOSPLIT
-func runtime·releasem(mp *M) {
- mp->locks--;
- if(mp->locks == 0 && g->preempt) {
- // restore the preemption request in case we've cleared it in newstack
- g->stackguard0 = StackPreempt;
- }
-}
-
-// For testing.
-// TODO: find a better place for this.
-func GCMask(x Eface) (mask Slice) {
- runtime·getgcmask(x.data, x.type, &mask.array, &mask.len);
- mask.cap = mask.len;
-}
diff --git a/src/pkg/runtime/symtab.go b/src/pkg/runtime/symtab.go
new file mode 100644
index 0000000000..bd9e9924c4
--- /dev/null
+++ b/src/pkg/runtime/symtab.go
@@ -0,0 +1,292 @@
+// 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"
+
+// NOTE: Func does not expose the actual unexported fields, because we return *Func
+// values to users, and we want to keep them from being able to overwrite the data
+// with (say) *f = Func{}.
+// All code operating on a *Func must call raw to get the *_func instead.
+
+// A Func represents a Go function in the running binary.
+type Func struct {
+ opaque struct{} // unexported field to disallow conversions
+}
+
+func (f *Func) raw() *_func {
+ return (*_func)(unsafe.Pointer(f))
+}
+
+// funcdata.h
+const (
+ _PCDATA_ArgSize = 0
+ _PCDATA_StackMapIndex = 1
+ _FUNCDATA_ArgsPointerMaps = 0
+ _FUNCDATA_LocalsPointerMaps = 1
+ _FUNCDATA_DeadValueMaps = 2
+ _ArgsSizeUnknown = -0x80000000
+)
+
+var (
+ pclntable []byte
+ ftab []functab
+ filetab []uint32
+
+ pclntab, epclntab struct{} // linker symbols
+)
+
+type functab struct {
+ entry uintptr
+ funcoff uintptr
+}
+
+func symtabinit() {
+ // See golang.org/s/go12symtab for header: 0xfffffffb,
+ // two zero bytes, a byte giving the PC quantum,
+ // and a byte giving the pointer width in bytes.
+ pcln := (*[8]byte)(unsafe.Pointer(&pclntab))
+ pcln32 := (*[2]uint32)(unsafe.Pointer(&pclntab))
+ if pcln32[0] != 0xfffffffb || pcln[4] != 0 || pcln[5] != 0 || pcln[6] != _PCQuantum || pcln[7] != ptrSize {
+ println("runtime: function symbol table header:", hex(pcln32[0]), hex(pcln[4]), hex(pcln[5]), hex(pcln[6]), hex(pcln[7]))
+ gothrow("invalid function symbol table\n")
+ }
+
+ // pclntable is all bytes of pclntab symbol.
+ sp := (*sliceStruct)(unsafe.Pointer(&pclntable))
+ sp.array = unsafe.Pointer(&pclntab)
+ sp.len = int(uintptr(unsafe.Pointer(&epclntab)) - uintptr(unsafe.Pointer(&pclntab)))
+ sp.cap = sp.len
+
+ // ftab is lookup table for function by program counter.
+ nftab := int(*(*uintptr)(add(unsafe.Pointer(pcln), 8)))
+ p := add(unsafe.Pointer(pcln), 8+ptrSize)
+ sp = (*sliceStruct)(unsafe.Pointer(&ftab))
+ sp.array = p
+ sp.len = nftab + 1
+ sp.cap = sp.len
+ for i := 0; i < nftab; i++ {
+ // NOTE: ftab[nftab].entry is legal; it is the address beyond the final function.
+ if ftab[i].entry > ftab[i+1].entry {
+ f1 := (*_func)(unsafe.Pointer(&pclntable[ftab[i].funcoff]))
+ f2 := (*_func)(unsafe.Pointer(&pclntable[ftab[i+1].funcoff]))
+ f2name := "end"
+ if i+1 < nftab {
+ f2name = gofuncname(f2)
+ }
+ println("function symbol table not sorted by program counter:", hex(ftab[i].entry), gofuncname(f1), ">", hex(ftab[i+1].entry), f2name)
+ for j := 0; j <= i; j++ {
+ print("\t", hex(ftab[j].entry), " ", gofuncname((*_func)(unsafe.Pointer(&pclntable[ftab[j].funcoff]))))
+ }
+ gothrow("invalid runtime symbol table")
+ }
+ }
+
+ // file table follows ftab.
+ sp = (*sliceStruct)(unsafe.Pointer(&filetab))
+ p = unsafe.Pointer(add(unsafe.Pointer(pcln), ftab[nftab].funcoff))
+ sp.array = unsafe.Pointer(add(unsafe.Pointer(pcln), ftab[nftab].funcoff))
+ // length is in first element of array.
+ // set len to 1 so we can get first element.
+ sp.len = 1
+ sp.cap = 1
+ sp.len = int(filetab[0])
+ sp.cap = sp.len
+}
+
+// FuncForPC returns a *Func describing the function that contains the
+// given program counter address, or else nil.
+func FuncForPC(pc uintptr) *Func {
+ return (*Func)(unsafe.Pointer(findfunc(pc)))
+}
+
+// Name returns the name of the function.
+func (f *Func) Name() string {
+ return gofuncname(f.raw())
+}
+
+// Entry returns the entry address of the function.
+func (f *Func) Entry() uintptr {
+ return f.raw().entry
+}
+
+// FileLine returns the file name and line number of the
+// source code corresponding to the program counter pc.
+// The result will not be accurate if pc is not a program
+// counter within f.
+func (f *Func) FileLine(pc uintptr) (file string, line int) {
+ // Pass strict=false here, because anyone can call this function,
+ // and they might just be wrong about targetpc belonging to f.
+ line = int(funcline1(f.raw(), pc, &file, false))
+ return file, line
+}
+
+func findfunc(pc uintptr) *_func {
+ if len(ftab) == 0 {
+ return nil
+ }
+
+ if pc < ftab[0].entry || pc >= ftab[len(ftab)-1].entry {
+ return nil
+ }
+
+ // binary search to find func with entry <= pc.
+ lo := 0
+ nf := len(ftab) - 1 // last entry is sentinel
+ for nf > 0 {
+ n := nf / 2
+ f := &ftab[lo+n]
+ if f.entry <= pc && pc < ftab[lo+n+1].entry {
+ return (*_func)(unsafe.Pointer(&pclntable[f.funcoff]))
+ } else if pc < f.entry {
+ nf = n
+ } else {
+ lo += n + 1
+ nf -= n + 1
+ }
+ }
+
+ gothrow("findfunc: binary search failed")
+ return nil
+}
+
+func pcvalue(f *_func, off int32, targetpc uintptr, strict bool) int32 {
+ if off == 0 {
+ return -1
+ }
+ p := pclntable[off:]
+ pc := f.entry
+ val := int32(-1)
+ for {
+ var ok bool
+ p, ok = step(p, &pc, &val, pc == f.entry)
+ if !ok {
+ break
+ }
+ if targetpc < pc {
+ return val
+ }
+ }
+
+ // If there was a table, it should have covered all program counters.
+ // If not, something is wrong.
+ if panicking != 0 || !strict {
+ return -1
+ }
+
+ print("runtime: invalid pc-encoded table f=", gofuncname(f), " pc=", hex(pc), " targetpc=", hex(targetpc), " tab=", p, "\n")
+
+ p = pclntable[off:]
+ pc = f.entry
+ val = -1
+ for {
+ var ok bool
+ p, ok = step(p, &pc, &val, pc == f.entry)
+ if !ok {
+ break
+ }
+ print("\tvalue=", val, " until pc=", hex(pc), "\n")
+ }
+
+ gothrow("invalid runtime symbol table")
+ return -1
+}
+
+func funcname(f *_func) *byte {
+ if f == nil || f.nameoff == 0 {
+ return nil
+ }
+ return (*byte)(unsafe.Pointer(&pclntable[f.nameoff]))
+}
+
+func gofuncname(f *_func) string {
+ return gostringnocopy(funcname(f))
+}
+
+func funcline1(f *_func, targetpc uintptr, file *string, strict bool) int32 {
+ *file = "?"
+ fileno := int(pcvalue(f, f.pcfile, targetpc, strict))
+ line := pcvalue(f, f.pcln, targetpc, strict)
+ if fileno == -1 || line == -1 || fileno >= len(filetab) {
+ // print("looking for ", hex(targetpc), " in ", gofuncname(f), " got file=", fileno, " line=", lineno, "\n")
+ return 0
+ }
+ *file = gostringnocopy(&pclntable[filetab[fileno]])
+ return line
+}
+
+func funcline(f *_func, targetpc uintptr, file *string) int32 {
+ return funcline1(f, targetpc, file, true)
+}
+
+func funcspdelta(f *_func, targetpc uintptr) int32 {
+ x := pcvalue(f, f.pcsp, targetpc, true)
+ if x&(ptrSize-1) != 0 {
+ print("invalid spdelta ", f.pcsp, " ", x, "\n")
+ }
+ return x
+}
+
+func pcdatavalue(f *_func, table int32, targetpc uintptr) int32 {
+ if table < 0 || table >= f.npcdata {
+ return -1
+ }
+ off := *(*int32)(add(unsafe.Pointer(&f.nfuncdata), unsafe.Sizeof(f.nfuncdata)+uintptr(table)*4))
+ return pcvalue(f, off, targetpc, true)
+}
+
+func funcarglen(f *_func, targetpc uintptr) int32 {
+ if targetpc == f.entry {
+ return 0
+ }
+ return pcdatavalue(f, _PCDATA_ArgSize, targetpc-_PCQuantum)
+}
+
+func funcdata(f *_func, i int32) unsafe.Pointer {
+ if i < 0 || i >= f.nfuncdata {
+ return nil
+ }
+ p := add(unsafe.Pointer(&f.nfuncdata), unsafe.Sizeof(f.nfuncdata)+uintptr(f.npcdata)*4)
+ if ptrSize == 8 && uintptr(p)&4 != 0 {
+ if uintptr(unsafe.Pointer(f))&4 != 0 {
+ println("runtime: misaligned func", f)
+ }
+ p = add(p, 4)
+ }
+ return *(*unsafe.Pointer)(add(p, uintptr(i)*ptrSize))
+}
+
+// step advances to the next pc, value pair in the encoded table.
+func step(p []byte, pc *uintptr, val *int32, first bool) (newp []byte, ok bool) {
+ p, uvdelta := readvarint(p)
+ if uvdelta == 0 && !first {
+ return nil, false
+ }
+ if uvdelta&1 != 0 {
+ uvdelta = ^(uvdelta >> 1)
+ } else {
+ uvdelta >>= 1
+ }
+ vdelta := int32(uvdelta)
+ p, pcdelta := readvarint(p)
+ *pc += uintptr(pcdelta * _PCQuantum)
+ *val += vdelta
+ return p, true
+}
+
+// readvarint reads a varint from p.
+func readvarint(p []byte) (newp []byte, val uint32) {
+ var v, shift uint32
+ for {
+ b := p[0]
+ p = p[1:]
+ v |= (uint32(b) & 0x7F) << shift
+ if b&0x80 == 0 {
+ break
+ }
+ shift += 7
+ }
+ return p, v
+}
diff --git a/src/pkg/runtime/symtab.goc b/src/pkg/runtime/symtab.goc
deleted file mode 100644
index 961b78d5fc..0000000000
--- a/src/pkg/runtime/symtab.goc
+++ /dev/null
@@ -1,332 +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.
-
-// Runtime symbol table parsing.
-// See http://golang.org/s/go12symtab for an overview.
-
-package runtime
-#include "runtime.h"
-#include "defs_GOOS_GOARCH.h"
-#include "os_GOOS.h"
-#include "arch_GOARCH.h"
-#include "malloc.h"
-#include "funcdata.h"
-
-typedef struct Ftab Ftab;
-struct Ftab
-{
- uintptr entry;
- uintptr funcoff;
-};
-
-extern byte pclntab[];
-
-static Ftab *ftab;
-static uintptr nftab;
-static uint32 *filetab;
-static uint32 nfiletab;
-
-static String end = { (uint8*)"end", 3 };
-
-void
-runtime·symtabinit(void)
-{
- int32 i, j;
- Func *f1, *f2;
-
- // See golang.org/s/go12symtab for header: 0xfffffffb,
- // two zero bytes, a byte giving the PC quantum,
- // and a byte giving the pointer width in bytes.
- if(*(uint32*)pclntab != 0xfffffffb || pclntab[4] != 0 || pclntab[5] != 0 || pclntab[6] != PCQuantum || pclntab[7] != sizeof(void*)) {
- runtime·printf("runtime: function symbol table header: %x %x\n", *(uint32*)pclntab, *(uint32*)(pclntab+4));
- runtime·throw("invalid function symbol table\n");
- }
-
- nftab = *(uintptr*)(pclntab+8);
- ftab = (Ftab*)(pclntab+8+sizeof(void*));
- for(i=0; i<nftab; i++) {
- // NOTE: ftab[nftab].entry is legal; it is the address beyond the final function.
- if(ftab[i].entry > ftab[i+1].entry) {
- f1 = (Func*)(pclntab + ftab[i].funcoff);
- f2 = (Func*)(pclntab + ftab[i+1].funcoff);
- runtime·printf("function symbol table not sorted by program counter: %p %s > %p %s", ftab[i].entry, runtime·funcname(f1), ftab[i+1].entry, i+1 == nftab ? "end" : runtime·funcname(f2));
- for(j=0; j<=i; j++)
- runtime·printf("\t%p %s\n", ftab[j].entry, runtime·funcname((Func*)(pclntab + ftab[j].funcoff)));
- runtime·throw("invalid runtime symbol table");
- }
- }
-
- filetab = (uint32*)(pclntab + *(uint32*)&ftab[nftab].funcoff);
- nfiletab = filetab[0];
-}
-
-static uint32
-readvarint(byte **pp)
-{
- byte *p;
- uint32 v;
- int32 shift;
-
- v = 0;
- p = *pp;
- for(shift = 0;; shift += 7) {
- v |= (*p & 0x7F) << shift;
- if(!(*p++ & 0x80))
- break;
- }
- *pp = p;
- return v;
-}
-
-void*
-runtime·funcdata(Func *f, int32 i)
-{
- byte *p;
-
- if(i < 0 || i >= f->nfuncdata)
- return nil;
- p = (byte*)&f->nfuncdata + 4 + f->npcdata*4;
- if(sizeof(void*) == 8 && ((uintptr)p & 4)) {
- if(((uintptr)f & 4))
- runtime·printf("misaligned func %p\n", f);
- p += 4;
- }
- return ((void**)p)[i];
-}
-
-static bool
-step(byte **pp, uintptr *pc, int32 *value, bool first)
-{
- uint32 uvdelta, pcdelta;
- int32 vdelta;
-
- uvdelta = readvarint(pp);
- if(uvdelta == 0 && !first)
- return 0;
- if(uvdelta&1)
- uvdelta = ~(uvdelta>>1);
- else
- uvdelta >>= 1;
- vdelta = (int32)uvdelta;
- pcdelta = readvarint(pp) * PCQuantum;
- *value += vdelta;
- *pc += pcdelta;
- return 1;
-}
-
-// Return associated data value for targetpc in func f.
-// (Source file is f->src.)
-static int32
-pcvalue(Func *f, int32 off, uintptr targetpc, bool strict)
-{
- byte *p;
- uintptr pc;
- int32 value;
-
- enum {
- debug = 0
- };
-
- // The table is a delta-encoded sequence of (value, pc) pairs.
- // Each pair states the given value is in effect up to pc.
- // The value deltas are signed, zig-zag encoded.
- // The pc deltas are unsigned.
- // The starting value is -1, the starting pc is the function entry.
- // The table ends at a value delta of 0 except in the first pair.
- if(off == 0)
- return -1;
- p = pclntab + off;
- pc = f->entry;
- value = -1;
-
- if(debug && !runtime·panicking)
- runtime·printf("pcvalue start f=%s [%p] pc=%p targetpc=%p value=%d tab=%p\n",
- runtime·funcname(f), f, pc, targetpc, value, p);
-
- while(step(&p, &pc, &value, pc == f->entry)) {
- if(debug)
- runtime·printf("\tvalue=%d until pc=%p\n", value, pc);
- if(targetpc < pc)
- return value;
- }
-
- // If there was a table, it should have covered all program counters.
- // If not, something is wrong.
- if(runtime·panicking || !strict)
- return -1;
- runtime·printf("runtime: invalid pc-encoded table f=%s pc=%p targetpc=%p tab=%p\n",
- runtime·funcname(f), pc, targetpc, p);
- p = (byte*)f + off;
- pc = f->entry;
- value = -1;
-
- while(step(&p, &pc, &value, pc == f->entry))
- runtime·printf("\tvalue=%d until pc=%p\n", value, pc);
-
- runtime·throw("invalid runtime symbol table");
- return -1;
-}
-
-static String unknown = { (uint8*)"?", 1 };
-
-int8*
-runtime·funcname(Func *f)
-{
- if(f == nil || f->nameoff == 0)
- return nil;
- return (int8*)(pclntab + f->nameoff);
-}
-
-static int32
-funcline(Func *f, uintptr targetpc, String *file, bool strict)
-{
- int32 line;
- int32 fileno;
-
- *file = unknown;
- fileno = pcvalue(f, f->pcfile, targetpc, strict);
- line = pcvalue(f, f->pcln, targetpc, strict);
- if(fileno == -1 || line == -1 || fileno >= nfiletab) {
- // runtime·printf("looking for %p in %S got file=%d line=%d\n", targetpc, *f->name, fileno, line);
- return 0;
- }
- *file = runtime·gostringnocopy(pclntab + filetab[fileno]);
- return line;
-}
-
-int32
-runtime·funcline(Func *f, uintptr targetpc, String *file)
-{
- return funcline(f, targetpc, file, true);
-}
-
-int32
-runtime·funcspdelta(Func *f, uintptr targetpc)
-{
- int32 x;
-
- x = pcvalue(f, f->pcsp, targetpc, true);
- if(x&(sizeof(void*)-1))
- runtime·printf("invalid spdelta %d %d\n", f->pcsp, x);
- return x;
-}
-
-int32
-runtime·pcdatavalue(Func *f, int32 table, uintptr targetpc)
-{
- if(table < 0 || table >= f->npcdata)
- return -1;
- return pcvalue(f, (&f->nfuncdata)[1+table], targetpc, true);
-}
-
-int32
-runtime·funcarglen(Func *f, uintptr targetpc)
-{
- if(targetpc == f->entry)
- return 0;
- return runtime·pcdatavalue(f, PCDATA_ArgSize, targetpc-PCQuantum);
-}
-
-func funcline_go(f *Func, targetpc uintptr) (retfile String, retline int) {
- // Pass strict=false here, because anyone can call this function,
- // and they might just be wrong about targetpc belonging to f.
- retline = funcline(f, targetpc, &retfile, false);
-}
-
-func funcname_go(f *Func) (ret String) {
- ret = runtime·gostringnocopy((uint8*)runtime·funcname(f));
-}
-
-func funcentry_go(f *Func) (ret uintptr) {
- ret = f->entry;
-}
-
-Func*
-runtime·findfunc(uintptr addr)
-{
- Ftab *f;
- int32 nf, n;
-
- if(nftab == 0)
- return nil;
- if(addr < ftab[0].entry || addr >= ftab[nftab].entry)
- return nil;
-
- // binary search to find func with entry <= addr.
- f = ftab;
- nf = nftab;
- while(nf > 0) {
- n = nf/2;
- if(f[n].entry <= addr && addr < f[n+1].entry)
- return (Func*)(pclntab + f[n].funcoff);
- else if(addr < f[n].entry)
- nf = n;
- else {
- f += n+1;
- nf -= n+1;
- }
- }
-
- // can't get here -- we already checked above
- // that the address was in the table bounds.
- // this can only happen if the table isn't sorted
- // by address or if the binary search above is buggy.
- runtime·prints("findfunc unreachable\n");
- return nil;
-}
-
-func FuncForPC(pc uintptr) (ret *Func) {
- ret = runtime·findfunc(pc);
-}
-
-static bool
-hasprefix(String s, int8 *p)
-{
- int32 i;
-
- for(i=0; i<s.len; i++) {
- if(p[i] == 0)
- return 1;
- if(p[i] != s.str[i])
- return 0;
- }
- return p[i] == 0;
-}
-
-static bool
-contains(String s, int8 *p)
-{
- int32 i;
-
- if(p[0] == 0)
- return 1;
- for(i=0; i<s.len; i++) {
- if(s.str[i] != p[0])
- continue;
- if(hasprefix((String){s.str + i, s.len - i}, p))
- return 1;
- }
- return 0;
-}
-
-bool
-runtime·showframe(Func *f, G *gp)
-{
- static int32 traceback = -1;
- String name;
-
- if(g->m->throwing > 0 && gp != nil && (gp == g->m->curg || gp == g->m->caughtsig))
- return 1;
- if(traceback < 0)
- traceback = runtime·gotraceback(nil);
- name = runtime·gostringnocopy((uint8*)runtime·funcname(f));
-
- // Special case: always show runtime.panic frame, so that we can
- // see where a panic started in the middle of a stack trace.
- // See golang.org/issue/5832.
- if(name.len == 7+1+5 && hasprefix(name, "runtime.panic"))
- return 1;
-
- return traceback > 1 || f != nil && contains(name, ".") && !hasprefix(name, "runtime.");
-}
diff --git a/src/pkg/runtime/sys_darwin_386.s b/src/pkg/runtime/sys_darwin_386.s
index a702d9bfa1..a961c71a83 100644
--- a/src/pkg/runtime/sys_darwin_386.s
+++ b/src/pkg/runtime/sys_darwin_386.s
@@ -7,7 +7,7 @@
// or /usr/include/sys/syscall.h (on a Mac) for system call numbers.
#include "zasm_GOOS_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// Exit the entire program (like C exit)
TEXT runtime·exit(SB),NOSPLIT,$0
@@ -28,21 +28,25 @@ TEXT runtime·exit1(SB),NOSPLIT,$0
TEXT runtime·open(SB),NOSPLIT,$0
MOVL $5, AX
INT $0x80
+ MOVL AX, ret+12(FP)
RET
TEXT runtime·close(SB),NOSPLIT,$0
MOVL $6, AX
INT $0x80
+ MOVL AX, ret+4(FP)
RET
TEXT runtime·read(SB),NOSPLIT,$0
MOVL $3, AX
INT $0x80
+ MOVL AX, ret+12(FP)
RET
TEXT runtime·write(SB),NOSPLIT,$0
MOVL $4, AX
INT $0x80
+ MOVL AX, ret+12(FP)
RET
TEXT runtime·raise(SB),NOSPLIT,$16
@@ -59,6 +63,7 @@ TEXT runtime·raise(SB),NOSPLIT,$16
TEXT runtime·mmap(SB),NOSPLIT,$0
MOVL $197, AX
INT $0x80
+ MOVL AX, ret+24(FP)
RET
TEXT runtime·madvise(SB),NOSPLIT,$0
@@ -206,9 +211,8 @@ TEXT time·now(SB),NOSPLIT,$0
// void nanotime(int64 *nsec)
TEXT runtime·nanotime(SB),NOSPLIT,$0
CALL runtime·now(SB)
- MOVL ret+0(FP), DI
- MOVL AX, 0(DI)
- MOVL DX, 4(DI)
+ MOVL AX, ret_lo+0(FP)
+ MOVL DX, ret_hi+4(FP)
RET
TEXT runtime·sigprocmask(SB),NOSPLIT,$0
@@ -315,7 +319,7 @@ TEXT runtime·usleep(SB),NOSPLIT,$32
TEXT runtime·bsdthread_create(SB),NOSPLIT,$32
MOVL $360, AX
// 0(SP) is where the caller PC would be; kernel skips it
- MOVL func+12(FP), BX
+ MOVL fn+12(FP), BX
MOVL BX, 4(SP) // func
MOVL mm+4(FP), BX
MOVL BX, 8(SP) // arg
@@ -325,10 +329,12 @@ TEXT runtime·bsdthread_create(SB),NOSPLIT,$32
MOVL BX, 16(SP) // pthread
MOVL $0x1000000, 20(SP) // flags = PTHREAD_START_CUSTOM
INT $0x80
- JAE 3(PC)
+ JAE 4(PC)
NEGL AX
+ MOVL AX, ret+16(FP)
RET
MOVL $0, AX
+ MOVL AX, ret+16(FP)
RET
// The thread that bsdthread_create creates starts executing here,
@@ -382,10 +388,12 @@ TEXT runtime·bsdthread_register(SB),NOSPLIT,$40
MOVL $0, 20(SP) // targetconc_ptr
MOVL $0, 24(SP) // dispatchqueue_offset
INT $0x80
- JAE 3(PC)
+ JAE 4(PC)
NEGL AX
+ MOVL AX, ret+0(FP)
RET
MOVL $0, AX
+ MOVL AX, ret+0(FP)
RET
// Invoke Mach system call.
@@ -408,16 +416,19 @@ TEXT runtime·sysenter(SB),NOSPLIT,$0
TEXT runtime·mach_msg_trap(SB),NOSPLIT,$0
MOVL $-31, AX
CALL runtime·sysenter(SB)
+ MOVL AX, ret+28(FP)
RET
TEXT runtime·mach_reply_port(SB),NOSPLIT,$0
MOVL $-26, AX
CALL runtime·sysenter(SB)
+ MOVL AX, ret+0(FP)
RET
TEXT runtime·mach_task_self(SB),NOSPLIT,$0
MOVL $-28, AX
CALL runtime·sysenter(SB)
+ MOVL AX, ret+0(FP)
RET
// Mach provides trap versions of the semaphore ops,
@@ -427,24 +438,28 @@ TEXT runtime·mach_task_self(SB),NOSPLIT,$0
TEXT runtime·mach_semaphore_wait(SB),NOSPLIT,$0
MOVL $-36, AX
CALL runtime·sysenter(SB)
+ MOVL AX, ret+4(FP)
RET
// uint32 mach_semaphore_timedwait(uint32, uint32, uint32)
TEXT runtime·mach_semaphore_timedwait(SB),NOSPLIT,$0
MOVL $-38, AX
CALL runtime·sysenter(SB)
+ MOVL AX, ret+12(FP)
RET
// uint32 mach_semaphore_signal(uint32)
TEXT runtime·mach_semaphore_signal(SB),NOSPLIT,$0
MOVL $-33, AX
CALL runtime·sysenter(SB)
+ MOVL AX, ret+4(FP)
RET
// uint32 mach_semaphore_signal_all(uint32)
TEXT runtime·mach_semaphore_signal_all(SB),NOSPLIT,$0
MOVL $-34, AX
CALL runtime·sysenter(SB)
+ MOVL AX, ret+4(FP)
RET
// setldt(int entry, int address, int limit)
@@ -486,10 +501,12 @@ TEXT runtime·setldt(SB),NOSPLIT,$32
TEXT runtime·sysctl(SB),NOSPLIT,$0
MOVL $202, AX
INT $0x80
- JAE 3(PC)
+ JAE 4(PC)
NEGL AX
+ MOVL AX, ret+24(FP)
RET
MOVL $0, AX
+ MOVL AX, ret+24(FP)
RET
// int32 runtime·kqueue(void);
@@ -498,6 +515,7 @@ TEXT runtime·kqueue(SB),NOSPLIT,$0
INT $0x80
JAE 2(PC)
NEGL AX
+ MOVL AX, ret+0(FP)
RET
// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout);
@@ -506,6 +524,7 @@ TEXT runtime·kevent(SB),NOSPLIT,$0
INT $0x80
JAE 2(PC)
NEGL AX
+ MOVL AX, ret+24(FP)
RET
// int32 runtime·closeonexec(int32 fd);
diff --git a/src/pkg/runtime/sys_darwin_amd64.s b/src/pkg/runtime/sys_darwin_amd64.s
index 23995db7a8..bd397d72a7 100644
--- a/src/pkg/runtime/sys_darwin_amd64.s
+++ b/src/pkg/runtime/sys_darwin_amd64.s
@@ -12,11 +12,11 @@
//
#include "zasm_GOOS_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// Exit the entire program (like C exit)
TEXT runtime·exit(SB),NOSPLIT,$0
- MOVL 8(SP), DI // arg 1 exit status
+ MOVL code+0(FP), DI // arg 1 exit status
MOVL $(0x2000000+1), AX // syscall entry
SYSCALL
MOVL $0xf1, 0xf1 // crash
@@ -25,40 +25,44 @@ TEXT runtime·exit(SB),NOSPLIT,$0
// Exit this OS thread (like pthread_exit, which eventually
// calls __bsdthread_terminate).
TEXT runtime·exit1(SB),NOSPLIT,$0
- MOVL 8(SP), DI // arg 1 exit status
+ MOVL code+0(FP), DI // arg 1 exit status
MOVL $(0x2000000+361), AX // syscall entry
SYSCALL
MOVL $0xf1, 0xf1 // crash
RET
TEXT runtime·open(SB),NOSPLIT,$0
- MOVQ 8(SP), DI // arg 1 pathname
- MOVL 16(SP), SI // arg 2 flags
- MOVL 20(SP), DX // arg 3 mode
+ MOVQ name+0(FP), DI // arg 1 pathname
+ MOVL mode+8(FP), SI // arg 2 flags
+ MOVL perm+12(FP), DX // arg 3 mode
MOVL $(0x2000000+5), AX // syscall entry
SYSCALL
+ MOVL AX, ret+16(FP)
RET
TEXT runtime·close(SB),NOSPLIT,$0
- MOVL 8(SP), DI // arg 1 fd
+ MOVL fd+0(FP), DI // arg 1 fd
MOVL $(0x2000000+6), AX // syscall entry
SYSCALL
+ MOVL AX, ret+8(FP)
RET
TEXT runtime·read(SB),NOSPLIT,$0
- MOVL 8(SP), DI // arg 1 fd
- MOVQ 16(SP), SI // arg 2 buf
- MOVL 24(SP), DX // arg 3 count
+ MOVL fd+0(FP), DI // arg 1 fd
+ MOVQ p+8(FP), SI // arg 2 buf
+ MOVL n+16(FP), DX // arg 3 count
MOVL $(0x2000000+3), AX // syscall entry
SYSCALL
+ MOVL AX, ret+24(FP)
RET
TEXT runtime·write(SB),NOSPLIT,$0
- MOVL 8(SP), DI // arg 1 fd
- MOVQ 16(SP), SI // arg 2 buf
- MOVL 24(SP), DX // arg 3 count
+ MOVQ fd+0(FP), DI // arg 1 fd
+ MOVQ p+8(FP), SI // arg 2 buf
+ MOVL n+16(FP), DX // arg 3 count
MOVL $(0x2000000+4), AX // syscall entry
SYSCALL
+ MOVL AX, ret+24(FP)
RET
TEXT runtime·raise(SB),NOSPLIT,$24
@@ -72,17 +76,17 @@ TEXT runtime·raise(SB),NOSPLIT,$24
RET
TEXT runtime·setitimer(SB), NOSPLIT, $0
- MOVL 8(SP), DI
- MOVQ 16(SP), SI
- MOVQ 24(SP), DX
+ MOVL mode+0(FP), DI
+ MOVQ new+8(FP), SI
+ MOVQ old+16(FP), DX
MOVL $(0x2000000+83), AX // syscall entry
SYSCALL
RET
TEXT runtime·madvise(SB), NOSPLIT, $0
- MOVQ 8(SP), DI // arg 1 addr
- MOVQ 16(SP), SI // arg 2 len
- MOVL 24(SP), DX // arg 3 advice
+ MOVQ addr+0(FP), DI // arg 1 addr
+ MOVQ n+8(FP), SI // arg 2 len
+ MOVL flags+16(FP), DX // arg 3 advice
MOVL $(0x2000000+75), AX // syscall entry madvise
SYSCALL
// ignore failure - maybe pages are locked
@@ -99,8 +103,7 @@ TEXT runtime·madvise(SB), NOSPLIT, $0
#define gtod_ns_base 0x70
#define gtod_sec_base 0x78
-// int64 nanotime(void)
-TEXT runtime·nanotime(SB), NOSPLIT, $32
+TEXT nanotime<>(SB), NOSPLIT, $32
MOVQ $0x7fffffe00000, BP /* comm page base */
// Loop trying to take a consistent snapshot
// of the time parameters.
@@ -149,9 +152,14 @@ systime:
ADDQ DX, AX
RET
+TEXT runtime·nanotime(SB),NOSPLIT,$0-8
+ CALL nanotime<>(SB)
+ MOVQ AX, ret+0(FP)
+ RET
+
// func now() (sec int64, nsec int32)
-TEXT time·now(SB),NOSPLIT,$0
- CALL runtime·nanotime(SB)
+TEXT time·now(SB),NOSPLIT,$0-12
+ CALL nanotime<>(SB)
// generated code for
// func f(x uint64) (uint64, uint64) { return x/1000000000, x%100000000 }
@@ -169,9 +177,9 @@ TEXT time·now(SB),NOSPLIT,$0
RET
TEXT runtime·sigprocmask(SB),NOSPLIT,$0
- MOVL 8(SP), DI
- MOVQ 16(SP), SI
- MOVQ 24(SP), DX
+ MOVL sig+0(FP), DI
+ MOVQ new+8(FP), SI
+ MOVQ old+16(FP), DX
MOVL $(0x2000000+329), AX // pthread_sigmask (on OS X, sigprocmask==entire process)
SYSCALL
JCC 2(PC)
@@ -179,11 +187,11 @@ TEXT runtime·sigprocmask(SB),NOSPLIT,$0
RET
TEXT runtime·sigaction(SB),NOSPLIT,$0
- MOVL 8(SP), DI // arg 1 sig
- MOVQ 16(SP), SI // arg 2 act
- MOVQ 24(SP), DX // arg 3 oact
- MOVQ 24(SP), CX // arg 3 oact
- MOVQ 24(SP), R10 // arg 3 oact
+ MOVL mode+0(FP), DI // arg 1 sig
+ MOVQ new+8(FP), SI // arg 2 act
+ MOVQ old+16(FP), DX // arg 3 oact
+ MOVQ old+16(FP), CX // arg 3 oact
+ MOVQ old+16(FP), R10 // arg 3 oact
MOVL $(0x2000000+46), AX // syscall entry
SYSCALL
JCC 2(PC)
@@ -234,19 +242,20 @@ sigtramp_ret:
INT $3 // not reached
TEXT runtime·mmap(SB),NOSPLIT,$0
- MOVQ 8(SP), DI // arg 1 addr
- MOVQ 16(SP), SI // arg 2 len
- MOVL 24(SP), DX // arg 3 prot
- MOVL 28(SP), R10 // arg 4 flags
- MOVL 32(SP), R8 // arg 5 fid
- MOVL 36(SP), R9 // arg 6 offset
+ MOVQ addr+0(FP), DI // arg 1 addr
+ MOVQ n+8(FP), SI // arg 2 len
+ MOVL prot+16(FP), DX // arg 3 prot
+ MOVL flags+20(FP), R10 // arg 4 flags
+ MOVL fd+24(FP), R8 // arg 5 fid
+ MOVL off+28(FP), R9 // arg 6 offset
MOVL $(0x2000000+197), AX // syscall entry
SYSCALL
+ MOVQ AX, ret+32(FP)
RET
TEXT runtime·munmap(SB),NOSPLIT,$0
- MOVQ 8(SP), DI // arg 1 addr
- MOVQ 16(SP), SI // arg 2 len
+ MOVQ addr+0(FP), DI // arg 1 addr
+ MOVQ n+8(FP), SI // arg 2 len
MOVL $(0x2000000+73), AX // syscall entry
SYSCALL
JCC 2(PC)
@@ -293,10 +302,12 @@ TEXT runtime·bsdthread_create(SB),NOSPLIT,$0
MOVQ $0, R9 // paranoia
MOVQ $(0x2000000+360), AX // bsdthread_create
SYSCALL
- JCC 3(PC)
+ JCC 4(PC)
NEGQ AX
+ MOVL AX, ret+32(FP)
RET
MOVL $0, AX
+ MOVL AX, ret+32(FP)
RET
// The thread that bsdthread_create creates starts executing here,
@@ -346,42 +357,48 @@ TEXT runtime·bsdthread_register(SB),NOSPLIT,$0
MOVQ $0, R9 // dispatchqueue_offset
MOVQ $(0x2000000+366), AX // bsdthread_register
SYSCALL
- JCC 3(PC)
+ JCC 4(PC)
NEGQ AX
+ MOVL AX, ret+0(FP)
RET
MOVL $0, AX
+ MOVL AX, ret+0(FP)
RET
// Mach system calls use 0x1000000 instead of the BSD's 0x2000000.
// uint32 mach_msg_trap(void*, uint32, uint32, uint32, uint32, uint32, uint32)
TEXT runtime·mach_msg_trap(SB),NOSPLIT,$0
- MOVQ 8(SP), DI
- MOVL 16(SP), SI
- MOVL 20(SP), DX
- MOVL 24(SP), R10
- MOVL 28(SP), R8
- MOVL 32(SP), R9
- MOVL 36(SP), R11
+ MOVQ h+0(FP), DI
+ MOVL op+8(FP), SI
+ MOVL send_size+12(FP), DX
+ MOVL rcv_size+16(FP), R10
+ MOVL rcv_name+20(FP), R8
+ MOVL timeout+24(FP), R9
+ MOVL notify+28(FP), R11
PUSHQ R11 // seventh arg, on stack
MOVL $(0x1000000+31), AX // mach_msg_trap
SYSCALL
POPQ R11
+ MOVL AX, ret+32(FP)
RET
TEXT runtime·mach_task_self(SB),NOSPLIT,$0
MOVL $(0x1000000+28), AX // task_self_trap
SYSCALL
+ MOVL AX, ret+0(FP)
RET
TEXT runtime·mach_thread_self(SB),NOSPLIT,$0
MOVL $(0x1000000+27), AX // thread_self_trap
SYSCALL
+ MOVL AX, ret+0(FP)
RET
TEXT runtime·mach_reply_port(SB),NOSPLIT,$0
MOVL $(0x1000000+26), AX // mach_reply_port
SYSCALL
+ MOVL AX, ret+0(FP)
RET
// Mach provides trap versions of the semaphore ops,
@@ -389,32 +406,36 @@ TEXT runtime·mach_reply_port(SB),NOSPLIT,$0
// uint32 mach_semaphore_wait(uint32)
TEXT runtime·mach_semaphore_wait(SB),NOSPLIT,$0
- MOVL 8(SP), DI
+ MOVL sema+0(FP), DI
MOVL $(0x1000000+36), AX // semaphore_wait_trap
SYSCALL
+ MOVL AX, ret+8(FP)
RET
// uint32 mach_semaphore_timedwait(uint32, uint32, uint32)
TEXT runtime·mach_semaphore_timedwait(SB),NOSPLIT,$0
- MOVL 8(SP), DI
- MOVL 12(SP), SI
- MOVL 16(SP), DX
+ MOVL sema+0(FP), DI
+ MOVL sec+4(FP), SI
+ MOVL nsec+8(FP), DX
MOVL $(0x1000000+38), AX // semaphore_timedwait_trap
SYSCALL
+ MOVL AX, ret+16(FP)
RET
// uint32 mach_semaphore_signal(uint32)
TEXT runtime·mach_semaphore_signal(SB),NOSPLIT,$0
- MOVL 8(SP), DI
+ MOVL sema+0(FP), DI
MOVL $(0x1000000+33), AX // semaphore_signal_trap
SYSCALL
+ MOVL AX, ret+8(FP)
RET
// uint32 mach_semaphore_signal_all(uint32)
TEXT runtime·mach_semaphore_signal_all(SB),NOSPLIT,$0
- MOVL 8(SP), DI
+ MOVL sema+0(FP), DI
MOVL $(0x1000000+34), AX // semaphore_signal_all_trap
SYSCALL
+ MOVL AX, ret+8(FP)
RET
// set tls base to DI
@@ -431,18 +452,20 @@ TEXT runtime·settls(SB),NOSPLIT,$32
RET
TEXT runtime·sysctl(SB),NOSPLIT,$0
- MOVQ 8(SP), DI
- MOVL 16(SP), SI
- MOVQ 24(SP), DX
- MOVQ 32(SP), R10
- MOVQ 40(SP), R8
- MOVQ 48(SP), R9
+ MOVQ mib+0(FP), DI
+ MOVL miblen+8(FP), SI
+ MOVQ out+16(FP), DX
+ MOVQ size+24(FP), R10
+ MOVQ dst+32(FP), R8
+ MOVQ ndst+40(FP), R9
MOVL $(0x2000000+202), AX // syscall entry
SYSCALL
- JCC 3(PC)
+ JCC 4(PC)
NEGQ AX
+ MOVL AX, ret+48(FP)
RET
MOVL $0, AX
+ MOVL AX, ret+48(FP)
RET
// int32 runtime·kqueue(void);
@@ -454,25 +477,27 @@ TEXT runtime·kqueue(SB),NOSPLIT,$0
SYSCALL
JCC 2(PC)
NEGQ AX
+ MOVL AX, ret+0(FP)
RET
// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout);
TEXT runtime·kevent(SB),NOSPLIT,$0
- MOVL 8(SP), DI
- MOVQ 16(SP), SI
- MOVL 24(SP), DX
- MOVQ 32(SP), R10
- MOVL 40(SP), R8
- MOVQ 48(SP), R9
+ MOVL fd+0(FP), DI
+ MOVQ ev1+8(FP), SI
+ MOVL nev1+16(FP), DX
+ MOVQ ev2+24(FP), R10
+ MOVL nev2+32(FP), R8
+ MOVQ ts+40(FP), R9
MOVL $(0x2000000+363), AX
SYSCALL
JCC 2(PC)
NEGQ AX
+ MOVL AX, ret+48(FP)
RET
// void runtime·closeonexec(int32 fd);
TEXT runtime·closeonexec(SB),NOSPLIT,$0
- MOVL 8(SP), DI // fd
+ MOVL fd+0(FP), DI // fd
MOVQ $2, SI // F_SETFD
MOVQ $1, DX // FD_CLOEXEC
MOVL $(0x2000000+92), AX // fcntl
diff --git a/src/pkg/runtime/sys_dragonfly_386.s b/src/pkg/runtime/sys_dragonfly_386.s
index 0b8d219112..dd0e27e26a 100644
--- a/src/pkg/runtime/sys_dragonfly_386.s
+++ b/src/pkg/runtime/sys_dragonfly_386.s
@@ -7,13 +7,14 @@
//
#include "zasm_GOOS_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT runtime·sys_umtx_sleep(SB),NOSPLIT,$-4
MOVL $469, AX // umtx_sleep
INT $0x80
JAE 2(PC)
NEGL AX
+ MOVL AX, ret+12(FP)
RET
TEXT runtime·sys_umtx_wakeup(SB),NOSPLIT,$-4
@@ -21,11 +22,13 @@ TEXT runtime·sys_umtx_wakeup(SB),NOSPLIT,$-4
INT $0x80
JAE 2(PC)
NEGL AX
+ MOVL AX, ret+8(FP)
RET
TEXT runtime·lwp_create(SB),NOSPLIT,$-4
MOVL $495, AX // lwp_create
INT $0x80
+ MOVL AX, ret+4(FP)
RET
TEXT runtime·lwp_start(SB),NOSPLIT,$0
@@ -81,26 +84,31 @@ TEXT runtime·exit1(SB),NOSPLIT,$16
TEXT runtime·open(SB),NOSPLIT,$-4
MOVL $5, AX
INT $0x80
+ MOVL AX, ret+12(FP)
RET
TEXT runtime·close(SB),NOSPLIT,$-4
MOVL $6, AX
INT $0x80
+ MOVL AX, ret+4(FP)
RET
TEXT runtime·read(SB),NOSPLIT,$-4
MOVL $3, AX
INT $0x80
+ MOVL AX, ret+12(FP)
RET
TEXT runtime·write(SB),NOSPLIT,$-4
MOVL $4, AX
INT $0x80
+ MOVL AX, ret+12(FP)
RET
TEXT runtime·getrlimit(SB),NOSPLIT,$-4
MOVL $194, AX
INT $0x80
+ MOVL AX, ret+8(FP)
RET
TEXT runtime·raise(SB),NOSPLIT,$16
@@ -116,7 +124,7 @@ TEXT runtime·raise(SB),NOSPLIT,$16
RET
TEXT runtime·mmap(SB),NOSPLIT,$36
- LEAL arg0+0(FP), SI
+ LEAL addr+0(FP), SI
LEAL 4(SP), DI
CLD
MOVSL // arg 1 - addr
@@ -131,6 +139,7 @@ TEXT runtime·mmap(SB),NOSPLIT,$36
STOSL
MOVL $197, AX // sys_mmap
INT $0x80
+ MOVL AX, ret+24(FP)
RET
TEXT runtime·munmap(SB),NOSPLIT,$-4
@@ -185,9 +194,8 @@ TEXT runtime·nanotime(SB), NOSPLIT, $32
ADDL BX, AX
ADCL $0, DX
- MOVL ret+0(FP), DI
- MOVL AX, 0(DI)
- MOVL DX, 4(DI)
+ MOVL AX, ret_lo+0(FP)
+ MOVL DX, ret_hi+4(FP)
RET
@@ -302,7 +310,7 @@ TEXT runtime·settls(SB),NOSPLIT,$24
RET
TEXT runtime·sysctl(SB),NOSPLIT,$28
- LEAL arg0+0(FP), SI
+ LEAL mib+0(FP), SI
LEAL 4(SP), DI
CLD
MOVSL // arg 1 - name
@@ -313,10 +321,12 @@ TEXT runtime·sysctl(SB),NOSPLIT,$28
MOVSL // arg 6 - newlen
MOVL $202, AX // sys___sysctl
INT $0x80
- JCC 3(PC)
+ JCC 4(PC)
NEGL AX
+ MOVL AX, ret+24(FP)
RET
MOVL $0, AX
+ MOVL AX, ret+24(FP)
RET
TEXT runtime·osyield(SB),NOSPLIT,$-4
@@ -327,9 +337,9 @@ TEXT runtime·osyield(SB),NOSPLIT,$-4
TEXT runtime·sigprocmask(SB),NOSPLIT,$16
MOVL $0, 0(SP) // syscall gap
MOVL $3, 4(SP) // arg 1 - how (SIG_SETMASK)
- MOVL args+0(FP), AX
+ MOVL new+0(FP), AX
MOVL AX, 8(SP) // arg 2 - set
- MOVL args+4(FP), AX
+ MOVL old+4(FP), AX
MOVL AX, 12(SP) // arg 3 - oset
MOVL $340, AX // sys_sigprocmask
INT $0x80
@@ -343,6 +353,7 @@ TEXT runtime·kqueue(SB),NOSPLIT,$0
INT $0x80
JAE 2(PC)
NEGL AX
+ MOVL AX, ret+0(FP)
RET
// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout);
@@ -351,6 +362,7 @@ TEXT runtime·kevent(SB),NOSPLIT,$0
INT $0x80
JAE 2(PC)
NEGL AX
+ MOVL AX, ret+24(FP)
RET
// int32 runtime·closeonexec(int32 fd);
diff --git a/src/pkg/runtime/sys_dragonfly_amd64.s b/src/pkg/runtime/sys_dragonfly_amd64.s
index 25d2be3ad8..2c756018c2 100644
--- a/src/pkg/runtime/sys_dragonfly_amd64.s
+++ b/src/pkg/runtime/sys_dragonfly_amd64.s
@@ -7,31 +7,34 @@
//
#include "zasm_GOOS_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT runtime·sys_umtx_sleep(SB),NOSPLIT,$0
- MOVQ 8(SP), DI // arg 1 - ptr
- MOVL 16(SP), SI // arg 2 - value
- MOVL 20(SP), DX // arg 3 - timeout
+ MOVQ addr+0(FP), DI // arg 1 - ptr
+ MOVL val+8(FP), SI // arg 2 - value
+ MOVL timeout+12(FP), DX // arg 3 - timeout
MOVL $469, AX // umtx_sleep
SYSCALL
JCC 2(PC)
NEGQ AX
+ MOVL AX, ret+16(FP)
RET
TEXT runtime·sys_umtx_wakeup(SB),NOSPLIT,$0
- MOVQ 8(SP), DI // arg 1 - ptr
- MOVL 16(SP), SI // arg 2 - count
+ MOVQ addr+0(FP), DI // arg 1 - ptr
+ MOVL val+8(FP), SI // arg 2 - count
MOVL $470, AX // umtx_wakeup
SYSCALL
JCC 2(PC)
NEGQ AX
+ MOVL AX, ret+16(FP)
RET
TEXT runtime·lwp_create(SB),NOSPLIT,$0
- MOVQ 8(SP), DI // arg 1 - params
+ MOVQ param+0(FP), DI // arg 1 - params
MOVL $495, AX // lwp_create
SYSCALL
+ MOVL AX, ret+8(FP)
RET
TEXT runtime·lwp_start(SB),NOSPLIT,$0
@@ -54,54 +57,59 @@ TEXT runtime·lwp_start(SB),NOSPLIT,$0
// Exit the entire program (like C exit)
TEXT runtime·exit(SB),NOSPLIT,$-8
- MOVL 8(SP), DI // arg 1 exit status
+ MOVL code+0(FP), DI // arg 1 exit status
MOVL $1, AX
SYSCALL
MOVL $0xf1, 0xf1 // crash
RET
TEXT runtime·exit1(SB),NOSPLIT,$-8
- MOVQ 8(SP), DI // arg 1 exit status
+ MOVL code+0(FP), DI // arg 1 exit status
MOVL $431, AX
SYSCALL
MOVL $0xf1, 0xf1 // crash
RET
TEXT runtime·open(SB),NOSPLIT,$-8
- MOVQ 8(SP), DI // arg 1 pathname
- MOVL 16(SP), SI // arg 2 flags
- MOVL 20(SP), DX // arg 3 mode
+ MOVQ name+0(FP), DI // arg 1 pathname
+ MOVL mode+8(FP), SI // arg 2 flags
+ MOVL perm+12(FP), DX // arg 3 mode
MOVL $5, AX
SYSCALL
+ MOVL AX, ret+16(FP)
RET
TEXT runtime·close(SB),NOSPLIT,$-8
- MOVL 8(SP), DI // arg 1 fd
+ MOVL fd+0(FP), DI // arg 1 fd
MOVL $6, AX
SYSCALL
+ MOVL AX, ret+8(FP)
RET
TEXT runtime·read(SB),NOSPLIT,$-8
- MOVL 8(SP), DI // arg 1 fd
- MOVQ 16(SP), SI // arg 2 buf
- MOVL 24(SP), DX // arg 3 count
+ MOVL fd+0(FP), DI // arg 1 fd
+ MOVQ p+8(FP), SI // arg 2 buf
+ MOVL n+16(FP), DX // arg 3 count
MOVL $3, AX
SYSCALL
+ MOVL AX, ret+24(FP)
RET
TEXT runtime·write(SB),NOSPLIT,$-8
- MOVL 8(SP), DI // arg 1 fd
- MOVQ 16(SP), SI // arg 2 buf
- MOVL 24(SP), DX // arg 3 count
+ MOVQ fd+0(FP), DI // arg 1 fd
+ MOVQ p+8(FP), SI // arg 2 buf
+ MOVL n+16(FP), DX // arg 3 count
MOVL $4, AX
SYSCALL
+ MOVL AX, ret+24(FP)
RET
TEXT runtime·getrlimit(SB),NOSPLIT,$-8
- MOVL 8(SP), DI
- MOVQ 16(SP), SI
+ MOVL kind+0(FP), DI
+ MOVQ limit+8(FP), SI
MOVL $194, AX
SYSCALL
+ MOVL AX, ret+16(FP)
RET
TEXT runtime·raise(SB),NOSPLIT,$16
@@ -115,9 +123,9 @@ TEXT runtime·raise(SB),NOSPLIT,$16
RET
TEXT runtime·setitimer(SB), NOSPLIT, $-8
- MOVL 8(SP), DI
- MOVQ 16(SP), SI
- MOVQ 24(SP), DX
+ MOVL mode+0(FP), DI
+ MOVQ new+8(FP), SI
+ MOVQ old+16(FP), DX
MOVL $83, AX
SYSCALL
RET
@@ -148,12 +156,13 @@ TEXT runtime·nanotime(SB), NOSPLIT, $32
// return nsec in AX
IMULQ $1000000000, AX
ADDQ DX, AX
+ MOVQ AX, ret+0(FP)
RET
TEXT runtime·sigaction(SB),NOSPLIT,$-8
- MOVL 8(SP), DI // arg 1 sig
- MOVQ 16(SP), SI // arg 2 act
- MOVQ 24(SP), DX // arg 3 oact
+ MOVL sig+0(FP), DI // arg 1 sig
+ MOVQ new+8(FP), SI // arg 2 act
+ MOVQ old+16(FP), DX // arg 3 oact
MOVL $342, AX
SYSCALL
JCC 2(PC)
@@ -194,23 +203,24 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$64
RET
TEXT runtime·mmap(SB),NOSPLIT,$0
- MOVQ 8(SP), DI // arg 1 - addr
- MOVQ 16(SP), SI // arg 2 - len
- MOVL 24(SP), DX // arg 3 - prot
- MOVL 28(SP), R10 // arg 4 - flags
- MOVL 32(SP), R8 // arg 5 - fd
- MOVL 36(SP), R9
+ MOVQ addr+0(FP), DI // arg 1 - addr
+ MOVQ n+8(FP), SI // arg 2 - len
+ MOVL prot+16(FP), DX // arg 3 - prot
+ MOVL flags+20(FP), R10 // arg 4 - flags
+ MOVL fd+24(FP), R8 // arg 5 - fd
+ MOVL off+28(FP), R9
SUBQ $16, SP
MOVQ R9, 8(SP) // arg 7 - offset (passed on stack)
MOVQ $0, R9 // arg 6 - pad
MOVL $197, AX
SYSCALL
ADDQ $16, SP
+ MOVQ AX, ret+32(FP)
RET
TEXT runtime·munmap(SB),NOSPLIT,$0
- MOVQ 8(SP), DI // arg 1 addr
- MOVQ 16(SP), SI // arg 2 len
+ MOVQ addr+0(FP), DI // arg 1 addr
+ MOVQ n+8(FP), SI // arg 2 len
MOVL $73, AX
SYSCALL
JCC 2(PC)
@@ -218,9 +228,9 @@ TEXT runtime·munmap(SB),NOSPLIT,$0
RET
TEXT runtime·madvise(SB),NOSPLIT,$0
- MOVQ 8(SP), DI
- MOVQ 16(SP), SI
- MOVQ 24(SP), DX
+ MOVQ addr+0(FP), DI
+ MOVQ n+8(FP), SI
+ MOVL flags+16(FP), DX
MOVQ $75, AX // madvise
SYSCALL
// ignore failure - maybe pages are locked
@@ -266,18 +276,20 @@ TEXT runtime·settls(SB),NOSPLIT,$16
RET
TEXT runtime·sysctl(SB),NOSPLIT,$0
- MOVQ 8(SP), DI // arg 1 - name
- MOVL 16(SP), SI // arg 2 - namelen
- MOVQ 24(SP), DX // arg 3 - oldp
- MOVQ 32(SP), R10 // arg 4 - oldlenp
- MOVQ 40(SP), R8 // arg 5 - newp
- MOVQ 48(SP), R9 // arg 6 - newlen
+ MOVQ mib+0(FP), DI // arg 1 - name
+ MOVL miblen+8(FP), SI // arg 2 - namelen
+ MOVQ out+16(FP), DX // arg 3 - oldp
+ MOVQ size+24(FP), R10 // arg 4 - oldlenp
+ MOVQ dst+32(FP), R8 // arg 5 - newp
+ MOVQ ndst+40(FP), R9 // arg 6 - newlen
MOVQ $202, AX // sys___sysctl
SYSCALL
- JCC 3(PC)
+ JCC 4(PC)
NEGQ AX
+ MOVL AX, ret+48(FP)
RET
MOVL $0, AX
+ MOVL AX, ret+48(FP)
RET
TEXT runtime·osyield(SB),NOSPLIT,$-4
@@ -287,8 +299,8 @@ TEXT runtime·osyield(SB),NOSPLIT,$-4
TEXT runtime·sigprocmask(SB),NOSPLIT,$0
MOVL $3, DI // arg 1 - how (SIG_SETMASK)
- MOVQ 8(SP), SI // arg 2 - set
- MOVQ 16(SP), DX // arg 3 - oset
+ MOVQ new+0(FP), SI // arg 2 - set
+ MOVQ old+8(FP), DX // arg 3 - oset
MOVL $340, AX // sys_sigprocmask
SYSCALL
JAE 2(PC)
@@ -304,25 +316,27 @@ TEXT runtime·kqueue(SB),NOSPLIT,$0
SYSCALL
JCC 2(PC)
NEGQ AX
+ MOVL AX, ret+0(FP)
RET
// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout);
TEXT runtime·kevent(SB),NOSPLIT,$0
- MOVL 8(SP), DI
- MOVQ 16(SP), SI
- MOVL 24(SP), DX
- MOVQ 32(SP), R10
- MOVL 40(SP), R8
- MOVQ 48(SP), R9
+ MOVL fd+0(FP), DI
+ MOVQ ev1+8(FP), SI
+ MOVL nev1+16(FP), DX
+ MOVQ ev2+24(FP), R10
+ MOVL nev2+32(FP), R8
+ MOVQ ts+40(FP), R9
MOVL $363, AX
SYSCALL
JCC 2(PC)
NEGQ AX
+ MOVL AX, ret+48(FP)
RET
// void runtime·closeonexec(int32 fd);
TEXT runtime·closeonexec(SB),NOSPLIT,$0
- MOVL 8(SP), DI // fd
+ MOVL fd+0(FP), DI // fd
MOVQ $2, SI // F_SETFD
MOVQ $1, DX // FD_CLOEXEC
MOVL $92, AX // fcntl
diff --git a/src/pkg/runtime/sys_freebsd_386.s b/src/pkg/runtime/sys_freebsd_386.s
index d2ce25f994..ffc28560ec 100644
--- a/src/pkg/runtime/sys_freebsd_386.s
+++ b/src/pkg/runtime/sys_freebsd_386.s
@@ -7,11 +7,12 @@
//
#include "zasm_GOOS_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT runtime·sys_umtx_op(SB),NOSPLIT,$-4
MOVL $454, AX
INT $0x80
+ MOVL AX, ret+20(FP)
RET
TEXT runtime·thr_new(SB),NOSPLIT,$-4
@@ -60,26 +61,31 @@ TEXT runtime·exit1(SB),NOSPLIT,$-4
TEXT runtime·open(SB),NOSPLIT,$-4
MOVL $5, AX
INT $0x80
+ MOVL AX, ret+12(FP)
RET
TEXT runtime·close(SB),NOSPLIT,$-4
MOVL $6, AX
INT $0x80
+ MOVL AX, ret+4(FP)
RET
TEXT runtime·read(SB),NOSPLIT,$-4
MOVL $3, AX
INT $0x80
+ MOVL AX, ret+12(FP)
RET
TEXT runtime·write(SB),NOSPLIT,$-4
MOVL $4, AX
INT $0x80
+ MOVL AX, ret+12(FP)
RET
TEXT runtime·getrlimit(SB),NOSPLIT,$-4
MOVL $194, AX
INT $0x80
+ MOVL AX, ret+8(FP)
RET
TEXT runtime·raise(SB),NOSPLIT,$16
@@ -98,7 +104,7 @@ TEXT runtime·raise(SB),NOSPLIT,$16
RET
TEXT runtime·mmap(SB),NOSPLIT,$32
- LEAL arg0+0(FP), SI
+ LEAL addr+0(FP), SI
LEAL 4(SP), DI
CLD
MOVSL
@@ -111,6 +117,7 @@ TEXT runtime·mmap(SB),NOSPLIT,$32
STOSL
MOVL $477, AX
INT $0x80
+ MOVL AX, ret+24(FP)
RET
TEXT runtime·munmap(SB),NOSPLIT,$-4
@@ -167,9 +174,8 @@ TEXT runtime·nanotime(SB), NOSPLIT, $32
ADDL BX, AX
ADCL $0, DX
- MOVL ret+0(FP), DI
- MOVL AX, 0(DI)
- MOVL DX, 4(DI)
+ MOVL AX, ret_lo+0(FP)
+ MOVL DX, ret_hi+4(FP)
RET
@@ -314,7 +320,7 @@ TEXT runtime·i386_set_ldt(SB),NOSPLIT,$16
RET
TEXT runtime·sysctl(SB),NOSPLIT,$28
- LEAL arg0+0(FP), SI
+ LEAL mib+0(FP), SI
LEAL 4(SP), DI
CLD
MOVSL // arg 1 - name
@@ -325,10 +331,12 @@ TEXT runtime·sysctl(SB),NOSPLIT,$28
MOVSL // arg 6 - newlen
MOVL $202, AX // sys___sysctl
INT $0x80
- JAE 3(PC)
+ JAE 4(PC)
NEGL AX
+ MOVL AX, ret+24(FP)
RET
MOVL $0, AX
+ MOVL AX, ret+24(FP)
RET
TEXT runtime·osyield(SB),NOSPLIT,$-4
@@ -339,9 +347,9 @@ TEXT runtime·osyield(SB),NOSPLIT,$-4
TEXT runtime·sigprocmask(SB),NOSPLIT,$16
MOVL $0, 0(SP) // syscall gap
MOVL $3, 4(SP) // arg 1 - how (SIG_SETMASK)
- MOVL args+0(FP), AX
+ MOVL new+0(FP), AX
MOVL AX, 8(SP) // arg 2 - set
- MOVL args+4(FP), AX
+ MOVL old+4(FP), AX
MOVL AX, 12(SP) // arg 3 - oset
MOVL $340, AX // sys_sigprocmask
INT $0x80
@@ -355,6 +363,7 @@ TEXT runtime·kqueue(SB),NOSPLIT,$0
INT $0x80
JAE 2(PC)
NEGL AX
+ MOVL AX, ret+0(FP)
RET
// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout);
@@ -363,6 +372,7 @@ TEXT runtime·kevent(SB),NOSPLIT,$0
INT $0x80
JAE 2(PC)
NEGL AX
+ MOVL AX, ret+24(FP)
RET
// int32 runtime·closeonexec(int32 fd);
diff --git a/src/pkg/runtime/sys_freebsd_amd64.s b/src/pkg/runtime/sys_freebsd_amd64.s
index 2c6e33590f..65f8c1a6ee 100644
--- a/src/pkg/runtime/sys_freebsd_amd64.s
+++ b/src/pkg/runtime/sys_freebsd_amd64.s
@@ -7,7 +7,7 @@
//
#include "zasm_GOOS_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// FreeBSD 8, FreeBSD 9, and older versions that I have checked
// do not restore R10 on exit from a "restarted" system call
@@ -35,18 +35,19 @@
#define SYSCALL MOVQ R10, CX; INT $0x80
TEXT runtime·sys_umtx_op(SB),NOSPLIT,$0
- MOVQ 8(SP), DI
- MOVL 16(SP), SI
- MOVL 20(SP), DX
- MOVQ 24(SP), R10
- MOVQ 32(SP), R8
+ MOVQ addr+0(FP), DI
+ MOVL mode+8(FP), SI
+ MOVL val+12(FP), DX
+ MOVQ ptr2+16(FP), R10
+ MOVQ ts+24(FP), R8
MOVL $454, AX
SYSCALL
+ MOVL AX, ret+32(FP)
RET
TEXT runtime·thr_new(SB),NOSPLIT,$0
- MOVQ 8(SP), DI
- MOVQ 16(SP), SI
+ MOVQ param+0(FP), DI
+ MOVL size+8(FP), SI
MOVL $455, AX
SYSCALL
RET
@@ -71,54 +72,59 @@ TEXT runtime·thr_start(SB),NOSPLIT,$0
// Exit the entire program (like C exit)
TEXT runtime·exit(SB),NOSPLIT,$-8
- MOVL 8(SP), DI // arg 1 exit status
+ MOVL code+0(FP), DI // arg 1 exit status
MOVL $1, AX
SYSCALL
MOVL $0xf1, 0xf1 // crash
RET
TEXT runtime·exit1(SB),NOSPLIT,$-8
- MOVQ 8(SP), DI // arg 1 exit status
+ MOVL code+0(FP), DI // arg 1 exit status
MOVL $431, AX
SYSCALL
MOVL $0xf1, 0xf1 // crash
RET
TEXT runtime·open(SB),NOSPLIT,$-8
- MOVQ 8(SP), DI // arg 1 pathname
- MOVL 16(SP), SI // arg 2 flags
- MOVL 20(SP), DX // arg 3 mode
+ MOVQ name+0(FP), DI // arg 1 pathname
+ MOVL mode+8(FP), SI // arg 2 flags
+ MOVL perm+12(FP), DX // arg 3 mode
MOVL $5, AX
SYSCALL
+ MOVL AX, ret+16(FP)
RET
TEXT runtime·close(SB),NOSPLIT,$-8
- MOVL 8(SP), DI // arg 1 fd
+ MOVL fd+0(FP), DI // arg 1 fd
MOVL $6, AX
SYSCALL
+ MOVL AX, ret+8(FP)
RET
TEXT runtime·read(SB),NOSPLIT,$-8
- MOVL 8(SP), DI // arg 1 fd
- MOVQ 16(SP), SI // arg 2 buf
- MOVL 24(SP), DX // arg 3 count
+ MOVL fd+0(FP), DI // arg 1 fd
+ MOVQ p+8(FP), SI // arg 2 buf
+ MOVL n+16(FP), DX // arg 3 count
MOVL $3, AX
SYSCALL
+ MOVL AX, ret+24(FP)
RET
TEXT runtime·write(SB),NOSPLIT,$-8
- MOVL 8(SP), DI // arg 1 fd
- MOVQ 16(SP), SI // arg 2 buf
- MOVL 24(SP), DX // arg 3 count
+ MOVQ fd+0(FP), DI // arg 1 fd
+ MOVQ p+8(FP), SI // arg 2 buf
+ MOVL n+16(FP), DX // arg 3 count
MOVL $4, AX
SYSCALL
+ MOVL AX, ret+24(FP)
RET
TEXT runtime·getrlimit(SB),NOSPLIT,$-8
- MOVL 8(SP), DI
- MOVQ 16(SP), SI
+ MOVL kind+0(FP), DI
+ MOVQ limit+8(FP), SI
MOVL $194, AX
SYSCALL
+ MOVL AX, ret+16(FP)
RET
TEXT runtime·raise(SB),NOSPLIT,$16
@@ -134,9 +140,9 @@ TEXT runtime·raise(SB),NOSPLIT,$16
RET
TEXT runtime·setitimer(SB), NOSPLIT, $-8
- MOVL 8(SP), DI
- MOVQ 16(SP), SI
- MOVQ 24(SP), DX
+ MOVL mode+0(FP), DI
+ MOVQ new+8(FP), SI
+ MOVQ old+16(FP), DX
MOVL $83, AX
SYSCALL
RET
@@ -169,12 +175,13 @@ TEXT runtime·nanotime(SB), NOSPLIT, $32
// return nsec in AX
IMULQ $1000000000, AX
ADDQ DX, AX
+ MOVQ AX, ret+0(FP)
RET
TEXT runtime·sigaction(SB),NOSPLIT,$-8
- MOVL 8(SP), DI // arg 1 sig
- MOVQ 16(SP), SI // arg 2 act
- MOVQ 24(SP), DX // arg 3 oact
+ MOVL sig+0(FP), DI // arg 1 sig
+ MOVQ new+8(FP), SI // arg 2 act
+ MOVQ old+16(FP), DX // arg 3 oact
MOVL $416, AX
SYSCALL
JCC 2(PC)
@@ -215,19 +222,20 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$64
RET
TEXT runtime·mmap(SB),NOSPLIT,$0
- MOVQ 8(SP), DI // arg 1 addr
- MOVQ 16(SP), SI // arg 2 len
- MOVL 24(SP), DX // arg 3 prot
- MOVL 28(SP), R10 // arg 4 flags
- MOVL 32(SP), R8 // arg 5 fid
- MOVL 36(SP), R9 // arg 6 offset
+ MOVQ addr+0(FP), DI // arg 1 addr
+ MOVQ n+8(FP), SI // arg 2 len
+ MOVL prot+16(FP), DX // arg 3 prot
+ MOVL flags+20(FP), R10 // arg 4 flags
+ MOVL fd+24(FP), R8 // arg 5 fid
+ MOVL off+28(FP), R9 // arg 6 offset
MOVL $477, AX
SYSCALL
+ MOVQ AX, ret+32(FP)
RET
TEXT runtime·munmap(SB),NOSPLIT,$0
- MOVQ 8(SP), DI // arg 1 addr
- MOVQ 16(SP), SI // arg 2 len
+ MOVQ addr+0(FP), DI // arg 1 addr
+ MOVQ n+8(FP), SI // arg 2 len
MOVL $73, AX
SYSCALL
JCC 2(PC)
@@ -235,9 +243,9 @@ TEXT runtime·munmap(SB),NOSPLIT,$0
RET
TEXT runtime·madvise(SB),NOSPLIT,$0
- MOVQ 8(SP), DI
- MOVQ 16(SP), SI
- MOVQ 24(SP), DX
+ MOVQ addr+0(FP), DI
+ MOVQ n+8(FP), SI
+ MOVL flags+16(FP), DX
MOVQ $75, AX // madvise
SYSCALL
// ignore failure - maybe pages are locked
@@ -281,18 +289,20 @@ TEXT runtime·settls(SB),NOSPLIT,$8
RET
TEXT runtime·sysctl(SB),NOSPLIT,$0
- MOVQ 8(SP), DI // arg 1 - name
- MOVL 16(SP), SI // arg 2 - namelen
- MOVQ 24(SP), DX // arg 3 - oldp
- MOVQ 32(SP), R10 // arg 4 - oldlenp
- MOVQ 40(SP), R8 // arg 5 - newp
- MOVQ 48(SP), R9 // arg 6 - newlen
+ MOVQ mib+0(FP), DI // arg 1 - name
+ MOVL miblen+8(FP), SI // arg 2 - namelen
+ MOVQ out+16(FP), DX // arg 3 - oldp
+ MOVQ size+24(FP), R10 // arg 4 - oldlenp
+ MOVQ dst+32(FP), R8 // arg 5 - newp
+ MOVQ ndst+40(FP), R9 // arg 6 - newlen
MOVQ $202, AX // sys___sysctl
SYSCALL
- JCC 3(PC)
+ JCC 4(PC)
NEGQ AX
+ MOVL AX, ret+48(FP)
RET
MOVL $0, AX
+ MOVL AX, ret+48(FP)
RET
TEXT runtime·osyield(SB),NOSPLIT,$-4
@@ -302,8 +312,8 @@ TEXT runtime·osyield(SB),NOSPLIT,$-4
TEXT runtime·sigprocmask(SB),NOSPLIT,$0
MOVL $3, DI // arg 1 - how (SIG_SETMASK)
- MOVQ 8(SP), SI // arg 2 - set
- MOVQ 16(SP), DX // arg 3 - oset
+ MOVQ new+0(FP), SI // arg 2 - set
+ MOVQ old+8(FP), DX // arg 3 - oset
MOVL $340, AX // sys_sigprocmask
SYSCALL
JAE 2(PC)
@@ -319,25 +329,27 @@ TEXT runtime·kqueue(SB),NOSPLIT,$0
SYSCALL
JCC 2(PC)
NEGQ AX
+ MOVL AX, ret+0(FP)
RET
// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout);
TEXT runtime·kevent(SB),NOSPLIT,$0
- MOVL 8(SP), DI
- MOVQ 16(SP), SI
- MOVL 24(SP), DX
- MOVQ 32(SP), R10
- MOVL 40(SP), R8
- MOVQ 48(SP), R9
+ MOVL fd+0(FP), DI
+ MOVQ ev1+8(FP), SI
+ MOVL nev1+16(FP), DX
+ MOVQ ev2+24(FP), R10
+ MOVL nev2+32(FP), R8
+ MOVQ ts+40(FP), R9
MOVL $363, AX
SYSCALL
JCC 2(PC)
NEGQ AX
+ MOVL AX, ret+48(FP)
RET
// void runtime·closeonexec(int32 fd);
TEXT runtime·closeonexec(SB),NOSPLIT,$0
- MOVL 8(SP), DI // fd
+ MOVL fd+0(FP), DI // fd
MOVQ $2, SI // F_SETFD
MOVQ $1, DX // FD_CLOEXEC
MOVL $92, AX // fcntl
diff --git a/src/pkg/runtime/sys_freebsd_arm.s b/src/pkg/runtime/sys_freebsd_arm.s
index dbb2583b00..d875138b62 100644
--- a/src/pkg/runtime/sys_freebsd_arm.s
+++ b/src/pkg/runtime/sys_freebsd_arm.s
@@ -7,7 +7,7 @@
//
#include "zasm_GOOS_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// for EABI, as we don't support OABI
#define SYS_BASE 0x0
@@ -48,6 +48,7 @@ TEXT runtime·sys_umtx_op(SB),NOSPLIT,$0
SWI $0
SUB $20, R13
// BCS error
+ MOVW R0, ret+20(FP)
RET
TEXT runtime·thr_new(SB),NOSPLIT,$0
@@ -91,6 +92,7 @@ TEXT runtime·open(SB),NOSPLIT,$-8
MOVW 8(FP), R2 // arg 3 perm
MOVW $SYS_open, R7
SWI $0
+ MOVW R0, ret+12(FP)
RET
TEXT runtime·read(SB),NOSPLIT,$-8
@@ -99,6 +101,7 @@ TEXT runtime·read(SB),NOSPLIT,$-8
MOVW 8(FP), R2 // arg 3 count
MOVW $SYS_read, R7
SWI $0
+ MOVW R0, ret+12(FP)
RET
TEXT runtime·write(SB),NOSPLIT,$-8
@@ -107,12 +110,14 @@ TEXT runtime·write(SB),NOSPLIT,$-8
MOVW 8(FP), R2 // arg 3 count
MOVW $SYS_write, R7
SWI $0
+ MOVW R0, ret+12(FP)
RET
TEXT runtime·close(SB),NOSPLIT,$-8
MOVW 0(FP), R0 // arg 1 fd
MOVW $SYS_close, R7
SWI $0
+ MOVW R0, ret+4(FP)
RET
TEXT runtime·getrlimit(SB),NOSPLIT,$-8
@@ -120,6 +125,7 @@ TEXT runtime·getrlimit(SB),NOSPLIT,$-8
MOVW 4(FP), R1
MOVW $SYS_getrlimit, R7
SWI $0
+ MOVW R0, ret+8(FP)
RET
TEXT runtime·raise(SB),NOSPLIT,$8
@@ -178,9 +184,8 @@ TEXT runtime·nanotime(SB), NOSPLIT, $32
ADD.S R2, R0
ADC R4, R1
- MOVW 0(FP), R3
- MOVW R0, 0(R3)
- MOVW R1, 4(R3)
+ MOVW R0, ret_lo+0(FP)
+ MOVW R1, ret_hi+4(FP)
RET
TEXT runtime·sigaction(SB),NOSPLIT,$-8
@@ -247,6 +252,7 @@ TEXT runtime·mmap(SB),NOSPLIT,$16
SWI $0
SUB $4, R13
// TODO(dfc) error checking ?
+ MOVW R0, ret+24(FP)
RET
TEXT runtime·munmap(SB),NOSPLIT,$0
@@ -307,6 +313,7 @@ TEXT runtime·sysctl(SB),NOSPLIT,$0
SWI $0
SUB.CS $0, R0, R0
SUB $20, R13
+ MOVW R0, ret+24(FP)
RET
TEXT runtime·osyield(SB),NOSPLIT,$-4
@@ -329,6 +336,7 @@ TEXT runtime·kqueue(SB),NOSPLIT,$0
MOVW $SYS_kqueue, R7
SWI $0
RSB.CS $0, R0
+ MOVW R0, ret+0(FP)
RET
// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout)
@@ -342,6 +350,7 @@ TEXT runtime·kevent(SB),NOSPLIT,$0
SWI $0
RSB.CS $0, R0
SUB $20, R13
+ MOVW R0, ret+24(FP)
RET
// void runtime·closeonexec(int32 fd)
diff --git a/src/pkg/runtime/sys_linux_386.s b/src/pkg/runtime/sys_linux_386.s
index 3a8371cdd4..0f6d4bbb5e 100644
--- a/src/pkg/runtime/sys_linux_386.s
+++ b/src/pkg/runtime/sys_linux_386.s
@@ -7,57 +7,62 @@
//
#include "zasm_GOOS_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT runtime·exit(SB),NOSPLIT,$0
MOVL $252, AX // syscall number
- MOVL 4(SP), BX
+ MOVL code+0(FP), BX
CALL *runtime·_vdso(SB)
INT $3 // not reached
RET
TEXT runtime·exit1(SB),NOSPLIT,$0
MOVL $1, AX // exit - exit the current os thread
- MOVL 4(SP), BX
+ MOVL code+0(FP), BX
CALL *runtime·_vdso(SB)
INT $3 // not reached
RET
TEXT runtime·open(SB),NOSPLIT,$0
MOVL $5, AX // syscall - open
- MOVL 4(SP), BX
- MOVL 8(SP), CX
- MOVL 12(SP), DX
+ MOVL name+0(FP), BX
+ MOVL mode+4(FP), CX
+ MOVL perm+8(FP), DX
CALL *runtime·_vdso(SB)
+ MOVL AX, ret+12(FP)
RET
TEXT runtime·close(SB),NOSPLIT,$0
MOVL $6, AX // syscall - close
- MOVL 4(SP), BX
+ MOVL fd+0(FP), BX
CALL *runtime·_vdso(SB)
+ MOVL AX, ret+4(FP)
RET
TEXT runtime·write(SB),NOSPLIT,$0
MOVL $4, AX // syscall - write
- MOVL 4(SP), BX
- MOVL 8(SP), CX
- MOVL 12(SP), DX
+ MOVL fd+0(FP), BX
+ MOVL p+4(FP), CX
+ MOVL n+8(FP), DX
CALL *runtime·_vdso(SB)
+ MOVL AX, ret+12(FP)
RET
TEXT runtime·read(SB),NOSPLIT,$0
MOVL $3, AX // syscall - read
- MOVL 4(SP), BX
- MOVL 8(SP), CX
- MOVL 12(SP), DX
+ MOVL fd+0(FP), BX
+ MOVL p+4(FP), CX
+ MOVL n+8(FP), DX
CALL *runtime·_vdso(SB)
+ MOVL AX, ret+12(FP)
RET
TEXT runtime·getrlimit(SB),NOSPLIT,$0
MOVL $191, AX // syscall - ugetrlimit
- MOVL 4(SP), BX
- MOVL 8(SP), CX
+ MOVL kind+0(FP), BX
+ MOVL limit+4(FP), CX
CALL *runtime·_vdso(SB)
+ MOVL AX, ret+8(FP)
RET
TEXT runtime·usleep(SB),NOSPLIT,$8
@@ -87,20 +92,21 @@ TEXT runtime·raise(SB),NOSPLIT,$12
CALL *runtime·_vdso(SB)
RET
-TEXT runtime·setitimer(SB),NOSPLIT,$0-24
+TEXT runtime·setitimer(SB),NOSPLIT,$0-12
MOVL $104, AX // syscall - setitimer
- MOVL 4(SP), BX
- MOVL 8(SP), CX
- MOVL 12(SP), DX
+ MOVL mode+0(FP), BX
+ MOVL new+4(FP), CX
+ MOVL old+8(FP), DX
CALL *runtime·_vdso(SB)
RET
-TEXT runtime·mincore(SB),NOSPLIT,$0-24
+TEXT runtime·mincore(SB),NOSPLIT,$0-16
MOVL $218, AX // syscall - mincore
- MOVL 4(SP), BX
- MOVL 8(SP), CX
- MOVL 12(SP), DX
+ MOVL addr+0(FP), BX
+ MOVL n+4(FP), CX
+ MOVL dst+8(FP), DX
CALL *runtime·_vdso(SB)
+ MOVL AX, ret+12(FP)
RET
// func now() (sec int64, nsec int32)
@@ -137,17 +143,16 @@ TEXT runtime·nanotime(SB), NOSPLIT, $32
ADDL BX, AX
ADCL $0, DX
- MOVL ret+0(FP), DI
- MOVL AX, 0(DI)
- MOVL DX, 4(DI)
+ MOVL AX, ret_lo+0(FP)
+ MOVL DX, ret_hi+4(FP)
RET
TEXT runtime·rtsigprocmask(SB),NOSPLIT,$0
MOVL $175, AX // syscall entry
- MOVL 4(SP), BX
- MOVL 8(SP), CX
- MOVL 12(SP), DX
- MOVL 16(SP), SI
+ MOVL sig+0(FP), BX
+ MOVL new+4(FP), CX
+ MOVL old+8(FP), DX
+ MOVL size+12(FP), SI
CALL *runtime·_vdso(SB)
CMPL AX, $0xfffff001
JLS 2(PC)
@@ -156,11 +161,12 @@ TEXT runtime·rtsigprocmask(SB),NOSPLIT,$0
TEXT runtime·rt_sigaction(SB),NOSPLIT,$0
MOVL $174, AX // syscall - rt_sigaction
- MOVL 4(SP), BX
- MOVL 8(SP), CX
- MOVL 12(SP), DX
- MOVL 16(SP), SI
+ MOVL sig+0(FP), BX
+ MOVL new+4(FP), CX
+ MOVL old+8(FP), DX
+ MOVL size+12(FP), SI
CALL *runtime·_vdso(SB)
+ MOVL AX, ret+16(FP)
RET
TEXT runtime·sigtramp(SB),NOSPLIT,$44
@@ -212,24 +218,25 @@ TEXT runtime·sigreturn(SB),NOSPLIT,$0
TEXT runtime·mmap(SB),NOSPLIT,$0
MOVL $192, AX // mmap2
- MOVL 4(SP), BX
- MOVL 8(SP), CX
- MOVL 12(SP), DX
- MOVL 16(SP), SI
- MOVL 20(SP), DI
- MOVL 24(SP), BP
+ MOVL addr+0(FP), BX
+ MOVL n+4(FP), CX
+ MOVL prot+8(FP), DX
+ MOVL flags+12(FP), SI
+ MOVL fd+16(FP), DI
+ MOVL off+20(FP), BP
SHRL $12, BP
CALL *runtime·_vdso(SB)
CMPL AX, $0xfffff001
JLS 3(PC)
NOTL AX
INCL AX
+ MOVL AX, ret+24(FP)
RET
TEXT runtime·munmap(SB),NOSPLIT,$0
MOVL $91, AX // munmap
- MOVL 4(SP), BX
- MOVL 8(SP), CX
+ MOVL addr+0(FP), BX
+ MOVL n+4(FP), CX
CALL *runtime·_vdso(SB)
CMPL AX, $0xfffff001
JLS 2(PC)
@@ -238,9 +245,9 @@ TEXT runtime·munmap(SB),NOSPLIT,$0
TEXT runtime·madvise(SB),NOSPLIT,$0
MOVL $219, AX // madvise
- MOVL 4(SP), BX
- MOVL 8(SP), CX
- MOVL 12(SP), DX
+ MOVL addr+0(FP), BX
+ MOVL n+4(FP), CX
+ MOVL flags+8(FP), DX
CALL *runtime·_vdso(SB)
// ignore failure - maybe pages are locked
RET
@@ -249,13 +256,14 @@ TEXT runtime·madvise(SB),NOSPLIT,$0
// struct timespec *timeout, int32 *uaddr2, int32 val2);
TEXT runtime·futex(SB),NOSPLIT,$0
MOVL $240, AX // futex
- MOVL 4(SP), BX
- MOVL 8(SP), CX
- MOVL 12(SP), DX
- MOVL 16(SP), SI
- MOVL 20(SP), DI
- MOVL 24(SP), BP
+ MOVL addr+0(FP), BX
+ MOVL op+4(FP), CX
+ MOVL val+8(FP), DX
+ MOVL ts+12(FP), SI
+ MOVL addr2+16(FP), DI
+ MOVL val3+20(FP), BP
CALL *runtime·_vdso(SB)
+ MOVL AX, ret+24(FP)
RET
// int32 clone(int32 flags, void *stack, M *mp, G *gp, void (*fn)(void));
@@ -284,11 +292,12 @@ TEXT runtime·clone(SB),NOSPLIT,$0
// In parent, return.
CMPL AX, $0
- JEQ 2(PC)
+ JEQ 3(PC)
+ MOVL AX, ret+20(FP)
RET
// Paranoia: check that SP is as we expect.
- MOVL 12(SP), BP
+ MOVL mm+8(FP), BP
CMPL BP, $1234
JEQ 2(PC)
INT $3
@@ -299,8 +308,8 @@ TEXT runtime·clone(SB),NOSPLIT,$0
// In child on new stack. Reload registers (paranoia).
MOVL 0(SP), BX // m
- MOVL 4(SP), DX // g
- MOVL 8(SP), SI // fn
+ MOVL flags+0(FP), DX // g
+ MOVL stk+4(FP), SI // fn
MOVL AX, m_procid(BX) // save tid as m->procid
@@ -337,7 +346,6 @@ TEXT runtime·clone(SB),NOSPLIT,$0
CALL SI // fn()
CALL runtime·exit1(SB)
MOVL $0x1234, 0x1005
- RET
TEXT runtime·sigaltstack(SB),NOSPLIT,$-8
MOVL $186, AX // sigaltstack
@@ -426,50 +434,55 @@ TEXT runtime·osyield(SB),NOSPLIT,$0
TEXT runtime·sched_getaffinity(SB),NOSPLIT,$0
MOVL $242, AX // syscall - sched_getaffinity
- MOVL 4(SP), BX
- MOVL 8(SP), CX
- MOVL 12(SP), DX
+ MOVL pid+0(FP), BX
+ MOVL len+4(FP), CX
+ MOVL buf+8(FP), DX
CALL *runtime·_vdso(SB)
+ MOVL AX, ret+12(FP)
RET
// int32 runtime·epollcreate(int32 size);
TEXT runtime·epollcreate(SB),NOSPLIT,$0
MOVL $254, AX
- MOVL 4(SP), BX
+ MOVL size+0(FP), BX
CALL *runtime·_vdso(SB)
+ MOVL AX, ret+4(FP)
RET
// int32 runtime·epollcreate1(int32 flags);
TEXT runtime·epollcreate1(SB),NOSPLIT,$0
MOVL $329, AX
- MOVL 4(SP), BX
+ MOVL flags+0(FP), BX
CALL *runtime·_vdso(SB)
+ MOVL AX, ret+4(FP)
RET
-// int32 runtime·epollctl(int32 epfd, int32 op, int32 fd, EpollEvent *ev);
+// func epollctl(epfd, op, fd int32, ev *epollEvent) int
TEXT runtime·epollctl(SB),NOSPLIT,$0
MOVL $255, AX
- MOVL 4(SP), BX
- MOVL 8(SP), CX
- MOVL 12(SP), DX
- MOVL 16(SP), SI
+ MOVL epfd+0(FP), BX
+ MOVL op+4(FP), CX
+ MOVL fd+8(FP), DX
+ MOVL ev+12(FP), SI
CALL *runtime·_vdso(SB)
+ MOVL AX, ret+16(FP)
RET
// int32 runtime·epollwait(int32 epfd, EpollEvent *ev, int32 nev, int32 timeout);
TEXT runtime·epollwait(SB),NOSPLIT,$0
MOVL $256, AX
- MOVL 4(SP), BX
- MOVL 8(SP), CX
- MOVL 12(SP), DX
- MOVL 16(SP), SI
+ MOVL epfd+0(FP), BX
+ MOVL ev+4(FP), CX
+ MOVL nev+8(FP), DX
+ MOVL timeout+12(FP), SI
CALL *runtime·_vdso(SB)
+ MOVL AX, ret+16(FP)
RET
// void runtime·closeonexec(int32 fd);
TEXT runtime·closeonexec(SB),NOSPLIT,$0
MOVL $55, AX // fcntl
- MOVL 4(SP), BX // fd
+ MOVL fd+0(FP), BX // fd
MOVL $2, CX // F_SETFD
MOVL $1, DX // FD_CLOEXEC
CALL *runtime·_vdso(SB)
diff --git a/src/pkg/runtime/sys_linux_amd64.s b/src/pkg/runtime/sys_linux_amd64.s
index c402c86164..33b91e872f 100644
--- a/src/pkg/runtime/sys_linux_amd64.s
+++ b/src/pkg/runtime/sys_linux_amd64.s
@@ -7,55 +7,60 @@
//
#include "zasm_GOOS_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
-TEXT runtime·exit(SB),NOSPLIT,$0-8
- MOVL 8(SP), DI
+TEXT runtime·exit(SB),NOSPLIT,$0-4
+ MOVL code+0(FP), DI
MOVL $231, AX // exitgroup - force all os threads to exit
SYSCALL
RET
-TEXT runtime·exit1(SB),NOSPLIT,$0-8
- MOVL 8(SP), DI
+TEXT runtime·exit1(SB),NOSPLIT,$0-4
+ MOVL code+0(FP), DI
MOVL $60, AX // exit - exit the current os thread
SYSCALL
RET
-TEXT runtime·open(SB),NOSPLIT,$0-16
- MOVQ 8(SP), DI
- MOVL 16(SP), SI
- MOVL 20(SP), DX
+TEXT runtime·open(SB),NOSPLIT,$0-20
+ MOVQ name+0(FP), DI
+ MOVL mode+8(FP), SI
+ MOVL perm+12(FP), DX
MOVL $2, AX // syscall entry
SYSCALL
+ MOVL AX, ret+16(FP)
RET
-TEXT runtime·close(SB),NOSPLIT,$0-16
- MOVL 8(SP), DI
+TEXT runtime·close(SB),NOSPLIT,$0-12
+ MOVL fd+0(FP), DI
MOVL $3, AX // syscall entry
SYSCALL
+ MOVL AX, ret+8(FP)
RET
-TEXT runtime·write(SB),NOSPLIT,$0-24
- MOVL 8(SP), DI
- MOVQ 16(SP), SI
- MOVL 24(SP), DX
+TEXT runtime·write(SB),NOSPLIT,$0-28
+ MOVQ fd+0(FP), DI
+ MOVQ p+8(FP), SI
+ MOVL n+16(FP), DX
MOVL $1, AX // syscall entry
SYSCALL
+ MOVL AX, ret+24(FP)
RET
-TEXT runtime·read(SB),NOSPLIT,$0-24
- MOVL 8(SP), DI
- MOVQ 16(SP), SI
- MOVL 24(SP), DX
+TEXT runtime·read(SB),NOSPLIT,$0-28
+ MOVL fd+0(FP), DI
+ MOVQ p+8(FP), SI
+ MOVL n+16(FP), DX
MOVL $0, AX // syscall entry
SYSCALL
+ MOVL AX, ret+24(FP)
RET
-TEXT runtime·getrlimit(SB),NOSPLIT,$0-24
- MOVL 8(SP), DI
- MOVQ 16(SP), SI
+TEXT runtime·getrlimit(SB),NOSPLIT,$0-20
+ MOVL kind+0(FP), DI
+ MOVQ limit+8(FP), SI
MOVL $97, AX // syscall entry
SYSCALL
+ MOVL AX, ret+16(FP)
RET
TEXT runtime·usleep(SB),NOSPLIT,$16
@@ -86,19 +91,20 @@ TEXT runtime·raise(SB),NOSPLIT,$0
RET
TEXT runtime·setitimer(SB),NOSPLIT,$0-24
- MOVL 8(SP), DI
- MOVQ 16(SP), SI
- MOVQ 24(SP), DX
+ MOVL mode+0(FP), DI
+ MOVQ new+8(FP), SI
+ MOVQ old+16(FP), DX
MOVL $38, AX // syscall entry
SYSCALL
RET
-TEXT runtime·mincore(SB),NOSPLIT,$0-24
- MOVQ 8(SP), DI
- MOVQ 16(SP), SI
- MOVQ 24(SP), DX
+TEXT runtime·mincore(SB),NOSPLIT,$0-28
+ MOVQ addr+0(FP), DI
+ MOVQ n+8(FP), SI
+ MOVQ dst+16(FP), DX
MOVL $27, AX // syscall entry
SYSCALL
+ MOVL AX, ret+24(FP)
RET
// func now() (sec int64, nsec int32)
@@ -145,6 +151,7 @@ TEXT runtime·nanotime(SB),NOSPLIT,$16
// return nsec in AX
IMULQ $1000000000, AX
ADDQ DX, AX
+ MOVQ AX, ret+0(FP)
RET
fallback_gtod_nt:
LEAQ 0(SP), DI
@@ -158,13 +165,14 @@ fallback_gtod_nt:
// return nsec in AX
IMULQ $1000000000, AX
ADDQ DX, AX
+ MOVQ AX, ret+0(FP)
RET
-TEXT runtime·rtsigprocmask(SB),NOSPLIT,$0-32
- MOVL 8(SP), DI
- MOVQ 16(SP), SI
- MOVQ 24(SP), DX
- MOVL 32(SP), R10
+TEXT runtime·rtsigprocmask(SB),NOSPLIT,$0-28
+ MOVL sig+0(FP), DI
+ MOVQ new+8(FP), SI
+ MOVQ old+16(FP), DX
+ MOVL size+24(FP), R10
MOVL $14, AX // syscall entry
SYSCALL
CMPQ AX, $0xfffffffffffff001
@@ -172,13 +180,14 @@ TEXT runtime·rtsigprocmask(SB),NOSPLIT,$0-32
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·rt_sigaction(SB),NOSPLIT,$0-32
- MOVL 8(SP), DI
- MOVQ 16(SP), SI
- MOVQ 24(SP), DX
- MOVQ 32(SP), R10
+TEXT runtime·rt_sigaction(SB),NOSPLIT,$0-36
+ MOVQ sig+0(FP), DI
+ MOVQ new+8(FP), SI
+ MOVQ old+16(FP), DX
+ MOVQ size+24(FP), R10
MOVL $13, AX // syscall entry
SYSCALL
+ MOVL AX, ret+32(FP)
RET
TEXT runtime·sigtramp(SB),NOSPLIT,$64
@@ -220,12 +229,12 @@ TEXT runtime·sigreturn(SB),NOSPLIT,$0
INT $3 // not reached
TEXT runtime·mmap(SB),NOSPLIT,$0
- MOVQ 8(SP), DI
- MOVQ 16(SP), SI
- MOVL 24(SP), DX
- MOVL 28(SP), R10
- MOVL 32(SP), R8
- MOVL 36(SP), R9
+ MOVQ addr+0(FP), DI
+ MOVQ n+8(FP), SI
+ MOVL prot+16(FP), DX
+ MOVL flags+20(FP), R10
+ MOVL fd+24(FP), R8
+ MOVL off+28(FP), R9
MOVL $9, AX // mmap
SYSCALL
@@ -233,11 +242,12 @@ TEXT runtime·mmap(SB),NOSPLIT,$0
JLS 3(PC)
NOTQ AX
INCQ AX
+ MOVQ AX, ret+32(FP)
RET
TEXT runtime·munmap(SB),NOSPLIT,$0
- MOVQ 8(SP), DI
- MOVQ 16(SP), SI
+ MOVQ addr+0(FP), DI
+ MOVQ n+8(FP), SI
MOVQ $11, AX // munmap
SYSCALL
CMPQ AX, $0xfffffffffffff001
@@ -246,9 +256,9 @@ TEXT runtime·munmap(SB),NOSPLIT,$0
RET
TEXT runtime·madvise(SB),NOSPLIT,$0
- MOVQ 8(SP), DI
- MOVQ 16(SP), SI
- MOVQ 24(SP), DX
+ MOVQ addr+0(FP), DI
+ MOVQ n+8(FP), SI
+ MOVL flags+16(FP), DX
MOVQ $28, AX // madvise
SYSCALL
// ignore failure - maybe pages are locked
@@ -257,17 +267,18 @@ TEXT runtime·madvise(SB),NOSPLIT,$0
// int64 futex(int32 *uaddr, int32 op, int32 val,
// struct timespec *timeout, int32 *uaddr2, int32 val2);
TEXT runtime·futex(SB),NOSPLIT,$0
- MOVQ 8(SP), DI
- MOVL 16(SP), SI
- MOVL 20(SP), DX
- MOVQ 24(SP), R10
- MOVQ 32(SP), R8
- MOVL 40(SP), R9
+ MOVQ addr+0(FP), DI
+ MOVL op+8(FP), SI
+ MOVL val+12(FP), DX
+ MOVQ ts+16(FP), R10
+ MOVQ addr2+24(FP), R8
+ MOVL val3+32(FP), R9
MOVL $202, AX
SYSCALL
+ MOVL AX, ret+40(FP)
RET
-// int64 clone(int32 flags, void *stack, M *mp, G *gp, void (*fn)(void));
+// int32 clone(int32 flags, void *stack, M *mp, G *gp, void (*fn)(void));
TEXT runtime·clone(SB),NOSPLIT,$0
MOVL flags+8(SP), DI
MOVQ stack+16(SP), SI
@@ -283,7 +294,8 @@ TEXT runtime·clone(SB),NOSPLIT,$0
// In parent, return.
CMPQ AX, $0
- JEQ 2(PC)
+ JEQ 3(PC)
+ MOVL AX, ret+40(FP)
RET
// In child, on new stack.
@@ -342,50 +354,55 @@ TEXT runtime·osyield(SB),NOSPLIT,$0
RET
TEXT runtime·sched_getaffinity(SB),NOSPLIT,$0
- MOVQ 8(SP), DI
- MOVL 16(SP), SI
- MOVQ 24(SP), DX
+ MOVQ pid+0(FP), DI
+ MOVQ len+8(FP), SI
+ MOVQ buf+16(FP), DX
MOVL $204, AX // syscall entry
SYSCALL
+ MOVL AX, ret+24(FP)
RET
// int32 runtime·epollcreate(int32 size);
TEXT runtime·epollcreate(SB),NOSPLIT,$0
- MOVL 8(SP), DI
+ MOVL size+0(FP), DI
MOVL $213, AX // syscall entry
SYSCALL
+ MOVL AX, ret+8(FP)
RET
// int32 runtime·epollcreate1(int32 flags);
TEXT runtime·epollcreate1(SB),NOSPLIT,$0
- MOVL 8(SP), DI
+ MOVL flags+0(FP), DI
MOVL $291, AX // syscall entry
SYSCALL
+ MOVL AX, ret+8(FP)
RET
-// int32 runtime·epollctl(int32 epfd, int32 op, int32 fd, EpollEvent *ev);
+// func epollctl(epfd, op, fd int32, ev *epollEvent) int
TEXT runtime·epollctl(SB),NOSPLIT,$0
- MOVL 8(SP), DI
- MOVL 12(SP), SI
- MOVL 16(SP), DX
- MOVQ 24(SP), R10
+ MOVL epfd+0(FP), DI
+ MOVL op+4(FP), SI
+ MOVL fd+8(FP), DX
+ MOVQ ev+16(FP), R10
MOVL $233, AX // syscall entry
SYSCALL
+ MOVL AX, ret+24(FP)
RET
// int32 runtime·epollwait(int32 epfd, EpollEvent *ev, int32 nev, int32 timeout);
TEXT runtime·epollwait(SB),NOSPLIT,$0
- MOVL 8(SP), DI
- MOVQ 16(SP), SI
- MOVL 24(SP), DX
- MOVL 28(SP), R10
+ MOVL epfd+0(FP), DI
+ MOVQ ev+8(FP), SI
+ MOVL nev+16(FP), DX
+ MOVL timeout+20(FP), R10
MOVL $232, AX // syscall entry
SYSCALL
+ MOVL AX, ret+24(FP)
RET
// void runtime·closeonexec(int32 fd);
TEXT runtime·closeonexec(SB),NOSPLIT,$0
- MOVL 8(SP), DI // fd
+ MOVL fd+0(FP), DI // fd
MOVQ $2, SI // F_SETFD
MOVQ $1, DX // FD_CLOEXEC
MOVL $72, AX // fcntl
diff --git a/src/pkg/runtime/sys_linux_arm.s b/src/pkg/runtime/sys_linux_arm.s
index 770b963d24..bd285f3998 100644
--- a/src/pkg/runtime/sys_linux_arm.s
+++ b/src/pkg/runtime/sys_linux_arm.s
@@ -7,7 +7,7 @@
//
#include "zasm_GOOS_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// for EABI, as we don't support OABI
#define SYS_BASE 0x0
@@ -51,12 +51,14 @@ TEXT runtime·open(SB),NOSPLIT,$0
MOVW 8(FP), R2
MOVW $SYS_open, R7
SWI $0
+ MOVW R0, ret+12(FP)
RET
TEXT runtime·close(SB),NOSPLIT,$0
MOVW 0(FP), R0
MOVW $SYS_close, R7
SWI $0
+ MOVW R0, ret+4(FP)
RET
TEXT runtime·write(SB),NOSPLIT,$0
@@ -65,6 +67,7 @@ TEXT runtime·write(SB),NOSPLIT,$0
MOVW 8(FP), R2
MOVW $SYS_write, R7
SWI $0
+ MOVW R0, ret+12(FP)
RET
TEXT runtime·read(SB),NOSPLIT,$0
@@ -73,6 +76,7 @@ TEXT runtime·read(SB),NOSPLIT,$0
MOVW 8(FP), R2
MOVW $SYS_read, R7
SWI $0
+ MOVW R0, ret+12(FP)
RET
TEXT runtime·getrlimit(SB),NOSPLIT,$0
@@ -80,6 +84,7 @@ TEXT runtime·getrlimit(SB),NOSPLIT,$0
MOVW 4(FP), R1
MOVW $SYS_ugetrlimit, R7
SWI $0
+ MOVW R0, ret+8(FP)
RET
TEXT runtime·exit(SB),NOSPLIT,$-4
@@ -119,6 +124,7 @@ TEXT runtime·mmap(SB),NOSPLIT,$0
MOVW $0xfffff001, R6
CMP R6, R0
RSB.HI $0, R0
+ MOVW R0, ret+24(FP)
RET
TEXT runtime·munmap(SB),NOSPLIT,$0
@@ -155,6 +161,7 @@ TEXT runtime·mincore(SB),NOSPLIT,$0
MOVW 8(FP), R2
MOVW $SYS_mincore, R7
SWI $0
+ MOVW R0, ret+12(FP)
RET
TEXT time·now(SB), NOSPLIT, $32
@@ -172,8 +179,7 @@ TEXT time·now(SB), NOSPLIT, $32
MOVW R2, 8(FP)
RET
-// int64 nanotime(void) so really
-// void nanotime(int64 *nsec)
+// int64 nanotime(void)
TEXT runtime·nanotime(SB),NOSPLIT,$32
MOVW $1, R0 // CLOCK_MONOTONIC
MOVW $8(R13), R1 // timespec
@@ -189,9 +195,8 @@ TEXT runtime·nanotime(SB),NOSPLIT,$32
ADD.S R2, R0
ADC R4, R1
- MOVW 0(FP), R3
- MOVW R0, 0(R3)
- MOVW R1, 4(R3)
+ MOVW R0, ret_lo+0(FP)
+ MOVW R1, ret_hi+4(FP)
RET
// int32 futex(int32 *uaddr, int32 op, int32 val,
@@ -205,13 +210,14 @@ TEXT runtime·futex(SB),NOSPLIT,$0
MOVW 24(SP), R5
MOVW $SYS_futex, R7
SWI $0
+ MOVW R0, ret+24(FP)
RET
// int32 clone(int32 flags, void *stack, M *mp, G *gp, void (*fn)(void));
TEXT runtime·clone(SB),NOSPLIT,$0
MOVW flags+0(FP), R0
- MOVW stack+4(FP), R1
+ MOVW stk+4(FP), R1
MOVW $0, R2 // parent tid ptr
MOVW $0, R3 // tls_val
MOVW $0, R4 // child tid ptr
@@ -234,7 +240,8 @@ TEXT runtime·clone(SB),NOSPLIT,$0
// In parent, return.
CMP $0, R0
- BEQ 2(PC)
+ BEQ 3(PC)
+ MOVW R0, ret+20(FP)
RET
// Paranoia: check that SP is as we expect. Use R13 to avoid linker 'fixup'
@@ -338,6 +345,7 @@ TEXT runtime·rt_sigaction(SB),NOSPLIT,$0
MOVW 12(FP), R3
MOVW $SYS_rt_sigaction, R7
SWI $0
+ MOVW R0, ret+16(FP)
RET
TEXT runtime·usleep(SB),NOSPLIT,$12
@@ -363,22 +371,24 @@ TEXT cas<>(SB),NOSPLIT,$0
MOVW $0xffff0fc0, PC
TEXT runtime·cas(SB),NOSPLIT,$0
- MOVW valptr+0(FP), R2
+ MOVW ptr+0(FP), R2
MOVW old+4(FP), R0
casagain:
MOVW new+8(FP), R1
BL cas<>(SB)
BCC cascheck
MOVW $1, R0
+ MOVB R0, ret+12(FP)
RET
cascheck:
// Kernel lies; double-check.
- MOVW valptr+0(FP), R2
+ MOVW ptr+0(FP), R2
MOVW old+4(FP), R0
MOVW 0(R2), R3
CMP R0, R3
BEQ casagain
MOVW $0, R0
+ MOVB R0, ret+12(FP)
RET
TEXT runtime·casp(SB),NOSPLIT,$0
@@ -395,6 +405,7 @@ TEXT runtime·sched_getaffinity(SB),NOSPLIT,$0
MOVW 8(FP), R2
MOVW $SYS_sched_getaffinity, R7
SWI $0
+ MOVW R0, ret+12(FP)
RET
// int32 runtime·epollcreate(int32 size)
@@ -402,6 +413,7 @@ TEXT runtime·epollcreate(SB),NOSPLIT,$0
MOVW 0(FP), R0
MOVW $SYS_epoll_create, R7
SWI $0
+ MOVW R0, ret+4(FP)
RET
// int32 runtime·epollcreate1(int32 flags)
@@ -409,16 +421,18 @@ TEXT runtime·epollcreate1(SB),NOSPLIT,$0
MOVW 0(FP), R0
MOVW $SYS_epoll_create1, R7
SWI $0
+ MOVW R0, ret+4(FP)
RET
-// int32 runtime·epollctl(int32 epfd, int32 op, int32 fd, EpollEvent *ev)
+// func epollctl(epfd, op, fd int32, ev *epollEvent) int
TEXT runtime·epollctl(SB),NOSPLIT,$0
- MOVW 0(FP), R0
- MOVW 4(FP), R1
- MOVW 8(FP), R2
- MOVW 12(FP), R3
+ MOVW epfd+0(FP), R0
+ MOVW op+4(FP), R1
+ MOVW fd+8(FP), R2
+ MOVW ev+12(FP), R3
MOVW $SYS_epoll_ctl, R7
SWI $0
+ MOVW R0, ret+16(FP)
RET
// int32 runtime·epollwait(int32 epfd, EpollEvent *ev, int32 nev, int32 timeout)
@@ -429,6 +443,7 @@ TEXT runtime·epollwait(SB),NOSPLIT,$0
MOVW 12(FP), R3
MOVW $SYS_epoll_wait, R7
SWI $0
+ MOVW R0, ret+16(FP)
RET
// void runtime·closeonexec(int32 fd)
diff --git a/src/pkg/runtime/sys_nacl_386.s b/src/pkg/runtime/sys_nacl_386.s
index 50dca316df..47985f31f5 100644
--- a/src/pkg/runtime/sys_nacl_386.s
+++ b/src/pkg/runtime/sys_nacl_386.s
@@ -3,31 +3,54 @@
// license that can be found in the LICENSE file.
#include "zasm_GOOS_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
#include "syscall_nacl.h"
#define NACL_SYSCALL(code) \
MOVL $(0x10000 + ((code)<<5)), AX; CALL AX
-#define NACL_SYSJMP(code) \
- MOVL $(0x10000 + ((code)<<5)), AX; JMP AX
+TEXT runtime·exit(SB),NOSPLIT,$4
+ MOVL code+0(FP), AX
+ MOVL AX, 0(SP)
+ NACL_SYSCALL(SYS_exit)
+ JMP 0(PC)
-TEXT runtime·exit(SB),NOSPLIT,$0
- NACL_SYSJMP(SYS_exit)
-
-TEXT runtime·exit1(SB),NOSPLIT,$0
- NACL_SYSJMP(SYS_thread_exit)
+TEXT runtime·exit1(SB),NOSPLIT,$4
+ MOVL code+0(FP), AX
+ MOVL AX, 0(SP)
+ NACL_SYSCALL(SYS_thread_exit)
+ RET
-TEXT runtime·open(SB),NOSPLIT,$0
- NACL_SYSJMP(SYS_open)
+TEXT runtime·open(SB),NOSPLIT,$12
+ MOVL name+0(FP), AX
+ MOVL AX, 0(SP)
+ MOVL mode+4(FP), AX
+ MOVL AX, 4(SP)
+ MOVL perm+8(FP), AX
+ MOVL AX, 8(SP)
+ NACL_SYSCALL(SYS_open)
+ MOVL AX, ret+12(FP)
+ RET
-TEXT runtime·close(SB),NOSPLIT,$0
- NACL_SYSJMP(SYS_close)
+TEXT runtime·close(SB),NOSPLIT,$4
+ MOVL fd+0(FP), AX
+ MOVL AX, 0(SP)
+ NACL_SYSCALL(SYS_close)
+ MOVL AX, ret+4(FP)
+ RET
-TEXT runtime·read(SB),NOSPLIT,$0
- NACL_SYSJMP(SYS_read)
+TEXT runtime·read(SB),NOSPLIT,$12
+ MOVL fd+0(FP), AX
+ MOVL AX, 0(SP)
+ MOVL p+4(FP), AX
+ MOVL AX, 4(SP)
+ MOVL n+8(FP), AX
+ MOVL AX, 8(SP)
+ NACL_SYSCALL(SYS_read)
+ MOVL AX, ret+12(FP)
+ RET
-TEXT syscall·naclWrite(SB), NOSPLIT, $12-16
+TEXT syscall·naclWrite(SB), NOSPLIT, $16-16
MOVL arg1+0(FP), DI
MOVL arg2+4(FP), SI
MOVL arg3+8(FP), DX
@@ -38,80 +61,172 @@ TEXT syscall·naclWrite(SB), NOSPLIT, $12-16
MOVL AX, ret+16(FP)
RET
-TEXT runtime·write(SB),NOSPLIT,$0
- NACL_SYSJMP(SYS_write)
+TEXT runtime·write(SB),NOSPLIT,$12
+ MOVL fd+0(FP), AX
+ MOVL AX, 0(SP)
+ MOVL p+4(FP), AX
+ MOVL AX, 4(SP)
+ MOVL n+8(FP), AX
+ MOVL AX, 8(SP)
+ NACL_SYSCALL(SYS_write)
+ MOVL AX, ret+12(FP)
+ RET
-TEXT runtime·nacl_exception_stack(SB),NOSPLIT,$0
- NACL_SYSJMP(SYS_exception_stack)
+TEXT runtime·nacl_exception_stack(SB),NOSPLIT,$8
+ MOVL p+0(FP), AX
+ MOVL AX, 0(SP)
+ MOVL size+4(FP), AX
+ MOVL AX, 4(SP)
+ NACL_SYSCALL(SYS_exception_stack)
+ MOVL AX, ret+8(FP)
+ RET
-TEXT runtime·nacl_exception_handler(SB),NOSPLIT,$0
- NACL_SYSJMP(SYS_exception_handler)
+TEXT runtime·nacl_exception_handler(SB),NOSPLIT,$8
+ MOVL fn+0(FP), AX
+ MOVL AX, 0(SP)
+ MOVL arg+4(FP), AX
+ MOVL AX, 4(SP)
+ NACL_SYSCALL(SYS_exception_handler)
+ MOVL AX, ret+8(FP)
+ RET
-TEXT runtime·nacl_sem_create(SB),NOSPLIT,$0
- NACL_SYSJMP(SYS_sem_create)
+TEXT runtime·nacl_sem_create(SB),NOSPLIT,$4
+ MOVL flag+0(FP), AX
+ MOVL AX, 0(SP)
+ NACL_SYSCALL(SYS_sem_create)
+ MOVL AX, ret+4(FP)
+ RET
-TEXT runtime·nacl_sem_wait(SB),NOSPLIT,$0
- NACL_SYSJMP(SYS_sem_wait)
+TEXT runtime·nacl_sem_wait(SB),NOSPLIT,$4
+ MOVL sem+0(FP), AX
+ MOVL AX, 0(SP)
+ NACL_SYSCALL(SYS_sem_wait)
+ MOVL AX, ret+4(FP)
+ RET
-TEXT runtime·nacl_sem_post(SB),NOSPLIT,$0
- NACL_SYSJMP(SYS_sem_post)
+TEXT runtime·nacl_sem_post(SB),NOSPLIT,$4
+ MOVL sem+0(FP), AX
+ MOVL AX, 0(SP)
+ NACL_SYSCALL(SYS_sem_post)
+ MOVL AX, ret+4(FP)
+ RET
-TEXT runtime·nacl_mutex_create(SB),NOSPLIT,$0
- NACL_SYSJMP(SYS_mutex_create)
+TEXT runtime·nacl_mutex_create(SB),NOSPLIT,$4
+ MOVL flag+0(FP), AX
+ MOVL AX, 0(SP)
+ NACL_SYSCALL(SYS_mutex_create)
+ MOVL AX, ret+4(FP)
+ RET
-TEXT runtime·nacl_mutex_lock(SB),NOSPLIT,$0
- NACL_SYSJMP(SYS_mutex_lock)
+TEXT runtime·nacl_mutex_lock(SB),NOSPLIT,$4
+ MOVL mutex+0(FP), AX
+ MOVL AX, 0(SP)
+ NACL_SYSCALL(SYS_mutex_lock)
+ MOVL AX, ret+4(FP)
+ RET
-TEXT runtime·nacl_mutex_trylock(SB),NOSPLIT,$0
- NACL_SYSJMP(SYS_mutex_trylock)
+TEXT runtime·nacl_mutex_trylock(SB),NOSPLIT,$4
+ MOVL mutex+0(FP), AX
+ MOVL AX, 0(SP)
+ NACL_SYSCALL(SYS_mutex_trylock)
+ MOVL AX, ret+4(FP)
+ RET
-TEXT runtime·nacl_mutex_unlock(SB),NOSPLIT,$0
- NACL_SYSJMP(SYS_mutex_unlock)
+TEXT runtime·nacl_mutex_unlock(SB),NOSPLIT,$4
+ MOVL mutex+0(FP), AX
+ MOVL AX, 0(SP)
+ NACL_SYSCALL(SYS_mutex_unlock)
+ MOVL AX, ret+4(FP)
+ RET
-TEXT runtime·nacl_cond_create(SB),NOSPLIT,$0
- NACL_SYSJMP(SYS_cond_create)
+TEXT runtime·nacl_cond_create(SB),NOSPLIT,$4
+ MOVL flag+0(FP), AX
+ MOVL AX, 0(SP)
+ NACL_SYSCALL(SYS_cond_create)
+ MOVL AX, ret+4(FP)
+ RET
-TEXT runtime·nacl_cond_wait(SB),NOSPLIT,$0
- NACL_SYSJMP(SYS_cond_wait)
+TEXT runtime·nacl_cond_wait(SB),NOSPLIT,$8
+ MOVL cond+0(FP), AX
+ MOVL AX, 0(SP)
+ MOVL n+4(FP), AX
+ MOVL AX, 4(SP)
+ NACL_SYSCALL(SYS_cond_wait)
+ MOVL AX, ret+8(FP)
+ RET
-TEXT runtime·nacl_cond_signal(SB),NOSPLIT,$0
- NACL_SYSJMP(SYS_cond_signal)
+TEXT runtime·nacl_cond_signal(SB),NOSPLIT,$4
+ MOVL cond+0(FP), AX
+ MOVL AX, 0(SP)
+ NACL_SYSCALL(SYS_cond_signal)
+ MOVL AX, ret+4(FP)
+ RET
-TEXT runtime·nacl_cond_broadcast(SB),NOSPLIT,$0
- NACL_SYSJMP(SYS_cond_broadcast)
+TEXT runtime·nacl_cond_broadcast(SB),NOSPLIT,$4
+ MOVL cond+0(FP), AX
+ MOVL AX, 0(SP)
+ NACL_SYSCALL(SYS_cond_broadcast)
+ MOVL AX, ret+4(FP)
+ RET
-TEXT runtime·nacl_cond_timed_wait_abs(SB),NOSPLIT,$0
- NACL_SYSJMP(SYS_cond_timed_wait_abs)
+TEXT runtime·nacl_cond_timed_wait_abs(SB),NOSPLIT,$12
+ MOVL cond+0(FP), AX
+ MOVL AX, 0(SP)
+ MOVL lock+4(FP), AX
+ MOVL AX, 4(SP)
+ MOVL ts+8(FP), AX
+ MOVL AX, 8(SP)
+ NACL_SYSCALL(SYS_cond_timed_wait_abs)
+ MOVL AX, ret+12(FP)
+ RET
-TEXT runtime·nacl_thread_create(SB),NOSPLIT,$0
- NACL_SYSJMP(SYS_thread_create)
+TEXT runtime·nacl_thread_create(SB),NOSPLIT,$16
+ MOVL fn+0(FP), AX
+ MOVL AX, 0(SP)
+ MOVL stk+4(FP), AX
+ MOVL AX, 4(SP)
+ MOVL tls+8(FP), AX
+ MOVL AX, 8(SP)
+ MOVL xx+12(FP), AX
+ MOVL AX, 12(SP)
+ NACL_SYSCALL(SYS_thread_create)
+ MOVL AX, ret+16(FP)
+ RET
TEXT runtime·mstart_nacl(SB),NOSPLIT,$0
JMP runtime·mstart(SB)
-TEXT runtime·nacl_nanosleep(SB),NOSPLIT,$0
- NACL_SYSJMP(SYS_nanosleep)
+TEXT runtime·nacl_nanosleep(SB),NOSPLIT,$8
+ MOVL ts+0(FP), AX
+ MOVL AX, 0(SP)
+ MOVL extra+4(FP), AX
+ MOVL AX, 4(SP)
+ NACL_SYSCALL(SYS_nanosleep)
+ MOVL AX, ret+8(FP)
+ RET
TEXT runtime·osyield(SB),NOSPLIT,$0
- NACL_SYSJMP(SYS_sched_yield)
+ NACL_SYSCALL(SYS_sched_yield)
+ RET
TEXT runtime·mmap(SB),NOSPLIT,$32
- MOVL arg1+0(FP), AX
+ MOVL addr+0(FP), AX
MOVL AX, 0(SP)
- MOVL arg2+4(FP), AX
+ MOVL n+4(FP), AX
MOVL AX, 4(SP)
- MOVL arg3+8(FP), AX
+ MOVL prot+8(FP), AX
MOVL AX, 8(SP)
- MOVL arg4+12(FP), AX
+ MOVL flags+12(FP), AX
MOVL AX, 12(SP)
- MOVL arg5+16(FP), AX
+ MOVL fd+16(FP), AX
MOVL AX, 16(SP)
- MOVL arg6+20(FP), AX
+ MOVL off+20(FP), AX
MOVL AX, 24(SP)
MOVL $0, 28(SP)
LEAL 24(SP), AX
MOVL AX, 20(SP)
NACL_SYSCALL(SYS_mmap)
+ MOVL AX, ret+24(FP)
RET
TEXT time·now(SB),NOSPLIT,$20
@@ -132,8 +247,14 @@ TEXT time·now(SB),NOSPLIT,$20
TEXT syscall·now(SB),NOSPLIT,$0
JMP time·now(SB)
-TEXT runtime·nacl_clock_gettime(SB),NOSPLIT,$0
- NACL_SYSJMP(SYS_clock_gettime)
+TEXT runtime·nacl_clock_gettime(SB),NOSPLIT,$8
+ MOVL arg1+0(FP), AX
+ MOVL AX, 0(SP)
+ MOVL arg2+4(FP), AX
+ MOVL AX, 4(SP)
+ NACL_SYSCALL(SYS_clock_gettime)
+ MOVL AX, ret+8(FP)
+ RET
TEXT runtime·nanotime(SB),NOSPLIT,$20
MOVL $0, 0(SP) // real time clock
@@ -150,9 +271,8 @@ TEXT runtime·nanotime(SB),NOSPLIT,$20
ADDL BX, AX
ADCL $0, DX
- MOVL ret+0(FP), DI
- MOVL AX, 0(DI)
- MOVL DX, 4(DI)
+ MOVL AX, ret_lo+0(FP)
+ MOVL DX, ret_hi+4(FP)
RET
TEXT runtime·setldt(SB),NOSPLIT,$8
diff --git a/src/pkg/runtime/sys_nacl_amd64p32.s b/src/pkg/runtime/sys_nacl_amd64p32.s
index d4e32ffe39..c30c2a8933 100644
--- a/src/pkg/runtime/sys_nacl_amd64p32.s
+++ b/src/pkg/runtime/sys_nacl_amd64p32.s
@@ -3,44 +3,49 @@
// license that can be found in the LICENSE file.
#include "zasm_GOOS_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
#include "syscall_nacl.h"
#define NACL_SYSCALL(code) \
MOVL $(0x10000 + ((code)<<5)), AX; CALL AX
-#define NACL_SYSJMP(code) \
- MOVL $(0x10000 + ((code)<<5)), AX; JMP AX
-
TEXT runtime·settls(SB),NOSPLIT,$0
MOVL DI, TLS // really BP
RET
TEXT runtime·exit(SB),NOSPLIT,$0
- MOVL arg1+0(FP), DI
- NACL_SYSJMP(SYS_exit)
+ MOVL code+0(FP), DI
+ NACL_SYSCALL(SYS_exit)
+ RET
TEXT runtime·exit1(SB),NOSPLIT,$0
- MOVL arg1+0(FP), DI
- NACL_SYSJMP(SYS_thread_exit)
+ MOVL code+0(FP), DI
+ NACL_SYSCALL(SYS_thread_exit)
+ RET
TEXT runtime·open(SB),NOSPLIT,$0
- MOVL arg1+0(FP), DI
- MOVL arg2+4(FP), SI
- MOVL arg3+8(FP), DX
- NACL_SYSJMP(SYS_open)
+ MOVL name+0(FP), DI
+ MOVL mode+4(FP), SI
+ MOVL perm+8(FP), DX
+ NACL_SYSCALL(SYS_open)
+ MOVL AX, ret+16(FP)
+ RET
TEXT runtime·close(SB),NOSPLIT,$0
- MOVL arg1+0(FP), DI
- NACL_SYSJMP(SYS_close)
+ MOVL fd+0(FP), DI
+ NACL_SYSCALL(SYS_close)
+ MOVL AX, ret+8(FP)
+ RET
TEXT runtime·read(SB),NOSPLIT,$0
- MOVL arg1+0(FP), DI
- MOVL arg2+4(FP), SI
- MOVL arg3+8(FP), DX
- NACL_SYSJMP(SYS_read)
+ MOVL fd+0(FP), DI
+ MOVL p+4(FP), SI
+ MOVL n+8(FP), DX
+ NACL_SYSCALL(SYS_read)
+ MOVL AX, ret+16(FP)
+ RET
-TEXT syscall·naclWrite(SB), NOSPLIT, $16-20
+TEXT syscall·naclWrite(SB), NOSPLIT, $24-20
MOVL arg1+0(FP), DI
MOVL arg2+4(FP), SI
MOVL arg3+8(FP), DX
@@ -48,16 +53,17 @@ TEXT syscall·naclWrite(SB), NOSPLIT, $16-20
MOVL SI, 4(SP)
MOVL DX, 8(SP)
CALL runtime·write(SB)
+ MOVL 16(SP), AX
MOVL AX, ret+16(FP)
RET
-TEXT runtime·write(SB),NOSPLIT,$16-12
+TEXT runtime·write(SB),NOSPLIT,$16-20
// If using fake time and writing to stdout or stderr,
// emit playback header before actual data.
MOVQ runtime·timens(SB), AX
CMPQ AX, $0
JEQ write
- MOVL arg1+0(FP), DI
+ MOVL fd+0(FP), DI
CMPL DI, $1
JEQ playback
CMPL DI, $2
@@ -65,10 +71,11 @@ TEXT runtime·write(SB),NOSPLIT,$16-12
write:
// Ordinary write.
- MOVL arg1+0(FP), DI
- MOVL arg2+4(FP), SI
- MOVL arg3+8(FP), DX
+ MOVL fd+0(FP), DI
+ MOVL p+4(FP), SI
+ MOVL n+8(FP), DX
NACL_SYSCALL(SYS_write)
+ MOVL AX, ret+16(FP)
RET
// Write with playback header.
@@ -83,7 +90,7 @@ playback:
MOVL $(('B'<<24) | ('P'<<16)), 0(SP)
BSWAPQ AX
MOVQ AX, 4(SP)
- MOVL arg3+8(FP), DX
+ MOVL n+8(FP), DX
BSWAPL DX
MOVL DX, 12(SP)
MOVL $1, DI // standard output
@@ -93,82 +100,113 @@ playback:
// Write actual data.
MOVL $1, DI // standard output
- MOVL arg2+4(FP), SI
- MOVL arg3+8(FP), DX
+ MOVL p+4(FP), SI
+ MOVL n+8(FP), DX
NACL_SYSCALL(SYS_write)
// Unlock.
MOVL $0, runtime·writelock(SB)
+ MOVL AX, ret+16(FP)
RET
TEXT runtime·nacl_exception_stack(SB),NOSPLIT,$0
- MOVL arg1+0(FP), DI
- MOVL arg2+4(FP), SI
- NACL_SYSJMP(SYS_exception_stack)
+ MOVL p+0(FP), DI
+ MOVL size+4(FP), SI
+ NACL_SYSCALL(SYS_exception_stack)
+ MOVL AX, ret+8(FP)
+ RET
TEXT runtime·nacl_exception_handler(SB),NOSPLIT,$0
- MOVL arg1+0(FP), DI
- MOVL arg2+4(FP), SI
- NACL_SYSJMP(SYS_exception_handler)
+ MOVL fn+0(FP), DI
+ MOVL arg+4(FP), SI
+ NACL_SYSCALL(SYS_exception_handler)
+ MOVL AX, ret+8(FP)
+ RET
TEXT runtime·nacl_sem_create(SB),NOSPLIT,$0
- MOVL arg1+0(FP), DI
- NACL_SYSJMP(SYS_sem_create)
+ MOVL flag+0(FP), DI
+ NACL_SYSCALL(SYS_sem_create)
+ MOVL AX, ret+8(FP)
+ RET
TEXT runtime·nacl_sem_wait(SB),NOSPLIT,$0
- MOVL arg1+0(FP), DI
- NACL_SYSJMP(SYS_sem_wait)
+ MOVL sem+0(FP), DI
+ NACL_SYSCALL(SYS_sem_wait)
+ MOVL AX, ret+8(FP)
+ RET
TEXT runtime·nacl_sem_post(SB),NOSPLIT,$0
- MOVL arg1+0(FP), DI
- NACL_SYSJMP(SYS_sem_post)
+ MOVL sem+0(FP), DI
+ NACL_SYSCALL(SYS_sem_post)
+ MOVL AX, ret+8(FP)
+ RET
TEXT runtime·nacl_mutex_create(SB),NOSPLIT,$0
- MOVL arg1+0(FP), DI
- NACL_SYSJMP(SYS_mutex_create)
+ MOVL flag+0(FP), DI
+ NACL_SYSCALL(SYS_mutex_create)
+ MOVL AX, ret+8(FP)
+ RET
TEXT runtime·nacl_mutex_lock(SB),NOSPLIT,$0
- MOVL arg1+0(FP), DI
- NACL_SYSJMP(SYS_mutex_lock)
+ MOVL mutex+0(FP), DI
+ NACL_SYSCALL(SYS_mutex_lock)
+ MOVL AX, ret+8(FP)
+ RET
TEXT runtime·nacl_mutex_trylock(SB),NOSPLIT,$0
- MOVL arg1+0(FP), DI
- NACL_SYSJMP(SYS_mutex_trylock)
+ MOVL mutex+0(FP), DI
+ NACL_SYSCALL(SYS_mutex_trylock)
+ MOVL AX, ret+8(FP)
+ RET
TEXT runtime·nacl_mutex_unlock(SB),NOSPLIT,$0
- MOVL arg1+0(FP), DI
- NACL_SYSJMP(SYS_mutex_unlock)
+ MOVL mutex+0(FP), DI
+ NACL_SYSCALL(SYS_mutex_unlock)
+ MOVL AX, ret+8(FP)
+ RET
TEXT runtime·nacl_cond_create(SB),NOSPLIT,$0
- MOVL arg1+0(FP), DI
- NACL_SYSJMP(SYS_cond_create)
+ MOVL flag+0(FP), DI
+ NACL_SYSCALL(SYS_cond_create)
+ MOVL AX, ret+8(FP)
+ RET
TEXT runtime·nacl_cond_wait(SB),NOSPLIT,$0
- MOVL arg1+0(FP), DI
- MOVL arg2+4(FP), SI
- NACL_SYSJMP(SYS_cond_wait)
+ MOVL cond+0(FP), DI
+ MOVL n+4(FP), SI
+ NACL_SYSCALL(SYS_cond_wait)
+ MOVL AX, ret+8(FP)
+ RET
TEXT runtime·nacl_cond_signal(SB),NOSPLIT,$0
- MOVL arg1+0(FP), DI
- NACL_SYSJMP(SYS_cond_signal)
+ MOVL cond+0(FP), DI
+ NACL_SYSCALL(SYS_cond_signal)
+ MOVL AX, ret+8(FP)
+ RET
TEXT runtime·nacl_cond_broadcast(SB),NOSPLIT,$0
- MOVL arg1+0(FP), DI
- NACL_SYSJMP(SYS_cond_broadcast)
+ MOVL cond+0(FP), DI
+ NACL_SYSCALL(SYS_cond_broadcast)
+ MOVL AX, ret+8(FP)
+ RET
TEXT runtime·nacl_cond_timed_wait_abs(SB),NOSPLIT,$0
- MOVL arg1+0(FP), DI
- MOVL arg2+4(FP), SI
- MOVL arg3+8(FP), DX
- NACL_SYSJMP(SYS_cond_timed_wait_abs)
+ MOVL cond+0(FP), DI
+ MOVL lock+4(FP), SI
+ MOVL ts+8(FP), DX
+ NACL_SYSCALL(SYS_cond_timed_wait_abs)
+ MOVL AX, ret+16(FP)
+ RET
TEXT runtime·nacl_thread_create(SB),NOSPLIT,$0
- MOVL arg1+0(FP), DI
- MOVL arg2+4(FP), SI
- MOVL arg3+8(FP), DX
- MOVL arg4+12(FP), CX
- NACL_SYSJMP(SYS_thread_create)
+ MOVL fn+0(FP), DI
+ MOVL stk+4(FP), SI
+ MOVL tls+8(FP), DX
+ MOVL xx+12(FP), CX
+ NACL_SYSCALL(SYS_thread_create)
+ MOVL AX, ret+16(FP)
+ RET
TEXT runtime·mstart_nacl(SB),NOSPLIT,$0
NACL_SYSCALL(SYS_tls_get)
@@ -177,26 +215,30 @@ TEXT runtime·mstart_nacl(SB),NOSPLIT,$0
JMP runtime·mstart(SB)
TEXT runtime·nacl_nanosleep(SB),NOSPLIT,$0
- MOVL arg1+0(FP), DI
- MOVL arg2+4(FP), SI
- NACL_SYSJMP(SYS_nanosleep)
+ MOVL ts+0(FP), DI
+ MOVL extra+4(FP), SI
+ NACL_SYSCALL(SYS_nanosleep)
+ MOVL AX, ret+8(FP)
+ RET
TEXT runtime·osyield(SB),NOSPLIT,$0
- NACL_SYSJMP(SYS_sched_yield)
+ NACL_SYSCALL(SYS_sched_yield)
+ RET
TEXT runtime·mmap(SB),NOSPLIT,$8
- MOVL arg1+0(FP), DI
- MOVL arg2+4(FP), SI
- MOVL arg3+8(FP), DX
- MOVL arg4+12(FP), CX
- MOVL arg5+16(FP), R8
- MOVL arg6+20(FP), AX
+ MOVL addr+0(FP), DI
+ MOVL n+4(FP), SI
+ MOVL prot+8(FP), DX
+ MOVL flags+12(FP), CX
+ MOVL fd+16(FP), R8
+ MOVL off+20(FP), AX
MOVQ AX, 0(SP)
MOVL SP, R9
NACL_SYSCALL(SYS_mmap)
CMPL AX, $-4095
JNA 2(PC)
NEGL AX
+ MOVL AX, ret+24(FP)
RET
TEXT time·now(SB),NOSPLIT,$16
@@ -230,12 +272,15 @@ TEXT syscall·now(SB),NOSPLIT,$0
TEXT runtime·nacl_clock_gettime(SB),NOSPLIT,$0
MOVL arg1+0(FP), DI
MOVL arg2+4(FP), SI
- NACL_SYSJMP(SYS_clock_gettime)
+ NACL_SYSCALL(SYS_clock_gettime)
+ MOVL AX, ret+8(FP)
+ RET
TEXT runtime·nanotime(SB),NOSPLIT,$16
MOVQ runtime·timens(SB), AX
CMPQ AX, $0
- JEQ 2(PC)
+ JEQ 3(PC)
+ MOVQ AX, ret+0(FP)
RET
MOVL $0, DI // real time clock
LEAL 0(SP), AX
@@ -248,6 +293,7 @@ TEXT runtime·nanotime(SB),NOSPLIT,$16
// return nsec in AX
IMULQ $1000000000, AX
ADDQ DX, AX
+ MOVQ AX, ret+0(FP)
RET
TEXT runtime·sigtramp(SB),NOSPLIT,$80
diff --git a/src/pkg/runtime/sys_nacl_arm.s b/src/pkg/runtime/sys_nacl_arm.s
index 6a22368507..d354ab4837 100644
--- a/src/pkg/runtime/sys_nacl_arm.s
+++ b/src/pkg/runtime/sys_nacl_arm.s
@@ -3,38 +3,43 @@
// license that can be found in the LICENSE file.
#include "zasm_GOOS_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
#include "syscall_nacl.h"
#define NACL_SYSCALL(code) \
MOVW $(0x10000 + ((code)<<5)), R8; BL (R8)
-#define NACL_SYSJMP(code) \
- MOVW $(0x10000 + ((code)<<5)), R8; B (R8)
-
TEXT runtime·exit(SB),NOSPLIT,$0
- MOVW arg1+0(FP), R0
- NACL_SYSJMP(SYS_exit)
+ MOVW code+0(FP), R0
+ NACL_SYSCALL(SYS_exit)
+ RET
TEXT runtime·exit1(SB),NOSPLIT,$0
- MOVW arg1+0(FP), R0
- NACL_SYSJMP(SYS_thread_exit)
+ MOVW code+0(FP), R0
+ NACL_SYSCALL(SYS_thread_exit)
+ RET
TEXT runtime·open(SB),NOSPLIT,$0
- MOVW arg1+0(FP), R0
- MOVW arg2+0(FP), R1
- MOVW arg3+0(FP), R2
- NACL_SYSJMP(SYS_open)
+ MOVW name+0(FP), R0
+ MOVW name+0(FP), R1
+ MOVW name+0(FP), R2
+ NACL_SYSCALL(SYS_open)
+ MOVW R0, ret+12(FP)
+ RET
TEXT runtime·close(SB),NOSPLIT,$0
- MOVW arg1+0(FP), R0
- NACL_SYSJMP(SYS_close)
+ MOVW fd+0(FP), R0
+ NACL_SYSCALL(SYS_close)
+ MOVW R0, ret+4(FP)
+ RET
TEXT runtime·read(SB),NOSPLIT,$0
- MOVW arg1+0(FP), R0
- MOVW arg2+4(FP), R1
- MOVW arg3+8(FP), R2
- NACL_SYSJMP(SYS_read)
+ MOVW fd+0(FP), R0
+ MOVW p+4(FP), R1
+ MOVW n+8(FP), R2
+ NACL_SYSCALL(SYS_read)
+ MOVW R0, ret+12(FP)
+ RET
// func naclWrite(fd int, b []byte) int
TEXT syscall·naclWrite(SB),NOSPLIT,$0
@@ -46,78 +51,110 @@ TEXT syscall·naclWrite(SB),NOSPLIT,$0
RET
TEXT runtime·write(SB),NOSPLIT,$0
- MOVW arg1+0(FP), R0
- MOVW arg2+4(FP), R1
- MOVW arg3+8(FP), R2
- NACL_SYSJMP(SYS_write)
+ MOVW fd+0(FP), R0
+ MOVW p+4(FP), R1
+ MOVW n+8(FP), R2
+ NACL_SYSCALL(SYS_write)
+ MOVW R0, ret+12(FP)
+ RET
TEXT runtime·nacl_exception_stack(SB),NOSPLIT,$0
- MOVW arg1+0(FP), R0
- MOVW arg2+4(FP), R1
- NACL_SYSJMP(SYS_exception_stack)
+ MOVW p+0(FP), R0
+ MOVW size+4(FP), R1
+ NACL_SYSCALL(SYS_exception_stack)
+ MOVW R0, ret+8(FP)
+ RET
TEXT runtime·nacl_exception_handler(SB),NOSPLIT,$0
- MOVW arg1+0(FP), R0
- MOVW arg2+4(FP), R1
- NACL_SYSJMP(SYS_exception_handler)
+ MOVW fn+0(FP), R0
+ MOVW arg+4(FP), R1
+ NACL_SYSCALL(SYS_exception_handler)
+ MOVW R0, ret+8(FP)
+ RET
TEXT runtime·nacl_sem_create(SB),NOSPLIT,$0
- MOVW arg1+0(FP), R0
- NACL_SYSJMP(SYS_sem_create)
+ MOVW flag+0(FP), R0
+ NACL_SYSCALL(SYS_sem_create)
+ MOVW R0, ret+4(FP)
+ RET
TEXT runtime·nacl_sem_wait(SB),NOSPLIT,$0
- MOVW arg1+0(FP), R0
- NACL_SYSJMP(SYS_sem_wait)
+ MOVW sem+0(FP), R0
+ NACL_SYSCALL(SYS_sem_wait)
+ MOVW R0, ret+4(FP)
+ RET
TEXT runtime·nacl_sem_post(SB),NOSPLIT,$0
- MOVW arg1+0(FP), R0
- NACL_SYSJMP(SYS_sem_post)
+ MOVW sem+0(FP), R0
+ NACL_SYSCALL(SYS_sem_post)
+ MOVW R0, ret+4(FP)
+ RET
TEXT runtime·nacl_mutex_create(SB),NOSPLIT,$0
- MOVW arg1+0(FP), R0
- NACL_SYSJMP(SYS_mutex_create)
+ MOVW flag+0(FP), R0
+ NACL_SYSCALL(SYS_mutex_create)
+ MOVW R0, ret+4(FP)
+ RET
TEXT runtime·nacl_mutex_lock(SB),NOSPLIT,$0
- MOVW arg1+0(FP), R0
- NACL_SYSJMP(SYS_mutex_lock)
+ MOVW mutex+0(FP), R0
+ NACL_SYSCALL(SYS_mutex_lock)
+ MOVW R0, ret+4(FP)
+ RET
TEXT runtime·nacl_mutex_trylock(SB),NOSPLIT,$0
- MOVW arg1+0(FP), R0
- NACL_SYSJMP(SYS_mutex_trylock)
+ MOVW mutex+0(FP), R0
+ NACL_SYSCALL(SYS_mutex_trylock)
+ MOVW R0, ret+4(FP)
+ RET
TEXT runtime·nacl_mutex_unlock(SB),NOSPLIT,$0
- MOVW arg1+0(FP), R0
- NACL_SYSJMP(SYS_mutex_unlock)
+ MOVW mutex+0(FP), R0
+ NACL_SYSCALL(SYS_mutex_unlock)
+ MOVW R0, ret+4(FP)
+ RET
TEXT runtime·nacl_cond_create(SB),NOSPLIT,$0
- MOVW arg1+0(FP), R0
- NACL_SYSJMP(SYS_cond_create)
+ MOVW flag+0(FP), R0
+ NACL_SYSCALL(SYS_cond_create)
+ MOVW R0, ret+4(FP)
+ RET
TEXT runtime·nacl_cond_wait(SB),NOSPLIT,$0
- MOVW arg1+0(FP), R0
- MOVW arg2+4(FP), R1
- NACL_SYSJMP(SYS_cond_wait)
+ MOVW cond+0(FP), R0
+ MOVW n+4(FP), R1
+ NACL_SYSCALL(SYS_cond_wait)
+ MOVW R0, ret+8(FP)
+ RET
TEXT runtime·nacl_cond_signal(SB),NOSPLIT,$0
- MOVW arg1+0(FP), R0
- NACL_SYSJMP(SYS_cond_signal)
+ MOVW cond+0(FP), R0
+ NACL_SYSCALL(SYS_cond_signal)
+ MOVW R0, ret+4(FP)
+ RET
TEXT runtime·nacl_cond_broadcast(SB),NOSPLIT,$0
- MOVW arg1+0(FP), R0
- NACL_SYSJMP(SYS_cond_broadcast)
+ MOVW cond+0(FP), R0
+ NACL_SYSCALL(SYS_cond_broadcast)
+ MOVW R0, ret+4(FP)
+ RET
TEXT runtime·nacl_cond_timed_wait_abs(SB),NOSPLIT,$0
- MOVW arg1+0(FP), R0
- MOVW arg2+4(FP), R1
- MOVW arg3+8(FP), R2
- NACL_SYSJMP(SYS_cond_timed_wait_abs)
+ MOVW cond+0(FP), R0
+ MOVW lock+4(FP), R1
+ MOVW ts+8(FP), R2
+ NACL_SYSCALL(SYS_cond_timed_wait_abs)
+ MOVW R0, ret+12(FP)
+ RET
TEXT runtime·nacl_thread_create(SB),NOSPLIT,$0
- MOVW arg1+0(FP), R0
- MOVW arg2+4(FP), R1
- MOVW arg3+8(FP), R2
- MOVW arg4+12(FP), R3
- NACL_SYSJMP(SYS_thread_create)
+ MOVW fn+0(FP), R0
+ MOVW stk+4(FP), R1
+ MOVW tls+8(FP), R2
+ MOVW xx+12(FP), R3
+ NACL_SYSCALL(SYS_thread_create)
+ MOVW R0, ret+16(FP)
+ RET
TEXT runtime·mstart_nacl(SB),NOSPLIT,$0
MOVW 0(R9), R0 // TLS
@@ -128,21 +165,24 @@ TEXT runtime·mstart_nacl(SB),NOSPLIT,$0
B runtime·mstart(SB)
TEXT runtime·nacl_nanosleep(SB),NOSPLIT,$0
- MOVW arg1+0(FP), R0
- MOVW arg2+4(FP), R1
- NACL_SYSJMP(SYS_nanosleep)
+ MOVW ts+0(FP), R0
+ MOVW extra+4(FP), R1
+ NACL_SYSCALL(SYS_nanosleep)
+ MOVW R0, ret+8(FP)
+ RET
TEXT runtime·osyield(SB),NOSPLIT,$0
- NACL_SYSJMP(SYS_sched_yield)
+ NACL_SYSCALL(SYS_sched_yield)
+ RET
TEXT runtime·mmap(SB),NOSPLIT,$8
- MOVW arg1+0(FP), R0
- MOVW arg2+4(FP), R1
- MOVW arg3+8(FP), R2
- MOVW arg4+12(FP), R3
- MOVW arg5+16(FP), R4
+ MOVW addr+0(FP), R0
+ MOVW n+4(FP), R1
+ MOVW prot+8(FP), R2
+ MOVW flags+12(FP), R3
+ MOVW fd+16(FP), R4
// arg6:offset should be passed as a pointer (to int64)
- MOVW arg6+20(FP), R5
+ MOVW off+20(FP), R5
MOVW R5, 4(R13)
MOVW $0, R6
MOVW R6, 8(R13)
@@ -152,6 +192,7 @@ TEXT runtime·mmap(SB),NOSPLIT,$8
MOVM.IA.W (R13), [R4, R5]
CMP $-4095, R0
RSB.HI $0, R0
+ MOVW R0, ret+24(FP)
RET
TEXT time·now(SB),NOSPLIT,$16
@@ -172,7 +213,9 @@ TEXT syscall·now(SB),NOSPLIT,$0
TEXT runtime·nacl_clock_gettime(SB),NOSPLIT,$0
MOVW arg1+0(FP), R0
MOVW arg2+4(FP), R1
- NACL_SYSJMP(SYS_clock_gettime)
+ NACL_SYSCALL(SYS_clock_gettime)
+ MOVW R0, ret+8(FP)
+ RET
// int64 nanotime(void) so really
// void nanotime(int64 *nsec)
@@ -188,9 +231,8 @@ TEXT runtime·nanotime(SB),NOSPLIT,$16
MOVW $0, R4
ADD.S R2, R0
ADC R4, R1
- MOVW 0(FP), R2
- MOVW R0, 0(R2)
- MOVW R1, 4(R2)
+ MOVW R0, ret_lo+0(FP)
+ MOVW R1, ret_hi+4(FP)
RET
TEXT runtime·sigtramp(SB),NOSPLIT,$80
diff --git a/src/pkg/runtime/sys_netbsd_386.s b/src/pkg/runtime/sys_netbsd_386.s
index 4a78cb9d2e..83a76cb343 100644
--- a/src/pkg/runtime/sys_netbsd_386.s
+++ b/src/pkg/runtime/sys_netbsd_386.s
@@ -7,7 +7,7 @@
//
#include "zasm_GOOS_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// Exit the entire program (like C exit)
TEXT runtime·exit(SB),NOSPLIT,$-4
@@ -26,21 +26,25 @@ TEXT runtime·exit1(SB),NOSPLIT,$-4
TEXT runtime·open(SB),NOSPLIT,$-4
MOVL $5, AX
INT $0x80
+ MOVL AX, ret+12(FP)
RET
TEXT runtime·close(SB),NOSPLIT,$-4
MOVL $6, AX
INT $0x80
+ MOVL AX, ret+4(FP)
RET
TEXT runtime·read(SB),NOSPLIT,$-4
MOVL $3, AX
INT $0x80
+ MOVL AX, ret+12(FP)
RET
TEXT runtime·write(SB),NOSPLIT,$-4
MOVL $4, AX // sys_write
INT $0x80
+ MOVL AX, ret+12(FP)
RET
TEXT runtime·usleep(SB),NOSPLIT,$24
@@ -74,7 +78,7 @@ TEXT runtime·raise(SB),NOSPLIT,$12
RET
TEXT runtime·mmap(SB),NOSPLIT,$36
- LEAL arg0+0(FP), SI
+ LEAL addr+0(FP), SI
LEAL 4(SP), DI
CLD
MOVSL // arg 1 - addr
@@ -89,6 +93,7 @@ TEXT runtime·mmap(SB),NOSPLIT,$36
STOSL
MOVL $197, AX // sys_mmap
INT $0x80
+ MOVL AX, ret+24(FP)
RET
TEXT runtime·munmap(SB),NOSPLIT,$-4
@@ -146,9 +151,8 @@ TEXT runtime·nanotime(SB),NOSPLIT,$32
ADDL BX, AX
ADCL CX, DX // add high bits with carry
- MOVL ret+0(FP), DI
- MOVL AX, 0(DI)
- MOVL DX, 4(DI)
+ MOVL AX, ret_lo+0(FP)
+ MOVL DX, ret_hi+4(FP)
RET
TEXT runtime·getcontext(SB),NOSPLIT,$-4
@@ -175,7 +179,7 @@ TEXT runtime·sigreturn_tramp(SB),NOSPLIT,$0
INT $0x80
TEXT runtime·sigaction(SB),NOSPLIT,$24
- LEAL arg0+0(FP), SI
+ LEAL sig+0(FP), SI
LEAL 4(SP), DI
CLD
MOVSL // arg 1 - sig
@@ -232,7 +236,7 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$44
// int32 lwp_create(void *context, uintptr flags, void *lwpid);
TEXT runtime·lwp_create(SB),NOSPLIT,$16
MOVL $0, 0(SP)
- MOVL context+0(FP), AX
+ MOVL ctxt+0(FP), AX
MOVL AX, 4(SP) // arg 1 - context
MOVL flags+4(FP), AX
MOVL AX, 8(SP) // arg 2 - flags
@@ -242,6 +246,7 @@ TEXT runtime·lwp_create(SB),NOSPLIT,$16
INT $0x80
JCC 2(PC)
NEGL AX
+ MOVL AX, ret+12(FP)
RET
TEXT runtime·lwp_tramp(SB),NOSPLIT,$0
@@ -312,20 +317,23 @@ TEXT runtime·osyield(SB),NOSPLIT,$-4
TEXT runtime·lwp_park(SB),NOSPLIT,$-4
MOVL $434, AX // sys__lwp_park
INT $0x80
+ MOVL AX, ret+16(FP)
RET
TEXT runtime·lwp_unpark(SB),NOSPLIT,$-4
MOVL $321, AX // sys__lwp_unpark
INT $0x80
+ MOVL AX, ret+8(FP)
RET
TEXT runtime·lwp_self(SB),NOSPLIT,$-4
MOVL $311, AX // sys__lwp_self
INT $0x80
+ MOVL AX, ret+0(FP)
RET
TEXT runtime·sysctl(SB),NOSPLIT,$28
- LEAL arg0+0(FP), SI
+ LEAL mib+0(FP), SI
LEAL 4(SP), DI
CLD
MOVSL // arg 1 - name
@@ -350,6 +358,7 @@ TEXT runtime·kqueue(SB),NOSPLIT,$0
INT $0x80
JAE 2(PC)
NEGL AX
+ MOVL AX, ret+0(FP)
RET
// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout)
@@ -358,6 +367,7 @@ TEXT runtime·kevent(SB),NOSPLIT,$0
INT $0x80
JAE 2(PC)
NEGL AX
+ MOVL AX, ret+24(FP)
RET
// int32 runtime·closeonexec(int32 fd)
diff --git a/src/pkg/runtime/sys_netbsd_amd64.s b/src/pkg/runtime/sys_netbsd_amd64.s
index 13b1cdc2f7..eb9766d3f5 100644
--- a/src/pkg/runtime/sys_netbsd_amd64.s
+++ b/src/pkg/runtime/sys_netbsd_amd64.s
@@ -7,17 +7,18 @@
//
#include "zasm_GOOS_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// int32 lwp_create(void *context, uintptr flags, void *lwpid)
TEXT runtime·lwp_create(SB),NOSPLIT,$0
- MOVQ context+0(FP), DI
+ MOVQ ctxt+0(FP), DI
MOVQ flags+8(FP), SI
MOVQ lwpid+16(FP), DX
MOVL $309, AX // sys__lwp_create
SYSCALL
JCC 2(PC)
NEGQ AX
+ MOVL AX, ret+24(FP)
RET
TEXT runtime·lwp_tramp(SB),NOSPLIT,$0
@@ -46,29 +47,32 @@ TEXT runtime·osyield(SB),NOSPLIT,$0
RET
TEXT runtime·lwp_park(SB),NOSPLIT,$0
- MOVQ 8(SP), DI // arg 1 - abstime
- MOVL 16(SP), SI // arg 2 - unpark
- MOVQ 24(SP), DX // arg 3 - hint
- MOVQ 32(SP), R10 // arg 4 - unparkhint
+ MOVQ abstime+0(FP), DI // arg 1 - abstime
+ MOVL unpark+8(FP), SI // arg 2 - unpark
+ MOVQ hint+16(FP), DX // arg 3 - hint
+ MOVQ unparkhint+24(FP), R10 // arg 4 - unparkhint
MOVL $434, AX // sys__lwp_park
SYSCALL
+ MOVL AX, ret+32(FP)
RET
TEXT runtime·lwp_unpark(SB),NOSPLIT,$0
- MOVQ 8(SP), DI // arg 1 - lwp
- MOVL 16(SP), SI // arg 2 - hint
+ MOVL lwp+0(FP), DI // arg 1 - lwp
+ MOVQ hint+8(FP), SI // arg 2 - hint
MOVL $321, AX // sys__lwp_unpark
SYSCALL
+ MOVL AX, ret+16(FP)
RET
TEXT runtime·lwp_self(SB),NOSPLIT,$0
MOVL $311, AX // sys__lwp_self
SYSCALL
+ MOVL AX, ret+0(FP)
RET
// Exit the entire program (like C exit)
TEXT runtime·exit(SB),NOSPLIT,$-8
- MOVL 8(SP), DI // arg 1 - exit status
+ MOVL code+0(FP), DI // arg 1 - exit status
MOVL $1, AX // sys_exit
SYSCALL
MOVL $0xf1, 0xf1 // crash
@@ -81,33 +85,37 @@ TEXT runtime·exit1(SB),NOSPLIT,$-8
RET
TEXT runtime·open(SB),NOSPLIT,$-8
- MOVQ 8(SP), DI // arg 1 pathname
- MOVL 16(SP), SI // arg 2 flags
- MOVL 20(SP), DX // arg 3 mode
+ MOVQ name+0(FP), DI // arg 1 pathname
+ MOVL mode+8(FP), SI // arg 2 flags
+ MOVL perm+12(FP), DX // arg 3 mode
MOVL $5, AX
SYSCALL
+ MOVL AX, ret+16(FP)
RET
TEXT runtime·close(SB),NOSPLIT,$-8
- MOVL 8(SP), DI // arg 1 fd
+ MOVL fd+0(FP), DI // arg 1 fd
MOVL $6, AX
SYSCALL
+ MOVL AX, ret+8(FP)
RET
TEXT runtime·read(SB),NOSPLIT,$-8
- MOVL 8(SP), DI // arg 1 fd
- MOVQ 16(SP), SI // arg 2 buf
- MOVL 24(SP), DX // arg 3 count
+ MOVL fd+0(FP), DI // arg 1 fd
+ MOVQ p+8(FP), SI // arg 2 buf
+ MOVL n+16(FP), DX // arg 3 count
MOVL $3, AX
SYSCALL
+ MOVL AX, ret+24(FP)
RET
TEXT runtime·write(SB),NOSPLIT,$-8
- MOVL 8(SP), DI // arg 1 - fd
- MOVQ 16(SP), SI // arg 2 - buf
- MOVL 24(SP), DX // arg 3 - nbyte
+ MOVQ fd+0(FP), DI // arg 1 - fd
+ MOVQ p+8(FP), SI // arg 2 - buf
+ MOVL n+16(FP), DX // arg 3 - nbyte
MOVL $4, AX // sys_write
SYSCALL
+ MOVL AX, ret+24(FP)
RET
TEXT runtime·usleep(SB),NOSPLIT,$16
@@ -136,9 +144,9 @@ TEXT runtime·raise(SB),NOSPLIT,$16
RET
TEXT runtime·setitimer(SB),NOSPLIT,$-8
- MOVL 8(SP), DI // arg 1 - which
- MOVQ 16(SP), SI // arg 2 - itv
- MOVQ 24(SP), DX // arg 3 - oitv
+ MOVL mode+0(FP), DI // arg 1 - which
+ MOVQ new+8(FP), SI // arg 2 - itv
+ MOVQ old+16(FP), DX // arg 3 - oitv
MOVL $425, AX // sys_setitimer
SYSCALL
RET
@@ -169,10 +177,11 @@ TEXT runtime·nanotime(SB),NOSPLIT,$32
// return nsec in AX
IMULQ $1000000000, AX
ADDQ DX, AX
+ MOVQ AX, ret+0(FP)
RET
TEXT runtime·getcontext(SB),NOSPLIT,$-8
- MOVQ 8(SP), DI // arg 1 - context
+ MOVQ ctxt+0(FP), DI // arg 1 - context
MOVL $307, AX // sys_getcontext
SYSCALL
JCC 2(PC)
@@ -180,9 +189,9 @@ TEXT runtime·getcontext(SB),NOSPLIT,$-8
RET
TEXT runtime·sigprocmask(SB),NOSPLIT,$0
- MOVL 8(SP), DI // arg 1 - how
- MOVQ 16(SP), SI // arg 2 - set
- MOVQ 24(SP), DX // arg 3 - oset
+ MOVL mode+0(FP), DI // arg 1 - how
+ MOVQ new+8(FP), SI // arg 2 - set
+ MOVQ old+16(FP), DX // arg 3 - oset
MOVL $293, AX // sys_sigprocmask
SYSCALL
JCC 2(PC)
@@ -198,9 +207,9 @@ TEXT runtime·sigreturn_tramp(SB),NOSPLIT,$-8
SYSCALL
TEXT runtime·sigaction(SB),NOSPLIT,$-8
- MOVL 8(SP), DI // arg 1 - signum
- MOVQ 16(SP), SI // arg 2 - nsa
- MOVQ 24(SP), DX // arg 3 - osa
+ MOVL sig+0(FP), DI // arg 1 - signum
+ MOVQ new+8(FP), SI // arg 2 - nsa
+ MOVQ old+16(FP), DX // arg 3 - osa
// arg 4 - tramp
LEAQ runtime·sigreturn_tramp(SB), R10
MOVQ $2, R8 // arg 5 - vers
@@ -244,23 +253,24 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$64
RET
TEXT runtime·mmap(SB),NOSPLIT,$0
- MOVQ 8(SP), DI // arg 1 - addr
- MOVQ 16(SP), SI // arg 2 - len
- MOVL 24(SP), DX // arg 3 - prot
- MOVL 28(SP), R10 // arg 4 - flags
- MOVL 32(SP), R8 // arg 5 - fd
- MOVQ 36(SP), R9
+ MOVQ addr+0(FP), DI // arg 1 - addr
+ MOVQ n+8(FP), SI // arg 2 - len
+ MOVL prot+16(FP), DX // arg 3 - prot
+ MOVL flags+20(FP), R10 // arg 4 - flags
+ MOVL fd+24(FP), R8 // arg 5 - fd
+ MOVL off+28(FP), R9
SUBQ $16, SP
MOVQ R9, 8(SP) // arg 7 - offset (passed on stack)
MOVQ $0, R9 // arg 6 - pad
MOVL $197, AX // sys_mmap
SYSCALL
ADDQ $16, SP
+ MOVQ AX, ret+32(FP)
RET
TEXT runtime·munmap(SB),NOSPLIT,$0
- MOVQ 8(SP), DI // arg 1 - addr
- MOVQ 16(SP), SI // arg 2 - len
+ MOVQ addr+0(FP), DI // arg 1 - addr
+ MOVQ n+8(FP), SI // arg 2 - len
MOVL $73, AX // sys_munmap
SYSCALL
JCC 2(PC)
@@ -270,8 +280,8 @@ TEXT runtime·munmap(SB),NOSPLIT,$0
TEXT runtime·madvise(SB),NOSPLIT,$0
MOVQ addr+0(FP), DI // arg 1 - addr
- MOVQ len+8(FP), SI // arg 2 - len
- MOVQ behav+16(FP), DX // arg 3 - behav
+ MOVQ n+8(FP), SI // arg 2 - len
+ MOVL flags+16(FP), DX // arg 3 - behav
MOVQ $75, AX // sys_madvise
SYSCALL
// ignore failure - maybe pages are locked
@@ -297,18 +307,20 @@ TEXT runtime·settls(SB),NOSPLIT,$8
RET
TEXT runtime·sysctl(SB),NOSPLIT,$0
- MOVQ 8(SP), DI // arg 1 - name
- MOVL 16(SP), SI // arg 2 - namelen
- MOVQ 24(SP), DX // arg 3 - oldp
- MOVQ 32(SP), R10 // arg 4 - oldlenp
- MOVQ 40(SP), R8 // arg 5 - newp
- MOVQ 48(SP), R9 // arg 6 - newlen
+ MOVQ mib+0(FP), DI // arg 1 - name
+ MOVL miblen+8(FP), SI // arg 2 - namelen
+ MOVQ out+16(FP), DX // arg 3 - oldp
+ MOVQ size+24(FP), R10 // arg 4 - oldlenp
+ MOVQ dst+32(FP), R8 // arg 5 - newp
+ MOVQ ndst+40(FP), R9 // arg 6 - newlen
MOVQ $202, AX // sys___sysctl
SYSCALL
- JCC 3(PC)
+ JCC 4(PC)
NEGQ AX
+ MOVL AX, ret+48(FP)
RET
MOVL $0, AX
+ MOVL AX, ret+48(FP)
RET
// int32 runtime·kqueue(void)
@@ -318,25 +330,27 @@ TEXT runtime·kqueue(SB),NOSPLIT,$0
SYSCALL
JCC 2(PC)
NEGQ AX
+ MOVL AX, ret+0(FP)
RET
// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout)
TEXT runtime·kevent(SB),NOSPLIT,$0
- MOVL 8(SP), DI
- MOVQ 16(SP), SI
- MOVL 24(SP), DX
- MOVQ 32(SP), R10
- MOVL 40(SP), R8
- MOVQ 48(SP), R9
+ MOVL fd+0(FP), DI
+ MOVQ ev1+8(FP), SI
+ MOVL nev1+16(FP), DX
+ MOVQ ev2+24(FP), R10
+ MOVL nev2+32(FP), R8
+ MOVQ ts+40(FP), R9
MOVL $435, AX
SYSCALL
JCC 2(PC)
NEGQ AX
+ MOVL AX, ret+48(FP)
RET
// void runtime·closeonexec(int32 fd)
TEXT runtime·closeonexec(SB),NOSPLIT,$0
- MOVL 8(SP), DI // fd
+ MOVL fd+0(FP), DI // fd
MOVQ $2, SI // F_SETFD
MOVQ $1, DX // FD_CLOEXEC
MOVL $92, AX // fcntl
diff --git a/src/pkg/runtime/sys_netbsd_arm.s b/src/pkg/runtime/sys_netbsd_arm.s
index acf01cf3bb..039a0832e0 100644
--- a/src/pkg/runtime/sys_netbsd_arm.s
+++ b/src/pkg/runtime/sys_netbsd_arm.s
@@ -7,7 +7,7 @@
//
#include "zasm_GOOS_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// Exit the entire program (like C exit)
TEXT runtime·exit(SB),NOSPLIT,$-4
@@ -28,11 +28,13 @@ TEXT runtime·open(SB),NOSPLIT,$-8
MOVW 4(FP), R1
MOVW 8(FP), R2
SWI $0xa00005
+ MOVW R0, ret+12(FP)
RET
TEXT runtime·close(SB),NOSPLIT,$-8
MOVW 0(FP), R0
SWI $0xa00006
+ MOVW R0, ret+4(FP)
RET
TEXT runtime·read(SB),NOSPLIT,$-8
@@ -40,6 +42,7 @@ TEXT runtime·read(SB),NOSPLIT,$-8
MOVW 4(FP), R1
MOVW 8(FP), R2
SWI $0xa00003
+ MOVW R0, ret+12(FP)
RET
TEXT runtime·write(SB),NOSPLIT,$-4
@@ -47,14 +50,16 @@ TEXT runtime·write(SB),NOSPLIT,$-4
MOVW 4(FP), R1 // arg 2 - buf
MOVW 8(FP), R2 // arg 3 - nbyte
SWI $0xa00004 // sys_write
+ MOVW R0, ret+12(FP)
RET
// int32 lwp_create(void *context, uintptr flags, void *lwpid)
TEXT runtime·lwp_create(SB),NOSPLIT,$0
- MOVW context+0(FP), R0
+ MOVW ctxt+0(FP), R0
MOVW flags+4(FP), R1
MOVW lwpid+8(FP), R2
SWI $0xa00135 // sys__lwp_create
+ MOVW R0, ret+12(FP)
RET
TEXT runtime·osyield(SB),NOSPLIT,$0
@@ -67,16 +72,19 @@ TEXT runtime·lwp_park(SB),NOSPLIT,$0
MOVW 8(FP), R2 // arg 3 - hint
MOVW 12(FP), R3 // arg 4 - unparkhint
SWI $0xa001b2 // sys__lwp_park
+ MOVW R0, ret+16(FP)
RET
TEXT runtime·lwp_unpark(SB),NOSPLIT,$0
MOVW 0(FP), R0 // arg 1 - lwp
MOVW 4(FP), R1 // arg 2 - hint
SWI $0xa00141 // sys__lwp_unpark
+ MOVW R0, ret+8(FP)
RET
TEXT runtime·lwp_self(SB),NOSPLIT,$0
SWI $0xa00137 // sys__lwp_self
+ MOVW R0, ret+0(FP)
RET
TEXT runtime·lwp_tramp(SB),NOSPLIT,$0
@@ -153,9 +161,8 @@ TEXT runtime·nanotime(SB), NOSPLIT, $32
ADD.S R2, R0
ADC R4, R1
- MOVW 0(FP), R3
- MOVW R0, 0(R3)
- MOVW R1, 4(R3)
+ MOVW R0, ret_lo+0(FP)
+ MOVW R1, ret_hi+4(FP)
RET
TEXT runtime·getcontext(SB),NOSPLIT,$-4
@@ -249,6 +256,7 @@ TEXT runtime·mmap(SB),NOSPLIT,$12
ADD $4, R13 // pass arg 5 and arg 6 on stack
SWI $0xa000c5 // sys_mmap
SUB $4, R13
+ MOVW R0, ret+24(FP)
RET
TEXT runtime·munmap(SB),NOSPLIT,$0
@@ -287,12 +295,14 @@ TEXT runtime·sysctl(SB),NOSPLIT,$8
ADD $4, R13 // pass arg 5 and 6 on stack
SWI $0xa000ca // sys___sysctl
SUB $4, R13
+ MOVW R0, ret+24(FP)
RET
// int32 runtime·kqueue(void)
TEXT runtime·kqueue(SB),NOSPLIT,$0
SWI $0xa00158 // sys_kqueue
RSB.CS $0, R0
+ MOVW R0, ret+0(FP)
RET
// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout)
@@ -309,6 +319,7 @@ TEXT runtime·kevent(SB),NOSPLIT,$8
SWI $0xa001b3 // sys___kevent50
RSB.CS $0, R0
SUB $4, R13
+ MOVW R0, ret+24(FP)
RET
// void runtime·closeonexec(int32 fd)
diff --git a/src/pkg/runtime/sys_openbsd_386.s b/src/pkg/runtime/sys_openbsd_386.s
index a94c4e4a8f..12d9c5c6b4 100644
--- a/src/pkg/runtime/sys_openbsd_386.s
+++ b/src/pkg/runtime/sys_openbsd_386.s
@@ -7,7 +7,7 @@
//
#include "zasm_GOOS_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
#define CLOCK_MONOTONIC $3
@@ -30,21 +30,25 @@ TEXT runtime·exit1(SB),NOSPLIT,$8
TEXT runtime·open(SB),NOSPLIT,$-4
MOVL $5, AX
INT $0x80
+ MOVL AX, ret+12(FP)
RET
TEXT runtime·close(SB),NOSPLIT,$-4
MOVL $6, AX
INT $0x80
+ MOVL AX, ret+4(FP)
RET
TEXT runtime·read(SB),NOSPLIT,$-4
MOVL $3, AX
INT $0x80
+ MOVL AX, ret+12(FP)
RET
TEXT runtime·write(SB),NOSPLIT,$-4
MOVL $4, AX // sys_write
INT $0x80
+ MOVL AX, ret+12(FP)
RET
TEXT runtime·usleep(SB),NOSPLIT,$24
@@ -78,7 +82,7 @@ TEXT runtime·raise(SB),NOSPLIT,$12
RET
TEXT runtime·mmap(SB),NOSPLIT,$36
- LEAL arg0+0(FP), SI
+ LEAL addr+0(FP), SI
LEAL 4(SP), DI
CLD
MOVSL // arg 1 - addr
@@ -93,6 +97,7 @@ TEXT runtime·mmap(SB),NOSPLIT,$36
STOSL
MOVL $197, AX // sys_mmap
INT $0x80
+ MOVL AX, ret+24(FP)
RET
TEXT runtime·munmap(SB),NOSPLIT,$-4
@@ -151,9 +156,8 @@ TEXT runtime·nanotime(SB),NOSPLIT,$32
ADDL BX, AX
ADCL CX, DX // add high bits with carry
- MOVL ret+0(FP), DI
- MOVL AX, 0(DI)
- MOVL DX, 4(DI)
+ MOVL AX, ret_lo+0(FP)
+ MOVL DX, ret_hi+4(FP)
RET
TEXT runtime·sigaction(SB),NOSPLIT,$-4
@@ -168,7 +172,7 @@ TEXT runtime·sigprocmask(SB),NOSPLIT,$-4
INT $0x80
JAE 2(PC)
MOVL $0xf1, 0xf1 // crash
- MOVL AX, oset+0(FP)
+ MOVL AX, ret+8(FP)
RET
TEXT runtime·sigtramp(SB),NOSPLIT,$44
@@ -222,38 +226,36 @@ sigtramp_ret:
TEXT runtime·tfork(SB),NOSPLIT,$12
// Copy mp, gp and fn from the parent stack onto the child stack.
- MOVL params+4(FP), AX
+ MOVL param+0(FP), AX
MOVL 8(AX), CX // tf_stack
SUBL $16, CX
MOVL CX, 8(AX)
- MOVL mm+12(FP), SI
+ MOVL mm+8(FP), SI
MOVL SI, 0(CX)
- MOVL gg+16(FP), SI
+ MOVL gg+12(FP), SI
MOVL SI, 4(CX)
- MOVL fn+20(FP), SI
+ MOVL fn+16(FP), SI
MOVL SI, 8(CX)
MOVL $1234, 12(CX)
MOVL $0, 0(SP) // syscall gap
- MOVL params+4(FP), AX
+ MOVL param+0(FP), AX
MOVL AX, 4(SP) // arg 1 - param
- MOVL psize+8(FP), AX
+ MOVL psize+4(FP), AX
MOVL AX, 8(SP) // arg 2 - psize
MOVL $8, AX // sys___tfork
INT $0x80
// Return if tfork syscall failed.
- JCC 5(PC)
+ JCC 4(PC)
NEGL AX
- MOVL ret+0(FP), DX
- MOVL AX, 0(DX)
+ MOVL AX, ret+20(FP)
RET
// In parent, return.
CMPL AX, $0
- JEQ 4(PC)
- MOVL ret+0(FP), DX
- MOVL AX, 0(DX)
+ JEQ 3(PC)
+ MOVL AX, ret+20(FP)
RET
// Paranoia: check that SP is as we expect.
@@ -333,15 +335,17 @@ TEXT runtime·osyield(SB),NOSPLIT,$-4
TEXT runtime·thrsleep(SB),NOSPLIT,$-4
MOVL $94, AX // sys___thrsleep
INT $0x80
+ MOVL AX, ret+20(FP)
RET
TEXT runtime·thrwakeup(SB),NOSPLIT,$-4
MOVL $301, AX // sys___thrwakeup
INT $0x80
+ MOVL AX, ret+8(FP)
RET
TEXT runtime·sysctl(SB),NOSPLIT,$28
- LEAL arg0+0(FP), SI
+ LEAL mib+0(FP), SI
LEAL 4(SP), DI
CLD
MOVSL // arg 1 - name
@@ -352,10 +356,12 @@ TEXT runtime·sysctl(SB),NOSPLIT,$28
MOVSL // arg 6 - newlen
MOVL $202, AX // sys___sysctl
INT $0x80
- JCC 3(PC)
+ JCC 4(PC)
NEGL AX
+ MOVL AX, ret+24(FP)
RET
MOVL $0, AX
+ MOVL AX, ret+24(FP)
RET
// int32 runtime·kqueue(void);
@@ -364,6 +370,7 @@ TEXT runtime·kqueue(SB),NOSPLIT,$0
INT $0x80
JAE 2(PC)
NEGL AX
+ MOVL AX, ret+0(FP)
RET
// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout);
@@ -372,6 +379,7 @@ TEXT runtime·kevent(SB),NOSPLIT,$0
INT $0x80
JAE 2(PC)
NEGL AX
+ MOVL AX, ret+24(FP)
RET
// int32 runtime·closeonexec(int32 fd);
diff --git a/src/pkg/runtime/sys_openbsd_amd64.s b/src/pkg/runtime/sys_openbsd_amd64.s
index dac90eae10..4e9db23908 100644
--- a/src/pkg/runtime/sys_openbsd_amd64.s
+++ b/src/pkg/runtime/sys_openbsd_amd64.s
@@ -7,11 +7,11 @@
//
#include "zasm_GOOS_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
#define CLOCK_MONOTONIC $3
-// int64 tfork(void *param, uintptr psize, M *mp, G *gp, void (*fn)(void));
+// int32 tfork(void *param, uintptr psize, M *mp, G *gp, void (*fn)(void));
TEXT runtime·tfork(SB),NOSPLIT,$32
// Copy mp, gp and fn off parent stack for use by child.
@@ -25,13 +25,15 @@ TEXT runtime·tfork(SB),NOSPLIT,$32
SYSCALL
// Return if tfork syscall failed.
- JCC 3(PC)
+ JCC 4(PC)
NEGQ AX
+ MOVL AX, ret+40(FP)
RET
// In parent, return.
CMPL AX, $0
- JEQ 2(PC)
+ JEQ 3(PC)
+ MOVL AX, ret+40(FP)
RET
// Set FS to point at m->tls.
@@ -59,25 +61,27 @@ TEXT runtime·osyield(SB),NOSPLIT,$0
RET
TEXT runtime·thrsleep(SB),NOSPLIT,$0
- MOVQ 8(SP), DI // arg 1 - ident
- MOVL 16(SP), SI // arg 2 - clock_id
- MOVQ 24(SP), DX // arg 3 - tp
- MOVQ 32(SP), R10 // arg 4 - lock
- MOVQ 40(SP), R8 // arg 5 - abort
+ MOVQ ident+0(FP), DI // arg 1 - ident
+ MOVL clock_id+8(FP), SI // arg 2 - clock_id
+ MOVQ tsp+16(FP), DX // arg 3 - tp
+ MOVQ lock+24(FP), R10 // arg 4 - lock
+ MOVQ abort+32(FP), R8 // arg 5 - abort
MOVL $94, AX // sys___thrsleep
SYSCALL
+ MOVL AX, ret+40(FP)
RET
TEXT runtime·thrwakeup(SB),NOSPLIT,$0
- MOVQ 8(SP), DI // arg 1 - ident
- MOVL 16(SP), SI // arg 2 - n
+ MOVQ ident+0(FP), DI // arg 1 - ident
+ MOVL n+8(FP), SI // arg 2 - n
MOVL $301, AX // sys___thrwakeup
SYSCALL
+ MOVL AX, ret+16(FP)
RET
// Exit the entire program (like C exit)
TEXT runtime·exit(SB),NOSPLIT,$-8
- MOVL 8(SP), DI // arg 1 - exit status
+ MOVL code+0(FP), DI // arg 1 - exit status
MOVL $1, AX // sys_exit
SYSCALL
MOVL $0xf1, 0xf1 // crash
@@ -91,33 +95,37 @@ TEXT runtime·exit1(SB),NOSPLIT,$-8
RET
TEXT runtime·open(SB),NOSPLIT,$-8
- MOVQ 8(SP), DI // arg 1 pathname
- MOVL 16(SP), SI // arg 2 flags
- MOVL 20(SP), DX // arg 3 mode
+ MOVQ name+0(FP), DI // arg 1 pathname
+ MOVL mode+8(FP), SI // arg 2 flags
+ MOVL perm+12(FP), DX // arg 3 mode
MOVL $5, AX
SYSCALL
+ MOVL AX, ret+16(FP)
RET
TEXT runtime·close(SB),NOSPLIT,$-8
- MOVL 8(SP), DI // arg 1 fd
+ MOVL fd+0(FP), DI // arg 1 fd
MOVL $6, AX
SYSCALL
+ MOVL AX, ret+8(FP)
RET
TEXT runtime·read(SB),NOSPLIT,$-8
- MOVL 8(SP), DI // arg 1 fd
- MOVQ 16(SP), SI // arg 2 buf
- MOVL 24(SP), DX // arg 3 count
+ MOVL fd+0(FP), DI // arg 1 fd
+ MOVQ p+8(FP), SI // arg 2 buf
+ MOVL n+16(FP), DX // arg 3 count
MOVL $3, AX
SYSCALL
+ MOVL AX, ret+24(FP)
RET
TEXT runtime·write(SB),NOSPLIT,$-8
- MOVL 8(SP), DI // arg 1 - fd
- MOVQ 16(SP), SI // arg 2 - buf
- MOVL 24(SP), DX // arg 3 - nbyte
+ MOVQ fd+0(FP), DI // arg 1 - fd
+ MOVQ p+8(FP), SI // arg 2 - buf
+ MOVL n+16(FP), DX // arg 3 - nbyte
MOVL $4, AX // sys_write
SYSCALL
+ MOVL AX, ret+24(FP)
RET
TEXT runtime·usleep(SB),NOSPLIT,$16
@@ -146,9 +154,9 @@ TEXT runtime·raise(SB),NOSPLIT,$16
RET
TEXT runtime·setitimer(SB),NOSPLIT,$-8
- MOVL 8(SP), DI // arg 1 - which
- MOVQ 16(SP), SI // arg 2 - itv
- MOVQ 24(SP), DX // arg 3 - oitv
+ MOVL mode+0(FP), DI // arg 1 - which
+ MOVQ new+8(FP), SI // arg 2 - itv
+ MOVQ old+16(FP), DX // arg 3 - oitv
MOVL $69, AX // sys_setitimer
SYSCALL
RET
@@ -179,12 +187,13 @@ TEXT runtime·nanotime(SB),NOSPLIT,$24
// return nsec in AX
IMULQ $1000000000, AX
ADDQ DX, AX
+ MOVQ AX, ret+0(FP)
RET
TEXT runtime·sigaction(SB),NOSPLIT,$-8
- MOVL 8(SP), DI // arg 1 - signum
- MOVQ 16(SP), SI // arg 2 - nsa
- MOVQ 24(SP), DX // arg 3 - osa
+ MOVL sig+0(FP), DI // arg 1 - signum
+ MOVQ new+8(FP), SI // arg 2 - nsa
+ MOVQ old+16(FP), DX // arg 3 - osa
MOVL $46, AX
SYSCALL
JCC 2(PC)
@@ -192,13 +201,13 @@ TEXT runtime·sigaction(SB),NOSPLIT,$-8
RET
TEXT runtime·sigprocmask(SB),NOSPLIT,$0
- MOVL 8(SP), DI // arg 1 - how
- MOVL 12(SP), SI // arg 2 - set
+ MOVL mode+0(FP), DI // arg 1 - how
+ MOVL new+4(FP), SI // arg 2 - set
MOVL $48, AX // sys_sigprocmask
SYSCALL
JCC 2(PC)
MOVL $0xf1, 0xf1 // crash
- MOVL AX, oset+0(FP) // Return oset
+ MOVL AX, ret+8(FP)
RET
TEXT runtime·sigtramp(SB),NOSPLIT,$64
@@ -235,23 +244,24 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$64
RET
TEXT runtime·mmap(SB),NOSPLIT,$0
- MOVQ 8(SP), DI // arg 1 - addr
- MOVQ 16(SP), SI // arg 2 - len
- MOVL 24(SP), DX // arg 3 - prot
- MOVL 28(SP), R10 // arg 4 - flags
- MOVL 32(SP), R8 // arg 5 - fd
- MOVQ 36(SP), R9
+ MOVQ addr+0(FP), DI // arg 1 - addr
+ MOVQ n+8(FP), SI // arg 2 - len
+ MOVL prot+16(FP), DX // arg 3 - prot
+ MOVL flags+20(FP), R10 // arg 4 - flags
+ MOVL fd+24(FP), R8 // arg 5 - fd
+ MOVL off+28(FP), R9
SUBQ $16, SP
MOVQ R9, 8(SP) // arg 7 - offset (passed on stack)
MOVQ $0, R9 // arg 6 - pad
MOVL $197, AX
SYSCALL
ADDQ $16, SP
+ MOVQ AX, ret+32(FP)
RET
TEXT runtime·munmap(SB),NOSPLIT,$0
- MOVQ 8(SP), DI // arg 1 - addr
- MOVQ 16(SP), SI // arg 2 - len
+ MOVQ addr+0(FP), DI // arg 1 - addr
+ MOVQ n+8(FP), SI // arg 2 - len
MOVL $73, AX // sys_munmap
SYSCALL
JCC 2(PC)
@@ -260,8 +270,8 @@ TEXT runtime·munmap(SB),NOSPLIT,$0
TEXT runtime·madvise(SB),NOSPLIT,$0
MOVQ addr+0(FP), DI // arg 1 - addr
- MOVQ len+8(FP), SI // arg 2 - len
- MOVQ behav+16(FP), DX // arg 3 - behav
+ MOVQ n+8(FP), SI // arg 2 - len
+ MOVL flags+16(FP), DX // arg 3 - behav
MOVQ $75, AX // sys_madvise
SYSCALL
// ignore failure - maybe pages are locked
@@ -287,18 +297,20 @@ TEXT runtime·settls(SB),NOSPLIT,$0
RET
TEXT runtime·sysctl(SB),NOSPLIT,$0
- MOVQ 8(SP), DI // arg 1 - name
- MOVL 16(SP), SI // arg 2 - namelen
- MOVQ 24(SP), DX // arg 3 - oldp
- MOVQ 32(SP), R10 // arg 4 - oldlenp
- MOVQ 40(SP), R8 // arg 5 - newp
- MOVQ 48(SP), R9 // arg 6 - newlen
+ MOVQ mib+0(FP), DI // arg 1 - name
+ MOVL miblen+8(FP), SI // arg 2 - namelen
+ MOVQ out+16(FP), DX // arg 3 - oldp
+ MOVQ size+24(FP), R10 // arg 4 - oldlenp
+ MOVQ dst+32(FP), R8 // arg 5 - newp
+ MOVQ ndst+40(FP), R9 // arg 6 - newlen
MOVQ $202, AX // sys___sysctl
SYSCALL
- JCC 3(PC)
+ JCC 4(PC)
NEGQ AX
+ MOVL AX, ret+48(FP)
RET
MOVL $0, AX
+ MOVL AX, ret+48(FP)
RET
// int32 runtime·kqueue(void);
@@ -310,25 +322,27 @@ TEXT runtime·kqueue(SB),NOSPLIT,$0
SYSCALL
JCC 2(PC)
NEGQ AX
+ MOVL AX, ret+0(FP)
RET
// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout);
TEXT runtime·kevent(SB),NOSPLIT,$0
- MOVL 8(SP), DI
- MOVQ 16(SP), SI
- MOVL 24(SP), DX
- MOVQ 32(SP), R10
- MOVL 40(SP), R8
- MOVQ 48(SP), R9
+ MOVL fd+0(FP), DI
+ MOVQ ev1+8(FP), SI
+ MOVL nev1+16(FP), DX
+ MOVQ ev2+24(FP), R10
+ MOVL nev2+32(FP), R8
+ MOVQ ts+40(FP), R9
MOVL $72, AX
SYSCALL
JCC 2(PC)
NEGQ AX
+ MOVL AX, ret+48(FP)
RET
// void runtime·closeonexec(int32 fd);
TEXT runtime·closeonexec(SB),NOSPLIT,$0
- MOVL 8(SP), DI // fd
+ MOVL fd+0(FP), DI // fd
MOVQ $2, SI // F_SETFD
MOVQ $1, DX // FD_CLOEXEC
MOVL $92, AX // fcntl
diff --git a/src/pkg/runtime/sys_plan9_386.s b/src/pkg/runtime/sys_plan9_386.s
index 5e8c7420e2..7432981813 100644
--- a/src/pkg/runtime/sys_plan9_386.s
+++ b/src/pkg/runtime/sys_plan9_386.s
@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
#include "zasm_GOOS_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// setldt(int entry, int address, int limit)
TEXT runtime·setldt(SB),NOSPLIT,$0
@@ -12,31 +12,49 @@ TEXT runtime·setldt(SB),NOSPLIT,$0
TEXT runtime·open(SB),NOSPLIT,$0
MOVL $14, AX
INT $64
+ MOVL AX, ret+12(FP)
RET
TEXT runtime·pread(SB),NOSPLIT,$0
MOVL $50, AX
INT $64
+ MOVL AX, ret+20(FP)
RET
TEXT runtime·pwrite(SB),NOSPLIT,$0
MOVL $51, AX
INT $64
+ MOVL AX, ret+20(FP)
RET
-TEXT runtime·seek(SB),NOSPLIT,$0
+// int32 _seek(int64*, int32, int64, int32)
+TEXT _seek<>(SB),NOSPLIT,$0
MOVL $39, AX
INT $64
- CMPL AX, $-1
- JNE 4(PC)
- MOVL a+0(FP), CX
- MOVL AX, 0(CX)
- MOVL AX, 4(CX)
+ RET
+
+TEXT runtime·seek(SB),NOSPLIT,$24
+ LEAL ret+16(FP), AX
+ MOVL fd+0(FP), BX
+ MOVL offset_lo+4(FP), CX
+ MOVL offset_hi+8(FP), DX
+ MOVL whence+12(FP), SI
+ MOVL AX, 0(SP)
+ MOVL BX, 4(SP)
+ MOVL CX, 8(SP)
+ MOVL DX, 12(SP)
+ MOVL SI, 16(SP)
+ CALL _seek<>(SB)
+ CMPL AX, $0
+ JGE 3(PC)
+ MOVL $-1, ret_lo+16(FP)
+ MOVL $-1, ret_hi+20(FP)
RET
TEXT runtime·close(SB),NOSPLIT,$0
MOVL $4, AX
INT $64
+ MOVL AX, ret+4(FP)
RET
TEXT runtime·exits(SB),NOSPLIT,$0
@@ -47,50 +65,75 @@ TEXT runtime·exits(SB),NOSPLIT,$0
TEXT runtime·brk_(SB),NOSPLIT,$0
MOVL $24, AX
INT $64
+ MOVL AX, ret+4(FP)
RET
TEXT runtime·sleep(SB),NOSPLIT,$0
MOVL $17, AX
INT $64
+ MOVL AX, ret+4(FP)
RET
TEXT runtime·plan9_semacquire(SB),NOSPLIT,$0
MOVL $37, AX
INT $64
+ MOVL AX, ret+8(FP)
RET
TEXT runtime·plan9_tsemacquire(SB),NOSPLIT,$0
MOVL $52, AX
INT $64
+ MOVL AX, ret+8(FP)
RET
-TEXT runtime·nsec(SB),NOSPLIT,$0
+TEXT nsec<>(SB),NOSPLIT,$0
MOVL $53, AX
INT $64
- CMPL AX, $-1
- JNE 4(PC)
- MOVL a+0(FP), CX
- MOVL AX, 0(CX)
- MOVL AX, 4(CX)
+ RET
+
+TEXT runtime·nsec(SB),NOSPLIT,$8
+ LEAL ret+4(FP), AX
+ MOVL AX, 0(SP)
+ CALL nsec<>(SB)
+ CMPL AX, $0
+ JGE 3(PC)
+ MOVL $-1, ret_lo+4(FP)
+ MOVL $-1, ret_hi+8(FP)
+ RET
+
+// func now() (sec int64, nsec int32)
+TEXT time·now(SB),NOSPLIT,$8-12
+ CALL runtime·nanotime(SB)
+ MOVL 0(SP), AX
+ MOVL 4(SP), DX
+
+ MOVL $1000000000, CX
+ DIVL CX
+ MOVL AX, sec+0(FP)
+ MOVL $0, sec+4(FP)
+ MOVL DX, nsec+8(FP)
RET
TEXT runtime·notify(SB),NOSPLIT,$0
MOVL $28, AX
INT $64
+ MOVL AX, ret+4(FP)
RET
TEXT runtime·noted(SB),NOSPLIT,$0
MOVL $29, AX
INT $64
+ MOVL AX, ret+4(FP)
RET
TEXT runtime·plan9_semrelease(SB),NOSPLIT,$0
MOVL $38, AX
INT $64
+ MOVL AX, ret+8(FP)
RET
TEXT runtime·rfork(SB),NOSPLIT,$0
- MOVL $19, AX // rfork
+ MOVL $19, AX // rfork
MOVL stack+8(SP), CX
MOVL mm+12(SP), BX // m
MOVL gg+16(SP), DX // g
@@ -99,7 +142,8 @@ TEXT runtime·rfork(SB),NOSPLIT,$0
// In parent, return.
CMPL AX, $0
- JEQ 2(PC)
+ JEQ 3(PC)
+ MOVL AX, ret+20(FP)
RET
// set SP to be on the new child stack
@@ -127,6 +171,7 @@ TEXT runtime·rfork(SB),NOSPLIT,$0
CALL SI // fn()
CALL runtime·exit(SB)
+ MOVL AX, ret+20(FP)
RET
// void sigtramp(void *ureg, int8 *note)
@@ -151,11 +196,11 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0
MOVL BP, SP
// make room for args and g
- SUBL $16, SP
+ SUBL $24, SP
// save g
MOVL g(AX), BP
- MOVL BP, 12(SP)
+ MOVL BP, 20(SP)
// g = m->gsignal
MOVL m_gsignal(BX), DI
@@ -167,10 +212,11 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0
MOVL BP, 8(SP)
CALL runtime·sighandler(SB)
+ MOVL 12(SP), AX
// restore g
get_tls(BX)
- MOVL 12(SP), BP
+ MOVL 20(SP), BP
MOVL BP, g(BX)
// call noted(AX)
@@ -184,28 +230,29 @@ TEXT runtime·setfpmasks(SB),NOSPLIT,$0
#define ERRMAX 128 /* from os_plan9.h */
-// func errstr() String
+// void errstr(int8 *buf, int32 len)
+TEXT errstr<>(SB),NOSPLIT,$0
+ MOVL $41, AX
+ INT $64
+ RET
+
+// func errstr() string
// Only used by package syscall.
// Grab error string due to a syscall made
// in entersyscall mode, without going
// through the allocator (issue 4994).
// See ../syscall/asm_plan9_386.s:/·Syscall/
-TEXT runtime·errstr(SB),NOSPLIT,$0
+TEXT runtime·errstr(SB),NOSPLIT,$8-8
get_tls(AX)
MOVL g(AX), BX
MOVL g_m(BX), BX
MOVL m_errstr(BX), CX
- MOVL CX, 4(SP)
- MOVL $ERRMAX, 8(SP)
- MOVL $41, AX
- INT $64
-
- // syscall requires caller-save
- MOVL 4(SP), CX
-
- // push the argument
- PUSHL CX
+ MOVL CX, 0(SP)
+ MOVL $ERRMAX, 4(SP)
+ CALL errstr<>(SB)
CALL runtime·findnull(SB)
- POPL CX
- MOVL AX, 8(SP)
+ MOVL 4(SP), AX
+ MOVL AX, ret_len+4(FP)
+ MOVL 0(SP), AX
+ MOVL AX, ret_base+0(FP)
RET
diff --git a/src/pkg/runtime/sys_plan9_amd64.s b/src/pkg/runtime/sys_plan9_amd64.s
index 8f4a5c05ec..954c0c27bb 100644
--- a/src/pkg/runtime/sys_plan9_amd64.s
+++ b/src/pkg/runtime/sys_plan9_amd64.s
@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
#include "zasm_GOOS_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// setldt(int entry, int address, int limit)
TEXT runtime·setldt(SB),NOSPLIT,$0
@@ -12,16 +12,19 @@ TEXT runtime·setldt(SB),NOSPLIT,$0
TEXT runtime·open(SB),NOSPLIT,$0
MOVQ $14, BP
SYSCALL
+ MOVL AX, ret+16(FP)
RET
TEXT runtime·pread(SB),NOSPLIT,$0
MOVQ $50, BP
SYSCALL
+ MOVL AX, ret+32(FP)
RET
TEXT runtime·pwrite(SB),NOSPLIT,$0
MOVQ $51, BP
SYSCALL
+ MOVL AX, ret+32(FP)
RET
// int32 _seek(int64*, int32, int64, int32)
@@ -31,25 +34,26 @@ TEXT _seek<>(SB),NOSPLIT,$0
RET
// int64 seek(int32, int64, int32)
-TEXT runtime·seek(SB),NOSPLIT,$56
- LEAQ new+48(SP), CX
- MOVQ CX, 0(SP)
- MOVQ fd+0(FP), CX
- MOVQ CX, 8(SP)
- MOVQ off+8(FP), CX
+// Convenience wrapper around _seek, the actual system call.
+TEXT runtime·seek(SB),NOSPLIT,$32
+ LEAQ ret+24(FP), AX
+ MOVL fd+0(FP), BX
+ MOVQ offset+8(FP), CX
+ MOVL whence+16(FP), DX
+ MOVQ AX, 0(SP)
+ MOVL BX, 8(SP)
MOVQ CX, 16(SP)
- MOVQ whence+16(FP), CX
- MOVQ CX, 24(SP)
+ MOVL DX, 24(SP)
CALL _seek<>(SB)
CMPL AX, $0
JGE 2(PC)
- MOVQ $-1, new+48(SP)
- MOVQ new+48(SP), AX
+ MOVQ $-1, ret+24(FP)
RET
TEXT runtime·close(SB),NOSPLIT,$0
MOVQ $4, BP
SYSCALL
+ MOVL AX, ret+8(FP)
RET
TEXT runtime·exits(SB),NOSPLIT,$0
@@ -60,41 +64,69 @@ TEXT runtime·exits(SB),NOSPLIT,$0
TEXT runtime·brk_(SB),NOSPLIT,$0
MOVQ $24, BP
SYSCALL
+ MOVQ AX, ret+8(FP)
RET
TEXT runtime·sleep(SB),NOSPLIT,$0
MOVQ $17, BP
SYSCALL
+ MOVL AX, ret+8(FP)
RET
TEXT runtime·plan9_semacquire(SB),NOSPLIT,$0
MOVQ $37, BP
SYSCALL
+ MOVL AX, ret+16(FP)
RET
TEXT runtime·plan9_tsemacquire(SB),NOSPLIT,$0
MOVQ $52, BP
SYSCALL
+ MOVL AX, ret+16(FP)
RET
TEXT runtime·nsec(SB),NOSPLIT,$0
MOVQ $53, BP
SYSCALL
+ MOVQ AX, ret+8(FP)
+ RET
+
+// func now() (sec int64, nsec int32)
+TEXT time·now(SB),NOSPLIT,$8-12
+ CALL runtime·nanotime(SB)
+ MOVQ 0(SP), AX
+
+ // generated code for
+ // func f(x uint64) (uint64, uint64) { return x/1000000000, x%100000000 }
+ // adapted to reduce duplication
+ MOVQ AX, CX
+ MOVQ $1360296554856532783, AX
+ MULQ CX
+ ADDQ CX, DX
+ RCRQ $1, DX
+ SHRQ $29, DX
+ MOVQ DX, sec+0(FP)
+ IMULQ $1000000000, DX
+ SUBQ DX, CX
+ MOVL CX, nsec+8(FP)
RET
TEXT runtime·notify(SB),NOSPLIT,$0
MOVQ $28, BP
SYSCALL
+ MOVL AX, ret+8(FP)
RET
TEXT runtime·noted(SB),NOSPLIT,$0
MOVQ $29, BP
SYSCALL
+ MOVL AX, ret+8(FP)
RET
TEXT runtime·plan9_semrelease(SB),NOSPLIT,$0
MOVQ $38, BP
SYSCALL
+ MOVL AX, ret+16(FP)
RET
TEXT runtime·rfork(SB),NOSPLIT,$0
@@ -103,7 +135,8 @@ TEXT runtime·rfork(SB),NOSPLIT,$0
// In parent, return.
CMPQ AX, $0
- JEQ 2(PC)
+ JEQ 3(PC)
+ MOVL AX, ret+40(FP)
RET
// In child on forked stack.
@@ -132,6 +165,7 @@ TEXT runtime·rfork(SB),NOSPLIT,$0
CALL SI // fn()
CALL runtime·exit(SB)
+ MOVL AX, ret+40(FP)
RET
// This is needed by asm_amd64.s
@@ -160,11 +194,11 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0
MOVQ BP, SP
// make room for args and g
- SUBQ $32, SP
+ SUBQ $40, SP
// save g
MOVQ g(AX), BP
- MOVQ BP, 24(SP)
+ MOVQ BP, 32(SP)
// g = m->gsignal
MOVQ R10, g(AX)
@@ -175,10 +209,11 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0
MOVQ BP, 16(SP)
CALL runtime·sighandler(SB)
+ MOVL 24(SP), AX
// restore g
get_tls(BX)
- MOVQ 24(SP), R10
+ MOVQ 32(SP), R10
MOVQ R10, g(BX)
// call noted(AX)
@@ -197,28 +232,29 @@ TEXT runtime·setfpmasks(SB),NOSPLIT,$8
#define ERRMAX 128 /* from os_plan9.h */
-// func errstr() String
+// void errstr(int8 *buf, int32 len)
+TEXT errstr<>(SB),NOSPLIT,$0
+ MOVQ $41, BP
+ SYSCALL
+ RET
+
+// func errstr() string
// Only used by package syscall.
// Grab error string due to a syscall made
// in entersyscall mode, without going
// through the allocator (issue 4994).
-// See ../syscall/asm_plan9_386.s:/·Syscall/
-TEXT runtime·errstr(SB),NOSPLIT,$0
+// See ../syscall/asm_plan9_amd64.s:/·Syscall/
+TEXT runtime·errstr(SB),NOSPLIT,$16-16
get_tls(AX)
MOVQ g(AX), BX
MOVQ g_m(BX), BX
MOVQ m_errstr(BX), CX
- MOVQ CX, 8(SP)
- MOVQ $ERRMAX, 16(SP)
- MOVQ $41, BP
- SYSCALL
-
- // syscall requires caller-save
- MOVQ 8(SP), CX
-
- // push the argument
- PUSHQ CX
+ MOVQ CX, 0(SP)
+ MOVQ $ERRMAX, 8(SP)
+ CALL errstr<>(SB)
CALL runtime·findnull(SB)
- POPQ CX
- MOVQ AX, 16(SP)
+ MOVQ 8(SP), AX
+ MOVQ AX, ret_len+8(FP)
+ MOVQ 0(SP), AX
+ MOVQ AX, ret_base+0(FP)
RET
diff --git a/src/pkg/runtime/sys_solaris_amd64.s b/src/pkg/runtime/sys_solaris_amd64.s
index 1b18c8d9eb..093315c4a4 100644
--- a/src/pkg/runtime/sys_solaris_amd64.s
+++ b/src/pkg/runtime/sys_solaris_amd64.s
@@ -7,7 +7,7 @@
//
#include "zasm_GOOS_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// This is needed by asm_amd64.s
TEXT runtime·settls(SB),NOSPLIT,$8
@@ -18,6 +18,7 @@ TEXT runtime·settls(SB),NOSPLIT,$8
// Set the TLS errno pointer in M.
//
// Called using runtime·asmcgocall from os_solaris.c:/minit.
+// NOT USING GO CALLING CONVENTION.
TEXT runtime·miniterrno(SB),NOSPLIT,$0
// asmcgocall will put first argument into DI.
CALL DI // SysV ABI so returns in AX
@@ -33,6 +34,7 @@ TEXT runtime·miniterrno(SB),NOSPLIT,$0
// runtime·nanotime stack.
//
// Called using runtime·sysvicall6 from os_solaris.c:/nanotime.
+// NOT USING GO CALLING CONVENTION.
TEXT runtime·nanotime1(SB),NOSPLIT,$0
// need space for the timespec argument.
SUBQ $64, SP // 16 bytes will do, but who knows in the future?
@@ -47,6 +49,7 @@ TEXT runtime·nanotime1(SB),NOSPLIT,$0
RET
// pipe(3c) wrapper that returns fds in AX, DX.
+// NOT USING GO CALLING CONVENTION.
TEXT runtime·pipe1(SB),NOSPLIT,$0
SUBQ $16, SP // 8 bytes will do, but stack has to be 16-byte alligned
MOVQ SP, DI
@@ -66,6 +69,7 @@ TEXT runtime·pipe1(SB),NOSPLIT,$0
// section 3.2.3.
//
// Called by runtime·asmcgocall or runtime·cgocall.
+// NOT USING GO CALLING CONVENTION.
TEXT runtime·asmsysvicall6(SB),NOSPLIT,$0
// asmcgocall will put first argument into DI.
PUSHQ DI // save for later
@@ -137,6 +141,7 @@ TEXT runtime·tstart_sysvicall(SB),NOSPLIT,$0
CALL runtime·mstart(SB)
XORL AX, AX // return 0 == success
+ MOVL AX, ret+8(FP)
RET
// Careful, this is called by __sighndlr, a libc function. We must preserve
@@ -274,7 +279,7 @@ exit:
// Called from runtime·usleep (Go). Can be called on Go stack, on OS stack,
// can also be called in cgo callback path without a g->m.
TEXT runtime·usleep1(SB),NOSPLIT,$0
- MOVL us+0(FP), DI
+ MOVL usec+0(FP), DI
MOVQ $runtime·usleep2(SB), AX // to hide from 6l
// Execute call on m->g0.
@@ -322,3 +327,23 @@ TEXT runtime·osyield1(SB),NOSPLIT,$0
MOVQ libc·sched_yield(SB), AX
CALL AX
RET
+
+// func now() (sec int64, nsec int32)
+TEXT time·now(SB),NOSPLIT,$8-12
+ CALL runtime·nanotime(SB)
+ MOVQ 0(SP), AX
+
+ // generated code for
+ // func f(x uint64) (uint64, uint64) { return x/1000000000, x%100000000 }
+ // adapted to reduce duplication
+ MOVQ AX, CX
+ MOVQ $1360296554856532783, AX
+ MULQ CX
+ ADDQ CX, DX
+ RCRQ $1, DX
+ SHRQ $29, DX
+ MOVQ DX, sec+0(FP)
+ IMULQ $1000000000, DX
+ SUBQ DX, CX
+ MOVL CX, nsec+8(FP)
+ RET
diff --git a/src/pkg/runtime/sys_windows_386.s b/src/pkg/runtime/sys_windows_386.s
index f2c2a4128c..a9e096f018 100644
--- a/src/pkg/runtime/sys_windows_386.s
+++ b/src/pkg/runtime/sys_windows_386.s
@@ -3,11 +3,11 @@
// license that can be found in the LICENSE file.
#include "zasm_GOOS_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// void runtime·asmstdcall(void *c);
TEXT runtime·asmstdcall(SB),NOSPLIT,$0
- MOVL c+0(FP), BX
+ MOVL fn+0(FP), BX
// SetLastError(0).
MOVL $0, 0x34(FS)
@@ -29,7 +29,7 @@ TEXT runtime·asmstdcall(SB),NOSPLIT,$0
MOVL BP, SP
// Return result.
- MOVL c+0(FP), BX
+ MOVL fn+0(FP), BX
MOVL AX, libcall_r1(BX)
MOVL DX, libcall_r2(BX)
@@ -62,6 +62,7 @@ TEXT runtime·badsignal2(SB),NOSPLIT,$24
// faster get/set last error
TEXT runtime·getlasterror(SB),NOSPLIT,$0
MOVL 0x34(FS), AX
+ MOVL AX, ret+0(FP)
RET
TEXT runtime·setlasterror(SB),NOSPLIT,$0
@@ -75,10 +76,10 @@ TEXT runtime·setlasterror(SB),NOSPLIT,$0
// Return 0 for 'not handled', -1 for handled.
TEXT runtime·sigtramp(SB),NOSPLIT,$0-0
MOVL ptrs+0(FP), CX
- SUBL $28, SP
+ SUBL $32, SP
// save callee-saved registers
- MOVL BX, 12(SP)
+ MOVL BX, 28(SP)
MOVL BP, 16(SP)
MOVL SI, 20(SP)
MOVL DI, 24(SP)
@@ -102,15 +103,16 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0-0
MOVL DX, 8(SP)
CALL runtime·sighandler(SB)
// AX is set to report result back to Windows
+ MOVL 12(SP), AX
done:
// restore callee-saved registers
MOVL 24(SP), DI
MOVL 20(SP), SI
MOVL 16(SP), BP
- MOVL 12(SP), BX
+ MOVL 28(SP), BX
- ADDL $28, SP
+ ADDL $32, SP
// RET 4 (return and pop 4 bytes parameters)
BYTE $0xC2; WORD $4
RET // unreached; make assembler happy
@@ -301,7 +303,7 @@ TEXT runtime·setldt(SB),NOSPLIT,$0
// Sleep duration is in 100ns units.
TEXT runtime·usleep1(SB),NOSPLIT,$0
- MOVL duration+0(FP), BX
+ MOVL usec+0(FP), BX
MOVL $runtime·usleep2(SB), AX // to hide from 8l
// Execute call on m->g0 stack, in case we are not actually
@@ -323,7 +325,7 @@ TEXT runtime·usleep1(SB),NOSPLIT,$0
MOVL SI, m_libcallg(BP)
// sp must be the last, because once async cpu profiler finds
// all three values to be non-zero, it will use them
- LEAL 4(SP), SI
+ LEAL usec+0(FP), SI
MOVL SI, m_libcallsp(BP)
MOVL m_g0(BP), SI
@@ -363,3 +365,16 @@ TEXT runtime·usleep2(SB),NOSPLIT,$20
CALL AX
MOVL BP, SP
RET
+
+// func now() (sec int64, nsec int32)
+TEXT time·now(SB),NOSPLIT,$8-12
+ CALL runtime·unixnano(SB)
+ MOVL 0(SP), AX
+ MOVL 4(SP), DX
+
+ MOVL $1000000000, CX
+ DIVL CX
+ MOVL AX, sec+0(FP)
+ MOVL $0, sec+4(FP)
+ MOVL DX, nsec+8(FP)
+ RET
diff --git a/src/pkg/runtime/sys_windows_amd64.s b/src/pkg/runtime/sys_windows_amd64.s
index 6dc13e293d..21f73daf09 100644
--- a/src/pkg/runtime/sys_windows_amd64.s
+++ b/src/pkg/runtime/sys_windows_amd64.s
@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
#include "zasm_GOOS_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// maxargs should be divisible by 2, as Windows stack
// must be kept 16-byte aligned on syscall entry.
@@ -87,6 +87,7 @@ TEXT runtime·badsignal2(SB),NOSPLIT,$48
TEXT runtime·getlasterror(SB),NOSPLIT,$0
MOVQ 0x30(GS), AX
MOVL 0x68(AX), AX
+ MOVL AX, ret+0(FP)
RET
TEXT runtime·setlasterror(SB),NOSPLIT,$0
@@ -105,7 +106,7 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0-0
// DI SI BP BX R12 R13 R14 R15 registers and DF flag are preserved
// as required by windows callback convention.
PUSHFQ
- SUBQ $88, SP
+ SUBQ $96, SP
MOVQ DI, 80(SP)
MOVQ SI, 72(SP)
MOVQ BP, 64(SP)
@@ -113,7 +114,7 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0-0
MOVQ R12, 48(SP)
MOVQ R13, 40(SP)
MOVQ R14, 32(SP)
- MOVQ R15, 24(SP)
+ MOVQ R15, 88(SP)
MOVQ 0(CX), BX // ExceptionRecord*
MOVQ 8(CX), CX // Context*
@@ -134,10 +135,11 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0-0
MOVQ DX, 16(SP)
CALL runtime·sighandler(SB)
// AX is set to report result back to Windows
+ MOVL 24(SP), AX
done:
// restore registers as required for windows callback
- MOVQ 24(SP), R15
+ MOVQ 88(SP), R15
MOVQ 32(SP), R14
MOVQ 40(SP), R13
MOVQ 48(SP), R12
@@ -145,7 +147,7 @@ done:
MOVQ 64(SP), BP
MOVQ 72(SP), SI
MOVQ 80(SP), DI
- ADDQ $88, SP
+ ADDQ $96, SP
POPFQ
RET
@@ -228,7 +230,8 @@ TEXT runtime·callbackasm1(SB),NOSPLIT,$0
ADDQ $8, SP
// determine index into runtime·cbctxts table
- SUBQ $runtime·callbackasm(SB), AX
+ MOVQ $runtime·callbackasm(SB), DX
+ SUBQ DX, AX
MOVQ $0, DX
MOVQ $5, CX // divide by 5 because each call instruction in runtime·callbacks is 5 bytes long
DIVL CX,
@@ -322,7 +325,7 @@ TEXT runtime·settls(SB),NOSPLIT,$0
// Sleep duration is in 100ns units.
TEXT runtime·usleep1(SB),NOSPLIT,$0
- MOVL duration+0(FP), BX
+ MOVL usec+0(FP), BX
MOVQ $runtime·usleep2(SB), AX // to hide from 6l
// Execute call on m->g0 stack, in case we are not actually
@@ -344,7 +347,7 @@ TEXT runtime·usleep1(SB),NOSPLIT,$0
MOVQ R12, m_libcallg(R13)
// sp must be the last, because once async cpu profiler finds
// all three values to be non-zero, it will use them
- LEAQ 8(SP), R12
+ LEAQ usec+0(FP), R12
MOVQ R12, m_libcallsp(R13)
MOVQ m_g0(R13), R14
@@ -381,3 +384,24 @@ TEXT runtime·usleep2(SB),NOSPLIT,$16
CALL AX
MOVQ 8(SP), SP
RET
+
+// func now() (sec int64, nsec int32)
+TEXT time·now(SB),NOSPLIT,$8-12
+ CALL runtime·unixnano(SB)
+ MOVQ 0(SP), AX
+
+ // generated code for
+ // func f(x uint64) (uint64, uint64) { return x/1000000000, x%100000000 }
+ // adapted to reduce duplication
+ MOVQ AX, CX
+ MOVQ $1360296554856532783, AX
+ MULQ CX
+ ADDQ CX, DX
+ RCRQ $1, DX
+ SHRQ $29, DX
+ MOVQ DX, sec+0(FP)
+ IMULQ $1000000000, DX
+ SUBQ DX, CX
+ MOVL CX, nsec+8(FP)
+ RET
+
diff --git a/src/pkg/runtime/syscall_solaris.c b/src/pkg/runtime/syscall_solaris.c
new file mode 100644
index 0000000000..13ac31bde2
--- /dev/null
+++ b/src/pkg/runtime/syscall_solaris.c
@@ -0,0 +1,23 @@
+// 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.
+
+#pragma dynimport libc·chdir chdir "libc.so"
+#pragma dynimport libc·chroot chroot "libc.so"
+#pragma dynimport libc·close close "libc.so"
+#pragma dynimport libc·dlclose dlclose "libc.so"
+#pragma dynimport libc·dlopen dlopen "libc.so"
+#pragma dynimport libc·dlsym dlsym "libc.so"
+#pragma dynimport libc·execve execve "libc.so"
+#pragma dynimport libc·fcntl fcntl "libc.so"
+#pragma dynimport libc·gethostname gethostname "libc.so"
+#pragma dynimport libc·ioctl ioctl "libc.so"
+#pragma dynimport libc·pipe pipe "libc.so"
+#pragma dynimport libc·setgid setgid "libc.so"
+#pragma dynimport libc·setgroups setgroups "libc.so"
+#pragma dynimport libc·setsid setsid "libc.so"
+#pragma dynimport libc·setuid setuid "libc.so"
+#pragma dynimport libc·setpgid setsid "libc.so"
+#pragma dynimport libc·syscall syscall "libc.so"
+#pragma dynimport libc·forkx forkx "libc.so"
+#pragma dynimport libc·wait4 wait4 "libc.so"
diff --git a/src/pkg/runtime/syscall_solaris.go b/src/pkg/runtime/syscall_solaris.go
new file mode 100644
index 0000000000..d0a3fc8dd0
--- /dev/null
+++ b/src/pkg/runtime/syscall_solaris.go
@@ -0,0 +1,322 @@
+// 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"
+
+var (
+ libc_chdir,
+ libc_chroot,
+ libc_close,
+ libc_dlopen,
+ libc_dlclose,
+ libc_dlsym,
+ libc_execve,
+ libc_exit,
+ libc_fcntl,
+ libc_forkx,
+ libc_gethostname,
+ libc_ioctl,
+ libc_pipe,
+ libc_setgid,
+ libc_setgroups,
+ libc_setsid,
+ libc_setuid,
+ libc_setpgid,
+ libc_syscall,
+ libc_wait4,
+ libc_write,
+ pipe1 libcFunc
+)
+
+//go:nosplit
+func syscall_sysvicall6(fn, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) {
+ call := libcall{
+ fn: unsafe.Pointer(fn),
+ n: nargs,
+ args: unsafe.Pointer(&a1),
+ }
+ entersyscallblock()
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&call))
+ exitsyscall()
+ return call.r1, call.r2, call.err
+}
+
+//go:nosplit
+func syscall_rawsysvicall6(fn, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) {
+ call := libcall{
+ fn: unsafe.Pointer(fn),
+ n: nargs,
+ args: unsafe.Pointer(&a1),
+ }
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&call))
+ return call.r1, call.r2, call.err
+}
+
+// TODO(aram): Once we remove all instances of C calling sysvicallN, make
+// sysvicallN return errors and replace the body of the following functions
+// with calls to sysvicallN.
+
+//go:nosplit
+func syscall_chdir(path uintptr) (err uintptr) {
+ call := libcall{
+ fn: unsafe.Pointer(&libc_chdir),
+ n: 1,
+ args: unsafe.Pointer(&path),
+ }
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&call))
+ return call.err
+}
+
+//go:nosplit
+func syscall_chroot(path uintptr) (err uintptr) {
+ call := libcall{
+ fn: unsafe.Pointer(&libc_chroot),
+ n: 1,
+ args: unsafe.Pointer(&path),
+ }
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&call))
+ return call.err
+}
+
+// like close, but must not split stack, for forkx.
+//go:nosplit
+func syscall_close(fd int32) int32 {
+ return int32(sysvicall1(&libc_close, uintptr(fd)))
+}
+
+func syscall_dlopen(name *byte, mode uintptr) (handle uintptr, err uintptr) {
+ call := libcall{
+ fn: unsafe.Pointer(&libc_dlopen),
+ n: 2,
+ args: unsafe.Pointer(&name),
+ }
+ entersyscallblock()
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&call))
+ exitsyscall()
+ if call.r1 == 0 {
+ return call.r1, call.err
+ }
+ return call.r1, 0
+}
+
+func syscall_dlclose(handle uintptr) (err uintptr) {
+ call := libcall{
+ fn: unsafe.Pointer(&libc_dlclose),
+ n: 1,
+ args: unsafe.Pointer(&handle),
+ }
+ entersyscallblock()
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&call))
+ exitsyscall()
+ return call.r1
+}
+
+func syscall_dlsym(handle uintptr, name *byte) (proc uintptr, err uintptr) {
+ call := libcall{
+ fn: unsafe.Pointer(&libc_dlsym),
+ n: 2,
+ args: unsafe.Pointer(&handle),
+ }
+ entersyscallblock()
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&call))
+ exitsyscall()
+ if call.r1 == 0 {
+ return call.r1, call.err
+ }
+ return call.r1, 0
+}
+
+//go:nosplit
+func syscall_execve(path, argv, envp uintptr) (err uintptr) {
+ call := libcall{
+ fn: unsafe.Pointer(&libc_execve),
+ n: 3,
+ args: unsafe.Pointer(&path),
+ }
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&call))
+ return call.err
+}
+
+// like exit, but must not split stack, for forkx.
+//go:nosplit
+func syscall_exit(code uintptr) {
+ sysvicall1(&libc_exit, code)
+}
+
+//go:nosplit
+func syscall_fcntl(fd, cmd, arg uintptr) (val, err uintptr) {
+ call := libcall{
+ fn: unsafe.Pointer(&libc_fcntl),
+ n: 3,
+ args: unsafe.Pointer(&fd),
+ }
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&call))
+ return call.r1, call.err
+}
+
+//go:nosplit
+func syscall_forkx(flags uintptr) (pid uintptr, err uintptr) {
+ call := libcall{
+ fn: unsafe.Pointer(&libc_forkx),
+ n: 1,
+ args: unsafe.Pointer(&flags),
+ }
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&call))
+ return call.r1, call.err
+}
+
+func syscall_gethostname() (name string, err uintptr) {
+ cname := new([_MAXHOSTNAMELEN]byte)
+ var args = [2]uintptr{uintptr(unsafe.Pointer(&cname[0])), _MAXHOSTNAMELEN}
+ call := libcall{
+ fn: unsafe.Pointer(&libc_gethostname),
+ n: 2,
+ args: unsafe.Pointer(&args[0]),
+ }
+ entersyscallblock()
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&call))
+ exitsyscall()
+ if call.r1 != 0 {
+ return "", call.err
+ }
+ cname[_MAXHOSTNAMELEN-1] = 0
+ return gostringnocopy(&cname[0]), 0
+}
+
+//go:nosplit
+func syscall_ioctl(fd, req, arg uintptr) (err uintptr) {
+ call := libcall{
+ fn: unsafe.Pointer(&libc_ioctl),
+ n: 3,
+ args: unsafe.Pointer(&fd),
+ }
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&call))
+ return call.err
+}
+
+func syscall_pipe() (r, w, err uintptr) {
+ call := libcall{
+ fn: unsafe.Pointer(&pipe1),
+ n: 0,
+ args: unsafe.Pointer(&pipe1), // it's unused but must be non-nil, otherwise crashes
+ }
+ entersyscallblock()
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&call))
+ exitsyscall()
+ return call.r1, call.r2, call.err
+}
+
+// This is syscall.RawSyscall, it exists to satisfy some build dependency,
+// but it doesn't work correctly.
+//
+// DO NOT USE!
+//
+// TODO(aram): make this panic once we stop calling fcntl(2) in net using it.
+func syscall_rawsyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
+ call := libcall{
+ fn: unsafe.Pointer(&libc_syscall),
+ n: 4,
+ args: unsafe.Pointer(&trap),
+ }
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&call))
+ return call.r1, call.r2, call.err
+}
+
+//go:nosplit
+func syscall_setgid(gid uintptr) (err uintptr) {
+ call := libcall{
+ fn: unsafe.Pointer(&libc_setgid),
+ n: 1,
+ args: unsafe.Pointer(&gid),
+ }
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&call))
+ return call.err
+}
+
+//go:nosplit
+func syscall_setgroups(ngid, gid uintptr) (err uintptr) {
+ call := libcall{
+ fn: unsafe.Pointer(&libc_setgroups),
+ n: 2,
+ args: unsafe.Pointer(&ngid),
+ }
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&call))
+ return call.err
+}
+
+//go:nosplit
+func syscall_setsid() (pid, err uintptr) {
+ call := libcall{
+ fn: unsafe.Pointer(&libc_setsid),
+ n: 0,
+ args: unsafe.Pointer(&libc_setsid), // it's unused but must be non-nil, otherwise crashes
+ }
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&call))
+ return call.r1, call.err
+}
+
+//go:nosplit
+func syscall_setuid(uid uintptr) (err uintptr) {
+ call := libcall{
+ fn: unsafe.Pointer(&libc_setuid),
+ n: 1,
+ args: unsafe.Pointer(&uid),
+ }
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&call))
+ return call.err
+}
+
+//go:nosplit
+func syscall_setpgid(pid, pgid uintptr) (err uintptr) {
+ call := libcall{
+ fn: unsafe.Pointer(&libc_setpgid),
+ n: 2,
+ args: unsafe.Pointer(&pid),
+ }
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&call))
+ return call.err
+}
+
+// This is syscall.Syscall, it exists to satisfy some build dependency,
+// but it doesn't work correctly.
+//
+// DO NOT USE!
+//
+// TODO(aram): make this panic once we stop calling fcntl(2) in net using it.
+func syscall_syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
+ call := libcall{
+ fn: unsafe.Pointer(&libc_syscall),
+ n: 4,
+ args: unsafe.Pointer(&trap),
+ }
+ entersyscallblock()
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&call))
+ exitsyscall()
+ return call.r1, call.r2, call.err
+}
+
+func syscall_wait4(pid uintptr, wstatus *uint32, options uintptr, rusage unsafe.Pointer) (wpid int, err uintptr) {
+ call := libcall{
+ fn: unsafe.Pointer(&libc_wait4),
+ n: 4,
+ args: unsafe.Pointer(&pid),
+ }
+ entersyscallblock()
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&call))
+ exitsyscall()
+ return int(call.r1), call.err
+}
+
+//go:nosplit
+func syscall_write(fd, buf, nbyte uintptr) (n, err uintptr) {
+ call := libcall{
+ fn: unsafe.Pointer(&libc_write),
+ n: 3,
+ args: unsafe.Pointer(&fd),
+ }
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&call))
+ return call.r1, call.err
+}
diff --git a/src/pkg/runtime/syscall_solaris.goc b/src/pkg/runtime/syscall_solaris.goc
deleted file mode 100644
index 21bcce4d17..0000000000
--- a/src/pkg/runtime/syscall_solaris.goc
+++ /dev/null
@@ -1,374 +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 syscall
-#include "runtime.h"
-#include "defs_GOOS_GOARCH.h"
-#include "os_GOOS.h"
-#include "cgocall.h"
-#include "../../cmd/ld/textflag.h"
-
-#pragma dynimport libc·chdir chdir "libc.so"
-#pragma dynimport libc·chroot chroot "libc.so"
-#pragma dynimport libc·close close "libc.so"
-#pragma dynimport libc·dlclose dlclose "libc.so"
-#pragma dynimport libc·dlopen dlopen "libc.so"
-#pragma dynimport libc·dlsym dlsym "libc.so"
-#pragma dynimport libc·execve execve "libc.so"
-#pragma dynimport libc·fcntl fcntl "libc.so"
-#pragma dynimport libc·gethostname gethostname "libc.so"
-#pragma dynimport libc·ioctl ioctl "libc.so"
-#pragma dynimport libc·pipe pipe "libc.so"
-#pragma dynimport libc·setgid setgid "libc.so"
-#pragma dynimport libc·setgroups setgroups "libc.so"
-#pragma dynimport libc·setsid setsid "libc.so"
-#pragma dynimport libc·setuid setuid "libc.so"
-#pragma dynimport libc·setpgid setsid "libc.so"
-#pragma dynimport libc·syscall syscall "libc.so"
-#pragma dynimport libc·forkx forkx "libc.so"
-#pragma dynimport libc·wait4 wait4 "libc.so"
-extern uintptr libc·chdir;
-extern uintptr libc·chroot;
-extern uintptr libc·close;
-extern uintptr libc·dlclose;
-extern uintptr libc·dlopen;
-extern uintptr libc·dlsym;
-extern uintptr libc·execve;
-extern uintptr libc·exit;
-extern uintptr libc·fcntl;
-extern uintptr libc·gethostname;
-extern uintptr libc·ioctl;
-extern uintptr libc·pipe;
-extern uintptr libc·setgid;
-extern uintptr libc·setgroups;
-extern uintptr libc·setsid;
-extern uintptr libc·setuid;
-extern uintptr libc·setpgid;
-extern uintptr libc·syscall;
-extern uintptr libc·forkx;
-extern uintptr libc·wait4;
-extern uintptr libc·write;
-
-func sysvicall6(func uintptr, nargs uintptr, a1 uintptr, a2 uintptr, a3 uintptr, a4 uintptr, a5 uintptr, a6 uintptr) (r1 uintptr, r2 uintptr, err uintptr)
-{
- LibCall c;
-
- USED(a2);
- USED(a3);
- USED(a4);
- USED(a5);
- USED(a6);
- c.fn = (void*)func;
- c.n = nargs;
- c.args = (void*)&a1;
- runtime·cgocall(runtime·asmsysvicall6, &c);
- err = c.err;
- r1 = c.r1;
- r2 = c.r2;
-}
-
-#pragma textflag NOSPLIT
-func rawSysvicall6(func uintptr, nargs uintptr, a1 uintptr, a2 uintptr, a3 uintptr, a4 uintptr, a5 uintptr, a6 uintptr) (r1 uintptr, r2 uintptr, err uintptr)
-{
- LibCall c;
-
- USED(a2);
- USED(a3);
- USED(a4);
- USED(a5);
- USED(a6);
- c.fn = (void*)func;
- c.n = nargs;
- c.args = (void*)&a1;
- runtime·asmcgocall(runtime·asmsysvicall6, &c);
- err = c.err;
- r1 = c.r1;
- r2 = c.r2;
-}
-
-#pragma textflag NOSPLIT
-func chdir(path uintptr) (err uintptr) {
- LibCall c;
-
- c.fn = (void*)libc·chdir;
- c.n = 1;
- c.args = (void*)&path;
- runtime·asmcgocall(runtime·asmsysvicall6, &c);
- err = c.err;
-}
-
-#pragma textflag NOSPLIT
-func chroot1(path uintptr) (err uintptr) {
- LibCall c;
-
- c.fn = (void*)libc·chroot;
- c.n = 1;
- c.args = (void*)&path;
- runtime·asmcgocall(runtime·asmsysvicall6, &c);
- err = c.err;
-}
-
-#pragma textflag NOSPLIT
-func close(fd uintptr) (err uintptr) {
- LibCall c;
-
- c.fn = (void*)libc·close;
- c.n = 1;
- c.args = (void*)&fd;
- runtime·asmcgocall(runtime·asmsysvicall6, &c);
- err = c.err;
-}
-
-func dlclose(handle uintptr) (err uintptr) {
- LibCall c;
-
- USED(handle);
- c.fn = (void*)libc·dlclose;
- c.n = 1;
- c.args = (void*)&handle;
- runtime·cgocall(runtime·asmsysvicall6, &c);
- err = c.r1;
-}
-
-func dlopen(name *uint8, mode uintptr) (handle uintptr, err uintptr) {
- LibCall c;
-
- USED(mode);
- c.fn = (void*)libc·dlopen;
- c.n = 2;
- c.args = (void*)&name;
- runtime·cgocall(runtime·asmsysvicall6, &c);
- handle = c.r1;
- if(handle == 0)
- err = c.err;
- else
- err = 0;
-}
-
-func dlsym(handle uintptr, name *uint8) (proc uintptr, err uintptr) {
- LibCall c;
-
- USED(name);
- c.fn = (void*)libc·dlsym;
- c.n = 2;
- c.args = &handle;
- runtime·cgocall(runtime·asmsysvicall6, &c);
- proc = c.r1;
- if(proc == 0)
- err = c.err;
- else
- err = 0;
-}
-
-#pragma textflag NOSPLIT
-func execve(path uintptr, argv uintptr, envp uintptr) (err uintptr) {
- LibCall c;
-
- USED(argv);
- USED(envp);
- c.fn = (void*)libc·execve;
- c.n = 3;
- c.args = (void*)&path;
- runtime·asmcgocall(runtime·asmsysvicall6, &c);
- err = c.err;
-}
-
-#pragma textflag NOSPLIT
-func exit(code uintptr) {
- LibCall c;
-
- c.fn = (void*)libc·exit;
- c.n = 1;
- c.args = (void*)&code;
- runtime·asmcgocall(runtime·asmsysvicall6, &c);
-}
-
-#pragma textflag NOSPLIT
-func fcntl1(fd uintptr, cmd uintptr, arg uintptr) (val uintptr, err uintptr) {
- LibCall c;
-
- USED(cmd);
- USED(arg);
- c.fn = (void*)libc·fcntl;
- c.n = 3;
- c.args = (void*)&fd;
- runtime·asmcgocall(runtime·asmsysvicall6, &c);
- err = c.err;
- val = c.r1;
-}
-
-func gethostname() (name String, err uintptr) {
- struct { uintptr v[2]; } args;
- uint8 cname[MAXHOSTNAMELEN];
- LibCall c;
-
- c.fn = (void*)libc·gethostname;
- c.n = 2;
- args.v[0] = (uintptr)&cname[0];
- args.v[1] = MAXHOSTNAMELEN;
- c.args = (void*)&args;
- runtime·cgocall(runtime·asmsysvicall6, &c);
- err = c.err;
- if(c.r1) {
- name = runtime·emptystring;
- return;
- }
- cname[MAXHOSTNAMELEN - 1] = 0;
- name = runtime·gostring(cname);
-}
-
-#pragma textflag NOSPLIT
-func ioctl(fd uintptr, req uintptr, arg uintptr) (err uintptr) {
- LibCall c;
-
- USED(req);
- USED(arg);
- c.fn = (void*)libc·ioctl;
- c.n = 3;
- c.args = (void*)&fd;
- runtime·asmcgocall(runtime·asmsysvicall6, &c);
- err = c.err;
-}
-
-func wait4(pid uintptr, wstatus *uint32, options uintptr, rusage *void) (wpid int, err uintptr) {
- LibCall c;
-
- USED(wstatus);
- USED(options);
- USED(rusage);
- c.fn = (void*)libc·wait4;
- c.n = 4;
- c.args = (void*)&pid;
- runtime·cgocall(runtime·asmsysvicall6, &c);
- err = c.err;
- wpid = c.r1;
-}
-
-#pragma textflag NOSPLIT
-func setgid(gid uintptr) (err uintptr) {
- LibCall c;
-
- c.fn = (void*)libc·setgid;
- c.n = 1;
- c.args = (void*)&gid;
- runtime·asmcgocall(runtime·asmsysvicall6, &c);
- err = c.err;
-}
-
-#pragma textflag NOSPLIT
-func setgroups1(ngid uintptr, gid uintptr) (err uintptr) {
- LibCall c;
-
- USED(gid);
- c.fn = (void*)libc·setgroups;
- c.n = 2;
- c.args = (void*)&ngid;
- runtime·asmcgocall(runtime·asmsysvicall6, &c);
- err = c.err;
-}
-
-#pragma textflag NOSPLIT
-func setsid() (pid uintptr, err uintptr) {
- LibCall c;
-
- c.fn = (void*)libc·setsid;
- c.n = 0;
- c.args = (void*)0;
- runtime·asmcgocall(runtime·asmsysvicall6, &c);
- err = c.err;
- pid = c.r1;
-}
-
-#pragma textflag NOSPLIT
-func setuid(uid uintptr) (err uintptr) {
- LibCall c;
-
- c.fn = (void*)libc·setuid;
- c.n = 1;
- c.args = (void*)&uid;
- runtime·asmcgocall(runtime·asmsysvicall6, &c);
- err = c.err;
-}
-
-#pragma textflag NOSPLIT
-func setpgid(pid uintptr, pgid uintptr) (err uintptr) {
- LibCall c;
-
- USED(pgid);
- c.fn = (void*)libc·setpgid;
- c.n = 2;
- c.args = (void*)&pid;
- runtime·asmcgocall(runtime·asmsysvicall6, &c);
- err = c.err;
-}
-
-#pragma textflag NOSPLIT
-func forkx(flags uintptr) (pid uintptr, err uintptr) {
- LibCall c;
-
- c.fn = (void*)libc·forkx;
- c.n = 1;
- c.args = (void*)&flags;
- runtime·asmcgocall(runtime·asmsysvicall6, &c);
- err = c.err;
- pid = c.r1;
-}
-
-void runtime·pipe1(void);
-
-func pipe() (r uintptr, w uintptr, err uintptr) {
- LibCall c;
-
- c.fn = (void*)runtime·pipe1;
- c.n = 0;
- c.args = (void*)0;
- runtime·cgocall(runtime·asmsysvicall6, &c);
- err = c.err;
- r = c.r1;
- w = c.r2;
-}
-
-#pragma textflag NOSPLIT
-func write1(fd uintptr, buf uintptr, nbyte uintptr) (n uintptr, err uintptr) {
- LibCall c;
-
- USED(buf);
- USED(nbyte);
- c.fn = (void*)libc·write;
- c.n = 3;
- c.args = (void*)fd;
- runtime·asmcgocall(runtime·asmsysvicall6, &c);
- err = c.err;
- n = c.r1;
-}
-
-func Syscall(trap uintptr, a1 uintptr, a2 uintptr, a3 uintptr) (r1 uintptr, r2 uintptr, err uintptr) {
- LibCall c;
-
- USED(a1);
- USED(a2);
- USED(a3);
- c.fn = (void*)libc·syscall;
- c.n = 4;
- c.args = &trap;
- runtime·cgocall(runtime·asmsysvicall6, &c);
- err = c.err;
- r1 = c.r1;
- r2 = c.r2;
-}
-
-func RawSyscall(trap uintptr, a1 uintptr, a2 uintptr, a3 uintptr) (r1 uintptr, r2 uintptr, err uintptr) {
- LibCall c;
-
- USED(a1);
- USED(a2);
- USED(a3);
- c.fn = (void*)libc·syscall;
- c.n = 4;
- c.args = &trap;
- runtime·asmcgocall(runtime·asmsysvicall6, &c);
- err = c.err;
- r1 = c.r1;
- r2 = c.r2;
-}
diff --git a/src/pkg/runtime/syscall_windows.c b/src/pkg/runtime/syscall_windows.c
new file mode 100644
index 0000000000..e7903b5171
--- /dev/null
+++ b/src/pkg/runtime/syscall_windows.c
@@ -0,0 +1,166 @@
+// 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.
+
+#include "runtime.h"
+#include "os_GOOS.h"
+#include "cgocall.h"
+#include "textflag.h"
+
+typedef struct HandleErr HandleErr;
+typedef struct SyscallErr SyscallErr;
+
+struct HandleErr {
+ uintptr handle;
+ uintptr err;
+};
+
+struct SyscallErr {
+ uintptr r1;
+ uintptr r2;
+ uintptr err;
+};
+
+#pragma textflag NOSPLIT
+HandleErr
+syscall·loadlibrary(uint16 *filename)
+{
+ LibCall c;
+ HandleErr r;
+
+ c.fn = runtime·LoadLibrary;
+ c.n = 1;
+ c.args = &filename;
+ runtime·cgocall_errno(runtime·asmstdcall, &c);
+ r.handle = c.r1;
+ if(r.handle == 0)
+ r.err = c.err;
+ else
+ r.err = 0;
+ return r;
+}
+
+#pragma textflag NOSPLIT
+HandleErr
+syscall·getprocaddress(uintptr handle, int8 *procname)
+{
+ LibCall c;
+ HandleErr r;
+
+ USED(procname);
+ c.fn = runtime·GetProcAddress;
+ c.n = 2;
+ c.args = &handle;
+ runtime·cgocall_errno(runtime·asmstdcall, &c);
+ r.handle = c.r1;
+ if(r.handle == 0)
+ r.err = c.err;
+ else
+ r.err = 0;
+ return r;
+}
+
+#pragma textflag NOSPLIT
+SyscallErr
+syscall·Syscall(uintptr fn, uintptr nargs, uintptr a1, uintptr a2, uintptr a3)
+{
+ LibCall c;
+
+ USED(a2);
+ USED(a3);
+ c.fn = (void*)fn;
+ c.n = nargs;
+ c.args = &a1;
+ runtime·cgocall_errno(runtime·asmstdcall, &c);
+ return (SyscallErr){c.r1, c.r2, c.err};
+}
+
+#pragma textflag NOSPLIT
+SyscallErr
+syscall·Syscall6(uintptr fn, uintptr nargs, uintptr a1, uintptr a2, uintptr a3, uintptr a4, uintptr a5, uintptr a6)
+{
+ LibCall c;
+
+ USED(a2);
+ USED(a3);
+ USED(a4);
+ USED(a5);
+ USED(a6);
+ c.fn = (void*)fn;
+ c.n = nargs;
+ c.args = &a1;
+ runtime·cgocall_errno(runtime·asmstdcall, &c);
+ return (SyscallErr){c.r1, c.r2, c.err};
+}
+
+#pragma textflag NOSPLIT
+SyscallErr
+syscall·Syscall9(uintptr fn, uintptr nargs, uintptr a1, uintptr a2, uintptr a3, uintptr a4, uintptr a5, uintptr a6, uintptr a7, uintptr a8, uintptr a9)
+{
+ LibCall c;
+
+ USED(a2);
+ USED(a3);
+ USED(a4);
+ USED(a5);
+ USED(a6);
+ USED(a7);
+ USED(a8);
+ USED(a9);
+ c.fn = (void*)fn;
+ c.n = nargs;
+ c.args = &a1;
+ runtime·cgocall_errno(runtime·asmstdcall, &c);
+ return (SyscallErr){c.r1, c.r2, c.err};
+}
+
+#pragma textflag NOSPLIT
+SyscallErr
+syscall·Syscall12(uintptr fn, uintptr nargs, uintptr a1, uintptr a2, uintptr a3, uintptr a4, uintptr a5, uintptr a6, uintptr a7, uintptr a8, uintptr a9, uintptr a10, uintptr a11, uintptr a12)
+{
+ LibCall c;
+
+ USED(a2);
+ USED(a3);
+ USED(a4);
+ USED(a5);
+ USED(a6);
+ USED(a7);
+ USED(a8);
+ USED(a9);
+ USED(a10);
+ USED(a11);
+ USED(a12);
+ c.fn = (void*)fn;
+ c.n = nargs;
+ c.args = &a1;
+ runtime·cgocall_errno(runtime·asmstdcall, &c);
+ return (SyscallErr){c.r1, c.r2, c.err};
+}
+
+#pragma textflag NOSPLIT
+SyscallErr
+syscall·Syscall15(uintptr fn, uintptr nargs, uintptr a1, uintptr a2, uintptr a3, uintptr a4, uintptr a5, uintptr a6, uintptr a7, uintptr a8, uintptr a9, uintptr a10, uintptr a11, uintptr a12, uintptr a13, uintptr a14, uintptr a15)
+{
+ LibCall c;
+
+ USED(a2);
+ USED(a3);
+ USED(a4);
+ USED(a5);
+ USED(a6);
+ USED(a7);
+ USED(a8);
+ USED(a9);
+ USED(a10);
+ USED(a11);
+ USED(a12);
+ USED(a13);
+ USED(a14);
+ USED(a15);
+ c.fn = (void*)fn;
+ c.n = nargs;
+ c.args = &a1;
+ runtime·cgocall_errno(runtime·asmstdcall, &c);
+ return (SyscallErr){c.r1, c.r2, c.err};
+}
diff --git a/src/pkg/runtime/syscall_windows.go b/src/pkg/runtime/syscall_windows.go
new file mode 100644
index 0000000000..0592c57e1d
--- /dev/null
+++ b/src/pkg/runtime/syscall_windows.go
@@ -0,0 +1,88 @@
+// 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"
+)
+
+type callbacks struct {
+ lock mutex
+ ctxt [cb_max]*wincallbackcontext
+ n int
+}
+
+func (c *wincallbackcontext) isCleanstack() bool {
+ return c.cleanstack
+}
+
+func (c *wincallbackcontext) setCleanstack(cleanstack bool) {
+ c.cleanstack = cleanstack
+}
+
+var (
+ cbs callbacks
+ cbctxts **wincallbackcontext = &cbs.ctxt[0] // to simplify access to cbs.ctxt in sys_windows_*.s
+
+ callbackasm byte // type isn't really byte, it's code in runtime
+)
+
+// callbackasmAddr returns address of runtime.callbackasm
+// function adjusted by i.
+// runtime.callbackasm is just a series of CALL instructions
+// (each is 5 bytes long), and we want callback to arrive at
+// correspondent call instruction instead of start of
+// runtime.callbackasm.
+func callbackasmAddr(i int) uintptr {
+ return uintptr(add(unsafe.Pointer(&callbackasm), uintptr(i*5)))
+}
+
+func compileCallback(fn eface, cleanstack bool) (code uintptr) {
+ if fn._type == nil || (fn._type.kind&kindMask) != kindFunc {
+ panic("compilecallback: not a function")
+ }
+ ft := (*functype)(unsafe.Pointer(fn._type))
+ if len(ft.out) != 1 {
+ panic("compilecallback: function must have one output parameter")
+ }
+ uintptrSize := unsafe.Sizeof(uintptr(0))
+ if t := (**_type)(unsafe.Pointer(&ft.out[0])); (*t).size != uintptrSize {
+ panic("compilecallback: output parameter size is wrong")
+ }
+ argsize := uintptr(0)
+ for _, t := range (*[1024](*_type))(unsafe.Pointer(&ft.in[0]))[:len(ft.in)] {
+ if (*t).size != uintptrSize {
+ panic("compilecallback: input parameter size is wrong")
+ }
+ argsize += uintptrSize
+ }
+
+ lock(&cbs.lock)
+ defer unlock(&cbs.lock)
+
+ n := cbs.n
+ for i := 0; i < n; i++ {
+ if cbs.ctxt[i].gobody == fn.data && cbs.ctxt[i].isCleanstack() == cleanstack {
+ return callbackasmAddr(i)
+ }
+ }
+ if n >= cb_max {
+ gothrow("too many callback functions")
+ }
+
+ c := new(wincallbackcontext)
+ c.gobody = fn.data
+ c.argsize = argsize
+ c.setCleanstack(cleanstack)
+ if cleanstack && argsize != 0 {
+ c.restorestack = argsize
+ } else {
+ c.restorestack = 0
+ }
+ cbs.ctxt[n] = c
+ cbs.n++
+
+ return callbackasmAddr(n)
+}
diff --git a/src/pkg/runtime/syscall_windows.goc b/src/pkg/runtime/syscall_windows.goc
deleted file mode 100644
index 528245363e..0000000000
--- a/src/pkg/runtime/syscall_windows.goc
+++ /dev/null
@@ -1,145 +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 syscall
-#include "runtime.h"
-#include "os_GOOS.h"
-#include "cgocall.h"
-
-func loadlibrary(filename *uint16) (handle uintptr, err uintptr) {
- LibCall c;
-
- c.fn = runtime·LoadLibrary;
- c.n = 1;
- c.args = &filename;
- runtime·cgocall(runtime·asmstdcall, &c);
- handle = c.r1;
- if(handle == 0)
- err = c.err;
- else
- err = 0;
-}
-
-func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err uintptr) {
- LibCall c;
-
- USED(procname);
- c.fn = runtime·GetProcAddress;
- c.n = 2;
- c.args = &handle;
- runtime·cgocall(runtime·asmstdcall, &c);
- proc = c.r1;
- if(proc == 0)
- err = c.err;
- else
- err = 0;
-}
-
-func NewCallback(fn Eface) (code uintptr) {
- code = (uintptr)runtime·compilecallback(fn, true);
-}
-
-func NewCallbackCDecl(fn Eface) (code uintptr) {
- code = (uintptr)runtime·compilecallback(fn, false);
-}
-
-func Syscall(fn uintptr, nargs uintptr, a1 uintptr, a2 uintptr, a3 uintptr) (r1 uintptr, r2 uintptr, err uintptr) {
- LibCall c;
-
- USED(a2);
- USED(a3);
- c.fn = (void*)fn;
- c.n = nargs;
- c.args = &a1;
- runtime·cgocall(runtime·asmstdcall, &c);
- err = c.err;
- r1 = c.r1;
- r2 = c.r2;
-}
-
-func Syscall6(fn uintptr, nargs uintptr, a1 uintptr, a2 uintptr, a3 uintptr, a4 uintptr, a5 uintptr, a6 uintptr) (r1 uintptr, r2 uintptr, err uintptr) {
- LibCall c;
-
- USED(a2);
- USED(a3);
- USED(a4);
- USED(a5);
- USED(a6);
- c.fn = (void*)fn;
- c.n = nargs;
- c.args = &a1;
- runtime·cgocall(runtime·asmstdcall, &c);
- err = c.err;
- r1 = c.r1;
- r2 = c.r2;
-}
-
-func Syscall9(fn uintptr, nargs uintptr, a1 uintptr, a2 uintptr, a3 uintptr, a4 uintptr, a5 uintptr, a6 uintptr, a7 uintptr, a8 uintptr, a9 uintptr) (r1 uintptr, r2 uintptr, err uintptr) {
- LibCall c;
-
- USED(a2);
- USED(a3);
- USED(a4);
- USED(a5);
- USED(a6);
- USED(a7);
- USED(a8);
- USED(a9);
- c.fn = (void*)fn;
- c.n = nargs;
- c.args = &a1;
- runtime·cgocall(runtime·asmstdcall, &c);
- err = c.err;
- r1 = c.r1;
- r2 = c.r2;
-}
-
-func Syscall12(fn uintptr, nargs uintptr, a1 uintptr, a2 uintptr, a3 uintptr, a4 uintptr, a5 uintptr, a6 uintptr, a7 uintptr, a8 uintptr, a9 uintptr, a10 uintptr, a11 uintptr, a12 uintptr) (r1 uintptr, r2 uintptr, err uintptr) {
- LibCall c;
-
- USED(a2);
- USED(a3);
- USED(a4);
- USED(a5);
- USED(a6);
- USED(a7);
- USED(a8);
- USED(a9);
- USED(a10);
- USED(a11);
- USED(a12);
- c.fn = (void*)fn;
- c.n = nargs;
- c.args = &a1;
- runtime·cgocall(runtime·asmstdcall, &c);
- err = c.err;
- r1 = c.r1;
- r2 = c.r2;
-}
-
-func Syscall15(fn uintptr, nargs uintptr, a1 uintptr, a2 uintptr, a3 uintptr, a4 uintptr, a5 uintptr, a6 uintptr, a7 uintptr, a8 uintptr, a9 uintptr, a10 uintptr, a11 uintptr, a12 uintptr, a13 uintptr, a14 uintptr, a15 uintptr) (r1 uintptr, r2 uintptr, err uintptr) {
- LibCall c;
-
- USED(a2);
- USED(a3);
- USED(a4);
- USED(a5);
- USED(a6);
- USED(a7);
- USED(a8);
- USED(a9);
- USED(a10);
- USED(a11);
- USED(a12);
- USED(a13);
- USED(a14);
- USED(a15);
- c.fn = (void*)fn;
- c.n = nargs;
- c.args = &a1;
- runtime·cgocall(runtime·asmstdcall, &c);
- err = c.err;
- r1 = c.r1;
- r2 = c.r2;
-}
diff --git a/src/pkg/runtime/thunk.s b/src/pkg/runtime/thunk.s
new file mode 100644
index 0000000000..8cd9afe608
--- /dev/null
+++ b/src/pkg/runtime/thunk.s
@@ -0,0 +1,156 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file exposes various internal runtime functions to other packages in std lib.
+
+#include "zasm_GOOS_GOARCH.h"
+#include "textflag.h"
+
+#ifdef GOARCH_arm
+#define JMP B
+#endif
+#ifdef GOARCH_power64
+#define JMP BR
+#endif
+#ifdef GOARCH_power64le
+#define JMP BR
+#endif
+
+TEXT net·runtimeNano(SB),NOSPLIT,$0-0
+ JMP runtime·nanotime(SB)
+
+TEXT time·runtimeNano(SB),NOSPLIT,$0-0
+ JMP runtime·nanotime(SB)
+
+TEXT time·Sleep(SB),NOSPLIT,$0-0
+ JMP runtime·timeSleep(SB)
+
+TEXT time·startTimer(SB),NOSPLIT,$0-0
+ JMP runtime·startTimer(SB)
+
+TEXT time·stopTimer(SB),NOSPLIT,$0-0
+ JMP runtime·stopTimer(SB)
+
+TEXT sync·runtime_Syncsemacquire(SB),NOSPLIT,$0-0
+ JMP runtime·syncsemacquire(SB)
+
+TEXT sync·runtime_Syncsemrelease(SB),NOSPLIT,$0-0
+ JMP runtime·syncsemrelease(SB)
+
+TEXT sync·runtime_Syncsemcheck(SB),NOSPLIT,$0-0
+ JMP runtime·syncsemcheck(SB)
+
+TEXT sync·runtime_Semacquire(SB),NOSPLIT,$0-0
+ JMP runtime·asyncsemacquire(SB)
+
+TEXT sync·runtime_Semrelease(SB),NOSPLIT,$0-0
+ JMP runtime·asyncsemrelease(SB)
+
+TEXT sync·runtime_registerPoolCleanup(SB),NOSPLIT,$0-0
+ JMP runtime·registerPoolCleanup(SB)
+
+TEXT net·runtime_Semacquire(SB),NOSPLIT,$0-0
+ JMP runtime·asyncsemacquire(SB)
+
+TEXT net·runtime_Semrelease(SB),NOSPLIT,$0-0
+ JMP runtime·asyncsemrelease(SB)
+
+TEXT runtime∕pprof·runtime_cyclesPerSecond(SB),NOSPLIT,$0-0
+ JMP runtime·tickspersecond(SB)
+
+TEXT bytes·Compare(SB),NOSPLIT,$0-0
+ JMP runtime·cmpbytes(SB)
+
+TEXT runtime·reflectcall(SB), NOSPLIT, $0-0
+ JMP reflect·call(SB)
+
+TEXT reflect·chanclose(SB), NOSPLIT, $0-0
+ JMP runtime·closechan(SB)
+
+TEXT reflect·chanlen(SB), NOSPLIT, $0-0
+ JMP runtime·reflect_chanlen(SB)
+
+TEXT reflect·chancap(SB), NOSPLIT, $0-0
+ JMP runtime·reflect_chancap(SB)
+
+TEXT reflect·chansend(SB), NOSPLIT, $0-0
+ JMP runtime·reflect_chansend(SB)
+
+TEXT reflect·chanrecv(SB), NOSPLIT, $0-0
+ JMP runtime·reflect_chanrecv(SB)
+
+TEXT runtime∕debug·freeOSMemory(SB), NOSPLIT, $0-0
+ JMP runtime·freeOSMemory(SB)
+
+TEXT net·runtime_pollServerInit(SB),NOSPLIT,$0-0
+ JMP runtime·netpollServerInit(SB)
+
+TEXT net·runtime_pollOpen(SB),NOSPLIT,$0-0
+ JMP runtime·netpollOpen(SB)
+
+TEXT net·runtime_pollClose(SB),NOSPLIT,$0-0
+ JMP runtime·netpollClose(SB)
+
+TEXT net·runtime_pollReset(SB),NOSPLIT,$0-0
+ JMP runtime·netpollReset(SB)
+
+TEXT net·runtime_pollWait(SB),NOSPLIT,$0-0
+ JMP runtime·netpollWait(SB)
+
+TEXT net·runtime_pollWaitCanceled(SB),NOSPLIT,$0-0
+ JMP runtime·netpollWaitCanceled(SB)
+
+TEXT net·runtime_pollSetDeadline(SB),NOSPLIT,$0-0
+ JMP runtime·netpollSetDeadline(SB)
+
+TEXT net·runtime_pollUnblock(SB),NOSPLIT,$0-0
+ JMP runtime·netpollUnblock(SB)
+
+TEXT syscall·setenv_c(SB), NOSPLIT, $0-0
+ JMP runtime·syscall_setenv_c(SB)
+
+TEXT reflect·makemap(SB),NOSPLIT,$0-0
+ JMP runtime·reflect_makemap(SB)
+
+TEXT reflect·mapaccess(SB),NOSPLIT,$0-0
+ JMP runtime·reflect_mapaccess(SB)
+
+TEXT reflect·mapassign(SB),NOSPLIT,$0-0
+ JMP runtime·reflect_mapassign(SB)
+
+TEXT reflect·mapdelete(SB),NOSPLIT,$0-0
+ JMP runtime·reflect_mapdelete(SB)
+
+TEXT reflect·mapiterinit(SB),NOSPLIT,$0-0
+ JMP runtime·reflect_mapiterinit(SB)
+
+TEXT reflect·mapiterkey(SB),NOSPLIT,$0-0
+ JMP runtime·reflect_mapiterkey(SB)
+
+TEXT reflect·mapiternext(SB),NOSPLIT,$0-0
+ JMP runtime·reflect_mapiternext(SB)
+
+TEXT reflect·maplen(SB),NOSPLIT,$0-0
+ JMP runtime·reflect_maplen(SB)
+
+TEXT reflect·ismapkey(SB),NOSPLIT,$0-0
+ JMP runtime·reflect_ismapkey(SB)
+
+TEXT reflect·ifaceE2I(SB),NOSPLIT,$0-0
+ JMP runtime·reflect_ifaceE2I(SB)
+
+TEXT reflect·unsafe_New(SB),NOSPLIT,$0-0
+ JMP runtime·newobject(SB)
+
+TEXT reflect·unsafe_NewArray(SB),NOSPLIT,$0-0
+ JMP runtime·newarray(SB)
+
+TEXT reflect·makechan(SB),NOSPLIT,$0-0
+ JMP runtime·makechan(SB)
+
+TEXT reflect·rselect(SB), NOSPLIT, $0-0
+ JMP runtime·reflect_rselect(SB)
+
+TEXT os·sigpipe(SB), NOSPLIT, $0-0
+ JMP runtime·os_sigpipe(SB)
diff --git a/src/pkg/runtime/thunk_solaris_amd64.s b/src/pkg/runtime/thunk_solaris_amd64.s
new file mode 100644
index 0000000000..352011e047
--- /dev/null
+++ b/src/pkg/runtime/thunk_solaris_amd64.s
@@ -0,0 +1,88 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file exposes various external library functions to Go code in the runtime.
+
+#include "zasm_GOOS_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
+
+TEXT runtime·libc_chdir(SB),NOSPLIT,$0
+ MOVQ libc·chdir(SB), AX
+ JMP AX
+
+TEXT runtime·libc_chroot(SB),NOSPLIT,$0
+ MOVQ libc·chroot(SB), AX
+ JMP AX
+
+TEXT runtime·libc_close(SB),NOSPLIT,$0
+ MOVQ libc·close(SB), AX
+ JMP AX
+
+TEXT runtime·libc_dlopen(SB),NOSPLIT,$0
+ MOVQ libc·dlopen(SB), AX
+ JMP AX
+
+TEXT runtime·libc_dlclose(SB),NOSPLIT,$0
+ MOVQ libc·dlclose(SB), AX
+ JMP AX
+
+TEXT runtime·libc_dlsym(SB),NOSPLIT,$0
+ MOVQ libc·dlsym(SB), AX
+ JMP AX
+
+TEXT runtime·libc_execve(SB),NOSPLIT,$0
+ MOVQ libc·execve(SB), AX
+ JMP AX
+
+TEXT runtime·libc_exit(SB),NOSPLIT,$0
+ MOVQ libc·exit(SB), AX
+ JMP AX
+
+TEXT runtime·libc_fcntl(SB),NOSPLIT,$0
+ MOVQ libc·fcntl(SB), AX
+ JMP AX
+
+TEXT runtime·libc_forkx(SB),NOSPLIT,$0
+ MOVQ libc·forkx(SB), AX
+ JMP AX
+
+TEXT runtime·libc_gethostname(SB),NOSPLIT,$0
+ MOVQ libc·gethostname(SB), AX
+ JMP AX
+
+TEXT runtime·libc_ioctl(SB),NOSPLIT,$0
+ MOVQ libc·ioctl(SB), AX
+ JMP AX
+
+TEXT runtime·libc_setgid(SB),NOSPLIT,$0
+ MOVQ libc·setgid(SB), AX
+ JMP AX
+
+TEXT runtime·libc_setgroups(SB),NOSPLIT,$0
+ MOVQ libc·setgroups(SB), AX
+ JMP AX
+
+TEXT runtime·libc_setsid(SB),NOSPLIT,$0
+ MOVQ libc·setsid(SB), AX
+ JMP AX
+
+TEXT runtime·libc_setuid(SB),NOSPLIT,$0
+ MOVQ libc·setuid(SB), AX
+ JMP AX
+
+TEXT runtime·libc_setpgid(SB),NOSPLIT,$0
+ MOVQ libc·setpgid(SB), AX
+ JMP AX
+
+TEXT runtime·libc_syscall(SB),NOSPLIT,$0
+ MOVQ libc·syscall(SB), AX
+ JMP AX
+
+TEXT runtime·libc_wait4(SB),NOSPLIT,$0
+ MOVQ libc·wait4(SB), AX
+ JMP AX
+
+TEXT runtime·libc_write(SB),NOSPLIT,$0
+ MOVQ libc·write(SB), AX
+ JMP AX
diff --git a/src/pkg/runtime/time.go b/src/pkg/runtime/time.go
new file mode 100644
index 0000000000..8cf9eecf83
--- /dev/null
+++ b/src/pkg/runtime/time.go
@@ -0,0 +1,266 @@
+// 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.
+
+// Time-related runtime and pieces of package time.
+
+package runtime
+
+import "unsafe"
+
+// Package time knows the layout of this structure.
+// If this struct changes, adjust ../time/sleep.go:/runtimeTimer.
+// For GOOS=nacl, package syscall knows the layout of this structure.
+// If this struct changes, adjust ../syscall/net_nacl.go:/runtimeTimer.
+type timer struct {
+ i int // heap index
+
+ // Timer wakes up at when, and then at when+period, ... (period > 0 only)
+ // each time calling f(now, arg) in the timer goroutine, so f must be
+ // a well-behaved function and not block.
+ when int64
+ period int64
+ f func(interface{}, uintptr)
+ arg interface{}
+ seq uintptr
+}
+
+var timers struct {
+ lock mutex
+ gp *g
+ created bool
+ sleeping bool
+ rescheduling bool
+ waitnote note
+ t []*timer
+}
+
+// nacl fake time support.
+var timens int64
+
+// Package time APIs.
+// Godoc uses the comments in package time, not these.
+
+// time.now is implemented in assembly.
+
+// Sleep puts the current goroutine to sleep for at least ns nanoseconds.
+func timeSleep(ns int64) {
+ if ns <= 0 {
+ return
+ }
+
+ t := new(timer)
+ t.when = nanotime() + ns
+ t.f = goroutineReady
+ t.arg = getg()
+ lock(&timers.lock)
+ addtimerLocked(t)
+ goparkunlock(&timers.lock, "sleep")
+}
+
+// startTimer adds t to the timer heap.
+func startTimer(t *timer) {
+ if raceenabled {
+ racerelease(unsafe.Pointer(t))
+ }
+ addtimer(t)
+}
+
+// stopTimer removes t from the timer heap if it is there.
+// It returns true if t was removed, false if t wasn't even there.
+func stopTimer(t *timer) bool {
+ return deltimer(t)
+}
+
+// Go runtime.
+
+// Ready the goroutine arg.
+func goroutineReady(arg interface{}, seq uintptr) {
+ goready(arg.(*g))
+}
+
+func addtimer(t *timer) {
+ lock(&timers.lock)
+ addtimerLocked(t)
+ unlock(&timers.lock)
+}
+
+// Add a timer to the heap and start or kick the timer proc.
+// If the new timer is earlier than any of the others.
+// Timers are locked.
+func addtimerLocked(t *timer) {
+ // when must never be negative; otherwise timerproc will overflow
+ // during its delta calculation and never expire other runtime·timers.
+ if t.when < 0 {
+ t.when = 1<<63 - 1
+ }
+ t.i = len(timers.t)
+ timers.t = append(timers.t, t)
+ siftupTimer(t.i)
+ if t.i == 0 {
+ // siftup moved to top: new earliest deadline.
+ if timers.sleeping {
+ timers.sleeping = false
+ notewakeup(&timers.waitnote)
+ }
+ if timers.rescheduling {
+ timers.rescheduling = false
+ goready(timers.gp)
+ }
+ }
+ if !timers.created {
+ timers.created = true
+ go timerproc()
+ }
+}
+
+// Delete timer t from the heap.
+// Do not need to update the timerproc: if it wakes up early, no big deal.
+func deltimer(t *timer) bool {
+ // Dereference t so that any panic happens before the lock is held.
+ // Discard result, because t might be moving in the heap.
+ _ = t.i
+
+ lock(&timers.lock)
+ // t may not be registered anymore and may have
+ // a bogus i (typically 0, if generated by Go).
+ // Verify it before proceeding.
+ i := t.i
+ last := len(timers.t) - 1
+ if i < 0 || i > last || timers.t[i] != t {
+ unlock(&timers.lock)
+ return false
+ }
+ if i != last {
+ timers.t[i] = timers.t[last]
+ timers.t[i].i = i
+ }
+ timers.t[last] = nil
+ timers.t = timers.t[:last]
+ if i != last {
+ siftupTimer(i)
+ siftdownTimer(i)
+ }
+ unlock(&timers.lock)
+ return true
+}
+
+// Timerproc runs the time-driven events.
+// It sleeps until the next event in the timers heap.
+// If addtimer inserts a new earlier event, addtimer1 wakes timerproc early.
+func timerproc() {
+ timers.gp = getg()
+ timers.gp.issystem = true
+ for {
+ lock(&timers.lock)
+ timers.sleeping = false
+ now := nanotime()
+ delta := int64(-1)
+ for {
+ if len(timers.t) == 0 {
+ delta = -1
+ break
+ }
+ t := timers.t[0]
+ delta = t.when - now
+ if delta > 0 {
+ break
+ }
+ if t.period > 0 {
+ // leave in heap but adjust next time to fire
+ t.when += t.period * (1 + -delta/t.period)
+ siftdownTimer(0)
+ } else {
+ // remove from heap
+ last := len(timers.t) - 1
+ if last > 0 {
+ timers.t[0] = timers.t[last]
+ timers.t[0].i = 0
+ }
+ timers.t[last] = nil
+ timers.t = timers.t[:last]
+ if last > 0 {
+ siftdownTimer(0)
+ }
+ t.i = -1 // mark as removed
+ }
+ f := t.f
+ arg := t.arg
+ seq := t.seq
+ unlock(&timers.lock)
+ if raceenabled {
+ raceacquire(unsafe.Pointer(t))
+ }
+ f(arg, seq)
+ lock(&timers.lock)
+ }
+ if delta < 0 {
+ // No timers left - put goroutine to sleep.
+ timers.rescheduling = true
+ goparkunlock(&timers.lock, "timer goroutine (idle)")
+ continue
+ }
+ // At least one timer pending. Sleep until then.
+ timers.sleeping = true
+ noteclear(&timers.waitnote)
+ unlock(&timers.lock)
+ notetsleepg(&timers.waitnote, delta)
+ }
+}
+
+// Heap maintenance algorithms.
+
+func siftupTimer(i int) {
+ t := timers.t
+ when := t[i].when
+ tmp := t[i]
+ for i > 0 {
+ p := (i - 1) / 4 // parent
+ if when >= t[p].when {
+ break
+ }
+ t[i] = t[p]
+ t[i].i = i
+ t[p] = tmp
+ t[p].i = p
+ i = p
+ }
+}
+
+func siftdownTimer(i int) {
+ t := timers.t
+ n := len(t)
+ when := t[i].when
+ tmp := t[i]
+ for {
+ c := i*4 + 1 // left child
+ c3 := c + 2 // mid child
+ if c >= n {
+ break
+ }
+ w := t[c].when
+ if c+1 < n && t[c+1].when < w {
+ w = t[c+1].when
+ c++
+ }
+ if c3 < n {
+ w3 := t[c3].when
+ if c3+1 < n && t[c3+1].when < w3 {
+ w3 = t[c3+1].when
+ c3++
+ }
+ if w3 < w {
+ w = w3
+ c = c3
+ }
+ }
+ if w >= when {
+ break
+ }
+ t[i] = t[c]
+ t[i].i = i
+ t[c] = tmp
+ t[c].i = c
+ i = c
+ }
+}
diff --git a/src/pkg/runtime/time.goc b/src/pkg/runtime/time.goc
deleted file mode 100644
index 791e4eb02b..0000000000
--- a/src/pkg/runtime/time.goc
+++ /dev/null
@@ -1,343 +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.
-
-// Time-related runtime and pieces of package time.
-
-package time
-
-#include "runtime.h"
-#include "defs_GOOS_GOARCH.h"
-#include "os_GOOS.h"
-#include "arch_GOARCH.h"
-#include "malloc.h"
-#include "race.h"
-
-enum {
- debug = 0,
-};
-
-static Timers timers;
-static void addtimer(Timer*);
-static void dumptimers(int8*);
-
-// nacl fake time support.
-int64 runtime·timens;
-
-// Package time APIs.
-// Godoc uses the comments in package time, not these.
-
-// time.now is implemented in assembly.
-
-// runtimeNano returns the current value of the runtime clock in nanoseconds.
-func runtimeNano() (ns int64) {
- ns = runtime·nanotime();
-}
-
-// Sleep puts the current goroutine to sleep for at least ns nanoseconds.
-func Sleep(ns int64) {
- runtime·tsleep(ns, "sleep");
-}
-
-// startTimer adds t to the timer heap.
-func startTimer(t *Timer) {
- if(raceenabled)
- runtime·racerelease(t);
- runtime·addtimer(t);
-}
-
-// stopTimer removes t from the timer heap if it is there.
-// It returns true if t was removed, false if t wasn't even there.
-func stopTimer(t *Timer) (stopped bool) {
- stopped = runtime·deltimer(t);
-}
-
-// C runtime.
-
-void runtime·gc_unixnanotime(int64 *now);
-
-int64 runtime·unixnanotime(void)
-{
- int64 now;
-
- runtime·gc_unixnanotime(&now);
- return now;
-}
-
-static void timerproc(void);
-static void siftup(int32);
-static void siftdown(int32);
-
-// Ready the goroutine e.data.
-static void
-ready(int64 now, Eface e)
-{
- USED(now);
-
- runtime·ready(e.data);
-}
-
-static FuncVal readyv = {(void(*)(void))ready};
-
-// Put the current goroutine to sleep for ns nanoseconds.
-void
-runtime·tsleep(int64 ns, int8 *reason)
-{
- Timer t;
-
- if(ns <= 0)
- return;
-
- t.when = runtime·nanotime() + ns;
- t.period = 0;
- t.fv = &readyv;
- t.arg.data = g;
- runtime·lock(&timers);
- addtimer(&t);
- runtime·parkunlock(&timers, reason);
-}
-
-static FuncVal timerprocv = {timerproc};
-
-void
-runtime·addtimer(Timer *t)
-{
- runtime·lock(&timers);
- addtimer(t);
- runtime·unlock(&timers);
-}
-
-// Add a timer to the heap and start or kick the timer proc
-// if the new timer is earlier than any of the others.
-static void
-addtimer(Timer *t)
-{
- int32 n;
- Timer **nt;
-
- // when must never be negative; otherwise timerproc will overflow
- // during its delta calculation and never expire other timers.
- if(t->when < 0)
- t->when = (1LL<<63)-1;
-
- if(timers.len >= timers.cap) {
- // Grow slice.
- n = 16;
- if(n <= timers.cap)
- n = timers.cap*3 / 2;
- nt = runtime·malloc(n*sizeof nt[0]);
- runtime·memmove(nt, timers.t, timers.len*sizeof nt[0]);
- timers.t = nt;
- timers.cap = n;
- }
- t->i = timers.len++;
- timers.t[t->i] = t;
- siftup(t->i);
- if(t->i == 0) {
- // siftup moved to top: new earliest deadline.
- if(timers.sleeping) {
- timers.sleeping = false;
- runtime·notewakeup(&timers.waitnote);
- }
- if(timers.rescheduling) {
- timers.rescheduling = false;
- runtime·ready(timers.timerproc);
- }
- }
- if(timers.timerproc == nil) {
- timers.timerproc = runtime·newproc1(&timerprocv, nil, 0, 0, addtimer);
- timers.timerproc->issystem = true;
- }
- if(debug)
- dumptimers("addtimer");
-}
-
-// Delete timer t from the heap.
-// Do not need to update the timerproc:
-// if it wakes up early, no big deal.
-bool
-runtime·deltimer(Timer *t)
-{
- int32 i;
-
- // Dereference t so that any panic happens before the lock is held.
- // Discard result, because t might be moving in the heap.
- i = t->i;
- USED(i);
-
- runtime·lock(&timers);
-
- // t may not be registered anymore and may have
- // a bogus i (typically 0, if generated by Go).
- // Verify it before proceeding.
- i = t->i;
- if(i < 0 || i >= timers.len || timers.t[i] != t) {
- runtime·unlock(&timers);
- return false;
- }
-
- timers.len--;
- if(i == timers.len) {
- timers.t[i] = nil;
- } else {
- timers.t[i] = timers.t[timers.len];
- timers.t[timers.len] = nil;
- timers.t[i]->i = i;
- siftup(i);
- siftdown(i);
- }
- if(debug)
- dumptimers("deltimer");
- runtime·unlock(&timers);
- return true;
-}
-
-// Timerproc runs the time-driven events.
-// It sleeps until the next event in the timers heap.
-// If addtimer inserts a new earlier event, addtimer
-// wakes timerproc early.
-static void
-timerproc(void)
-{
- int64 delta, now;
- Timer *t;
- void (*f)(int64, Eface);
- Eface arg;
-
- for(;;) {
- runtime·lock(&timers);
- timers.sleeping = false;
- now = runtime·nanotime();
- for(;;) {
- if(timers.len == 0) {
- delta = -1;
- break;
- }
- t = timers.t[0];
- delta = t->when - now;
- if(delta > 0)
- break;
- if(t->period > 0) {
- // leave in heap but adjust next time to fire
- t->when += t->period * (1 + -delta/t->period);
- siftdown(0);
- } else {
- // remove from heap
- timers.t[0] = timers.t[--timers.len];
- timers.t[0]->i = 0;
- siftdown(0);
- t->i = -1; // mark as removed
- }
- f = (void*)t->fv->fn;
- arg = t->arg;
- runtime·unlock(&timers);
- if(raceenabled)
- runtime·raceacquire(t);
- f(now, arg);
-
- // clear f and arg to avoid leak while sleeping for next timer
- f = nil;
- USED(f);
- arg.type = nil;
- arg.data = nil;
- USED(&arg);
-
- runtime·lock(&timers);
- }
- if(delta < 0) {
- // No timers left - put goroutine to sleep.
- timers.rescheduling = true;
- g->isbackground = true;
- runtime·parkunlock(&timers, "timer goroutine (idle)");
- g->isbackground = false;
- continue;
- }
- // At least one timer pending. Sleep until then.
- timers.sleeping = true;
- runtime·noteclear(&timers.waitnote);
- runtime·unlock(&timers);
- runtime·notetsleepg(&timers.waitnote, delta);
- }
-}
-
-// heap maintenance algorithms.
-
-static void
-siftup(int32 i)
-{
- int32 p;
- int64 when;
- Timer **t, *tmp;
-
- t = timers.t;
- when = t[i]->when;
- tmp = t[i];
- while(i > 0) {
- p = (i-1)/4; // parent
- if(when >= t[p]->when)
- break;
- t[i] = t[p];
- t[i]->i = i;
- t[p] = tmp;
- tmp->i = p;
- i = p;
- }
-}
-
-static void
-siftdown(int32 i)
-{
- int32 c, c3, len;
- int64 when, w, w3;
- Timer **t, *tmp;
-
- t = timers.t;
- len = timers.len;
- when = t[i]->when;
- tmp = t[i];
- for(;;) {
- c = i*4 + 1; // left child
- c3 = c + 2; // mid child
- if(c >= len) {
- break;
- }
- w = t[c]->when;
- if(c+1 < len && t[c+1]->when < w) {
- w = t[c+1]->when;
- c++;
- }
- if(c3 < len) {
- w3 = t[c3]->when;
- if(c3+1 < len && t[c3+1]->when < w3) {
- w3 = t[c3+1]->when;
- c3++;
- }
- if(w3 < w) {
- w = w3;
- c = c3;
- }
- }
- if(w >= when)
- break;
- t[i] = t[c];
- t[i]->i = i;
- t[c] = tmp;
- tmp->i = c;
- i = c;
- }
-}
-
-static void
-dumptimers(int8 *msg)
-{
- Timer *t;
- int32 i;
-
- runtime·printf("timers: %s\n", msg);
- for(i = 0; i < timers.len; i++) {
- t = timers.t[i];
- runtime·printf("\t%d\t%p:\ti %d when %D period %D fn %p\n",
- i, t, t->i, t->when, t->period, t->fv->fn);
- }
- runtime·printf("\n");
-}
diff --git a/src/pkg/runtime/tls_arm.s b/src/pkg/runtime/tls_arm.s
index 37edfa968e..7a247ab195 100644
--- a/src/pkg/runtime/tls_arm.s
+++ b/src/pkg/runtime/tls_arm.s
@@ -4,7 +4,7 @@
#include "zasm_GOOS_GOARCH.h"
#include "funcdata.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
// We have to resort to TLS variable to save g(R10).
// One reason is that external code might trigger
@@ -22,12 +22,14 @@
// ARM code that will overwrite those registers.
// NOTE: runtime.gogo assumes that R1 is preserved by this function.
// runtime.mcall assumes this function only clobbers R0 and R11.
-TEXT runtime·save_g(SB),NOSPLIT,$0
+// Returns with g in R0.
+TEXT runtime·save_g(SB),NOSPLIT,$-4
#ifdef GOOS_nacl
// nothing to do as nacl/arm does not use TLS at all.
+ MOVW g, R0 // preserve R0 across call to setg<>
RET
#endif
- MRC 15, 0, R0, C13, C0, 3 // fetch TLS base pointer
+ MRC 15, 0, R0, C13, C0, 3 // fetch TLS base pointer
// $runtime.tlsg(SB) is a special linker symbol.
// It is the offset from the TLS base pointer to our
// thread-local storage for g.
@@ -38,6 +40,7 @@ TEXT runtime·save_g(SB),NOSPLIT,$0
#endif
ADD R11, R0
MOVW g, 0(R0)
+ MOVW g, R0 // preserve R0 across call to setg<>
RET
// load_g loads the g register from pthread-provided
diff --git a/src/pkg/runtime/traceback.go b/src/pkg/runtime/traceback.go
new file mode 100644
index 0000000000..ec7be28dc0
--- /dev/null
+++ b/src/pkg/runtime/traceback.go
@@ -0,0 +1,639 @@
+// 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"
+
+// The code in this file implements stack trace walking for all architectures.
+// The most important fact about a given architecture is whether it uses a link register.
+// On systems with link registers, the prologue for a non-leaf function stores the
+// incoming value of LR at the bottom of the newly allocated stack frame.
+// On systems without link registers, the architecture pushes a return PC during
+// the call instruction, so the return PC ends up above the stack frame.
+// In this file, the return PC is always called LR, no matter how it was found.
+//
+// To date, the opposite of a link register architecture is an x86 architecture.
+// This code may need to change if some other kind of non-link-register
+// architecture comes along.
+//
+// The other important fact is the size of a pointer: on 32-bit systems the LR
+// takes up only 4 bytes on the stack, while on 64-bit systems it takes up 8 bytes.
+// Typically this is ptrSize.
+//
+// As an exception, amd64p32 has ptrSize == 4 but the CALL instruction still
+// stores an 8-byte return PC onto the stack. To accommodate this, we use regSize
+// as the size of the architecture-pushed return PC.
+//
+// usesLR is defined below. ptrSize and regSize are defined in stubs.go.
+
+const usesLR = GOARCH != "amd64" && GOARCH != "amd64p32" && GOARCH != "386"
+
+var (
+ deferprocPC = funcPC(deferproc)
+ goexitPC = funcPC(goexit)
+ jmpdeferPC = funcPC(jmpdefer)
+ lessstackPC = funcPC(lessstack)
+ mcallPC = funcPC(mcall)
+ morestackPC = funcPC(morestack)
+ mstartPC = funcPC(mstart)
+ newprocPC = funcPC(newproc)
+ newstackPC = funcPC(newstack)
+ rt0_goPC = funcPC(rt0_go)
+ sigpanicPC = funcPC(sigpanic)
+
+ externalthreadhandlerp uintptr // initialized elsewhere
+)
+
+// System-specific hook. See traceback_windows.go
+var systraceback func(*_func, *stkframe, *g, bool, func(*stkframe, unsafe.Pointer) bool, unsafe.Pointer) (changed, aborted bool)
+
+// Generic traceback. Handles runtime stack prints (pcbuf == nil),
+// the runtime.Callers function (pcbuf != nil), as well as the garbage
+// collector (callback != nil). A little clunky to merge these, but avoids
+// duplicating the code and all its subtlety.
+func gentraceback(pc0 uintptr, sp0 uintptr, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max int, callback func(*stkframe, unsafe.Pointer) bool, v unsafe.Pointer, printall bool) int {
+ g := getg()
+ gotraceback := gotraceback(nil)
+ if pc0 == ^uintptr(0) && sp0 == ^uintptr(0) { // Signal to fetch saved values from gp.
+ if gp.syscallstack != 0 {
+ pc0 = gp.syscallpc
+ sp0 = gp.syscallsp
+ if usesLR {
+ lr0 = 0
+ }
+ } else {
+ pc0 = gp.sched.pc
+ sp0 = gp.sched.sp
+ if usesLR {
+ lr0 = gp.sched.lr
+ }
+ }
+ }
+
+ nprint := 0
+ var frame stkframe
+ frame.pc = pc0
+ frame.sp = sp0
+ if usesLR {
+ frame.lr = lr0
+ }
+ waspanic := false
+ wasnewproc := false
+ printing := pcbuf == nil && callback == nil
+ panic := gp._panic
+ _defer := gp._defer
+
+ for _defer != nil && uintptr(_defer.argp) == _NoArgs {
+ _defer = _defer.link
+ }
+ for panic != nil && panic._defer == nil {
+ panic = panic.link
+ }
+
+ // If the PC is zero, it's likely a nil function call.
+ // Start in the caller's frame.
+ if frame.pc == 0 {
+ if usesLR {
+ frame.pc = *(*uintptr)(unsafe.Pointer(frame.sp))
+ frame.lr = 0
+ } else {
+ frame.pc = uintptr(*(*uintreg)(unsafe.Pointer(frame.sp)))
+ frame.sp += regSize
+ }
+ }
+
+ f := findfunc(frame.pc)
+ if f == nil {
+ if callback != nil {
+ print("runtime: unknown pc ", hex(frame.pc), "\n")
+ gothrow("unknown pc")
+ }
+ return 0
+ }
+ frame.fn = f
+
+ n := 0
+ stk := (*stktop)(unsafe.Pointer(gp.stackbase))
+ for n < max {
+ // Typically:
+ // pc is the PC of the running function.
+ // sp is the stack pointer at that program counter.
+ // fp is the frame pointer (caller's stack pointer) at that program counter, or nil if unknown.
+ // stk is the stack containing sp.
+ // The caller's program counter is lr, unless lr is zero, in which case it is *(uintptr*)sp.
+ if frame.pc == lessstackPC {
+ // Hit top of stack segment. Unwind to next segment.
+ frame.pc = stk.gobuf.pc
+ frame.sp = stk.gobuf.sp
+ frame.lr = 0
+ frame.fp = 0
+ if printing && showframe(nil, gp) {
+ print("----- stack segment boundary -----\n")
+ }
+ stk = (*stktop)(unsafe.Pointer(stk.stackbase))
+ f = findfunc(frame.pc)
+ if f == nil {
+ print("runtime: unknown pc ", hex(frame.pc), " after stack split\n")
+ if callback != nil {
+ gothrow("unknown pc")
+ }
+ }
+ frame.fn = f
+ continue
+ }
+ f = frame.fn
+
+ // Hook for handling Windows exception handlers. See traceback_windows.go.
+ if systraceback != nil {
+ changed, aborted := systraceback(f, (*stkframe)(noescape(unsafe.Pointer(&frame))), gp, printing, callback, v)
+ if aborted {
+ return n
+ }
+ if changed {
+ continue
+ }
+ }
+
+ // Found an actual function.
+ // Derive frame pointer and link register.
+ if frame.fp == 0 {
+ frame.fp = frame.sp + uintptr(funcspdelta(f, frame.pc))
+ if !usesLR {
+ // On x86, call instruction pushes return PC before entering new function.
+ frame.fp += regSize
+ }
+ }
+ var flr *_func
+ if topofstack(f) {
+ frame.lr = 0
+ flr = nil
+ } else if usesLR && f.entry == jmpdeferPC {
+ // jmpdefer modifies SP/LR/PC non-atomically.
+ // If a profiling interrupt arrives during jmpdefer,
+ // the stack unwind may see a mismatched register set
+ // and get confused. Stop if we see PC within jmpdefer
+ // to avoid that confusion.
+ // See golang.org/issue/8153.
+ if callback != nil {
+ gothrow("traceback_arm: found jmpdefer when tracing with callback")
+ }
+ frame.lr = 0
+ } else {
+ if usesLR {
+ if n == 0 && frame.sp < frame.fp || frame.lr == 0 {
+ frame.lr = *(*uintptr)(unsafe.Pointer(frame.sp))
+ }
+ } else {
+ if frame.lr == 0 {
+ frame.lr = uintptr(*(*uintreg)(unsafe.Pointer(frame.fp - regSize)))
+ }
+ }
+ flr = findfunc(frame.lr)
+ if flr == nil {
+ // This happens if you get a profiling interrupt at just the wrong time.
+ // In that context it is okay to stop early.
+ // But if callback is set, we're doing a garbage collection and must
+ // get everything, so crash loudly.
+ if callback != nil {
+ print("runtime: unexpected return pc for ", gofuncname(f), " called from ", hex(frame.lr), "\n")
+ gothrow("unknown caller pc")
+ }
+ }
+ }
+
+ frame.varp = frame.fp
+ if !usesLR {
+ // On x86, call instruction pushes return PC before entering new function.
+ frame.varp -= regSize
+ }
+
+ // Derive size of arguments.
+ // Most functions have a fixed-size argument block,
+ // so we can use metadata about the function f.
+ // Not all, though: there are some variadic functions
+ // in package runtime and reflect, and for those we use call-specific
+ // metadata recorded by f's caller.
+ if callback != nil || printing {
+ frame.argp = frame.fp
+ if usesLR {
+ frame.argp += ptrSize
+ }
+ if f.args != _ArgsSizeUnknown {
+ frame.arglen = uintptr(f.args)
+ } else if flr == nil {
+ frame.arglen = 0
+ } else if frame.lr == lessstackPC {
+ frame.arglen = uintptr(stk.argsize)
+ } else {
+ i := funcarglen(flr, frame.lr)
+ if i >= 0 {
+ frame.arglen = uintptr(i)
+ } else {
+ var tmp string
+ if flr != nil {
+ tmp = gofuncname(flr)
+ } else {
+ tmp = "?"
+ }
+ print("runtime: unknown argument frame size for ", gofuncname(f), " called from ", hex(frame.lr), " [", tmp, "]\n")
+ if callback != nil {
+ gothrow("invalid stack")
+ }
+ frame.arglen = 0
+ }
+ }
+ }
+
+ // Determine function SP where deferproc would find its arguments.
+ var sparg uintptr
+ if usesLR {
+ // On link register architectures, that's the standard bottom-of-stack plus 1 word
+ // for the saved LR. If the previous frame was a direct call to newproc/deferproc,
+ // however, the SP is three words lower than normal.
+ // If the function has no frame at all - perhaps it just started, or perhaps
+ // it is a leaf with no local variables - then we cannot possibly find its
+ // SP in a defer, and we might confuse its SP for its caller's SP, so
+ // leave sparg=0 in that case.
+ if frame.fp != frame.sp {
+ sparg = frame.sp + regSize
+ if wasnewproc {
+ sparg += 3 * regSize
+ }
+ }
+ } else {
+ // On x86 that's the standard bottom-of-stack, so SP exactly.
+ // If the previous frame was a direct call to newproc/deferproc, however,
+ // the SP is two words lower than normal.
+ sparg = frame.sp
+ if wasnewproc {
+ sparg += 2 * ptrSize
+ }
+ }
+
+ // Determine frame's 'continuation PC', where it can continue.
+ // Normally this is the return address on the stack, but if sigpanic
+ // is immediately below this function on the stack, then the frame
+ // stopped executing due to a trap, and frame.pc is probably not
+ // a safe point for looking up liveness information. In this panicking case,
+ // the function either doesn't return at all (if it has no defers or if the
+ // defers do not recover) or it returns from one of the calls to
+ // deferproc a second time (if the corresponding deferred func recovers).
+ // It suffices to assume that the most recent deferproc is the one that
+ // returns; everything live at earlier deferprocs is still live at that one.
+ frame.continpc = frame.pc
+ if waspanic {
+ if panic != nil && panic._defer.argp == sparg {
+ frame.continpc = panic._defer.pc
+ } else if _defer != nil && _defer.argp == sparg {
+ frame.continpc = _defer.pc
+ } else {
+ frame.continpc = 0
+ }
+ }
+
+ // Unwind our local panic & defer stacks past this frame.
+ for panic != nil && (panic._defer == nil || panic._defer.argp == sparg || panic._defer.argp == _NoArgs) {
+ panic = panic.link
+ }
+ for _defer != nil && (_defer.argp == sparg || _defer.argp == _NoArgs) {
+ _defer = _defer.link
+ }
+
+ if skip > 0 {
+ skip--
+ goto skipped
+ }
+
+ if pcbuf != nil {
+ (*[1 << 20]uintptr)(unsafe.Pointer(pcbuf))[n] = frame.pc
+ }
+ if callback != nil {
+ if !callback((*stkframe)(noescape(unsafe.Pointer(&frame))), v) {
+ return n
+ }
+ }
+ if printing {
+ if printall || showframe(f, gp) {
+ // Print during crash.
+ // main(0x1, 0x2, 0x3)
+ // /home/rsc/go/src/runtime/x.go:23 +0xf
+ //
+ tracepc := frame.pc // back up to CALL instruction for funcline.
+ if n > 0 && frame.pc > f.entry && !waspanic {
+ tracepc--
+ }
+ print(gofuncname(f), "(")
+ argp := (*[100]uintptr)(unsafe.Pointer(frame.argp))
+ for i := uintptr(0); i < frame.arglen/ptrSize; i++ {
+ if i >= 10 {
+ print(", ...")
+ break
+ }
+ if i != 0 {
+ print(", ")
+ }
+ print(hex(argp[i]))
+ }
+ print(")\n")
+ var file string
+ line := funcline(f, tracepc, &file)
+ print("\t", file, ":", line)
+ if frame.pc > f.entry {
+ print(" +", hex(frame.pc-f.entry))
+ }
+ if g.m.throwing > 0 && gp == g.m.curg || gotraceback >= 2 {
+ print(" fp=", hex(frame.fp), " sp=", hex(frame.sp))
+ }
+ print("\n")
+ nprint++
+ }
+ }
+ n++
+
+ skipped:
+ waspanic = f.entry == sigpanicPC
+ wasnewproc = f.entry == newprocPC || f.entry == deferprocPC
+
+ // Do not unwind past the bottom of the stack.
+ if flr == nil {
+ break
+ }
+
+ // Unwind to next frame.
+ frame.fn = flr
+ frame.pc = frame.lr
+ frame.lr = 0
+ frame.sp = frame.fp
+ frame.fp = 0
+
+ // On link register architectures, sighandler saves the LR on stack
+ // before faking a call to sigpanic.
+ if usesLR && waspanic {
+ x := *(*uintptr)(unsafe.Pointer(frame.sp))
+ frame.sp += ptrSize
+ f = findfunc(frame.pc)
+ frame.fn = f
+ if f == nil {
+ frame.pc = x
+ } else if f.frame == 0 {
+ frame.lr = x
+ }
+ }
+ }
+
+ if pcbuf == nil && callback == nil {
+ n = nprint
+ }
+
+ // If callback != nil, we're being called to gather stack information during
+ // garbage collection or stack growth. In that context, require that we used
+ // up the entire defer stack. If not, then there is a bug somewhere and the
+ // garbage collection or stack growth may not have seen the correct picture
+ // of the stack. Crash now instead of silently executing the garbage collection
+ // or stack copy incorrectly and setting up for a mysterious crash later.
+ //
+ // Note that panic != nil is okay here: there can be leftover panics,
+ // because the defers on the panic stack do not nest in frame order as
+ // they do on the defer stack. If you have:
+ //
+ // frame 1 defers d1
+ // frame 2 defers d2
+ // frame 3 defers d3
+ // frame 4 panics
+ // frame 4's panic starts running defers
+ // frame 5, running d3, defers d4
+ // frame 5 panics
+ // frame 5's panic starts running defers
+ // frame 6, running d4, garbage collects
+ // frame 6, running d2, garbage collects
+ //
+ // During the execution of d4, the panic stack is d4 -> d3, which
+ // is nested properly, and we'll treat frame 3 as resumable, because we
+ // can find d3. (And in fact frame 3 is resumable. If d4 recovers
+ // and frame 5 continues running, d3, d3 can recover and we'll
+ // resume execution in (returning from) frame 3.)
+ //
+ // During the execution of d2, however, the panic stack is d2 -> d3,
+ // which is inverted. The scan will match d2 to frame 2 but having
+ // d2 on the stack until then means it will not match d3 to frame 3.
+ // This is okay: if we're running d2, then all the defers after d2 have
+ // completed and their corresponding frames are dead. Not finding d3
+ // for frame 3 means we'll set frame 3's continpc == 0, which is correct
+ // (frame 3 is dead). At the end of the walk the panic stack can thus
+ // contain defers (d3 in this case) for dead frames. The inversion here
+ // always indicates a dead frame, and the effect of the inversion on the
+ // scan is to hide those dead frames, so the scan is still okay:
+ // what's left on the panic stack are exactly (and only) the dead frames.
+ //
+ // We require callback != nil here because only when callback != nil
+ // do we know that gentraceback is being called in a "must be correct"
+ // context as opposed to a "best effort" context. The tracebacks with
+ // callbacks only happen when everything is stopped nicely.
+ // At other times, such as when gathering a stack for a profiling signal
+ // or when printing a traceback during a crash, everything may not be
+ // stopped nicely, and the stack walk may not be able to complete.
+ // It's okay in those situations not to use up the entire defer stack:
+ // incomplete information then is still better than nothing.
+ if callback != nil && n < max && _defer != nil {
+ if _defer != nil {
+ print("runtime: g", gp.goid, ": leftover defer argp=", hex(_defer.argp), " pc=", hex(_defer.pc), "\n")
+ }
+ if panic != nil {
+ print("runtime: g", gp.goid, ": leftover panic argp=", hex(panic._defer.argp), " pc=", hex(panic._defer.pc), "\n")
+ }
+ for _defer = gp._defer; _defer != nil; _defer = _defer.link {
+ print("\tdefer ", _defer, " argp=", hex(_defer.argp), " pc=", hex(_defer.pc), "\n")
+ }
+ for panic = gp._panic; panic != nil; panic = panic.link {
+ print("\tpanic ", panic, " defer ", panic._defer)
+ if panic._defer != nil {
+ print(" argp=", hex(panic._defer.argp), " pc=", hex(panic._defer.pc))
+ }
+ print("\n")
+ }
+ gothrow("traceback has leftover defers or panics")
+ }
+
+ return n
+}
+
+func printcreatedby(gp *g) {
+ // Show what created goroutine, except main goroutine (goid 1).
+ pc := gp.gopc
+ f := findfunc(pc)
+ if f != nil && showframe(f, gp) && gp.goid != 1 {
+ print("created by ", gofuncname(f), "\n")
+ tracepc := pc // back up to CALL instruction for funcline.
+ if pc > f.entry {
+ tracepc -= _PCQuantum
+ }
+ var file string
+ line := funcline(f, tracepc, &file)
+ print("\t", file, ":", line)
+ if pc > f.entry {
+ print(" +", hex(pc-f.entry))
+ }
+ print("\n")
+ }
+}
+
+func traceback(pc uintptr, sp uintptr, lr uintptr, gp *g) {
+ var n int
+ if readgstatus(gp)&^_Gscan == _Gsyscall {
+ // Override signal registers if blocked in system call.
+ pc = gp.syscallpc
+ sp = gp.syscallsp
+ }
+ // Print traceback. By default, omits runtime frames.
+ // If that means we print nothing at all, repeat forcing all frames printed.
+ n = gentraceback(pc, sp, 0, gp, 0, nil, _TracebackMaxFrames, nil, nil, false)
+ if n == 0 {
+ n = gentraceback(pc, sp, 0, gp, 0, nil, _TracebackMaxFrames, nil, nil, true)
+ }
+ if n == _TracebackMaxFrames {
+ print("...additional frames elided...\n")
+ }
+ printcreatedby(gp)
+}
+
+func callers(skip int, pcbuf *uintptr, m int) int {
+ sp := getcallersp(unsafe.Pointer(&skip))
+ pc := uintptr(getcallerpc(unsafe.Pointer(&skip)))
+ return gentraceback(pc, sp, 0, getg(), skip, pcbuf, m, nil, nil, false)
+}
+
+func gcallers(gp *g, skip int, pcbuf *uintptr, m int) int {
+ return gentraceback(^uintptr(0), ^uintptr(0), 0, gp, skip, pcbuf, m, nil, nil, false)
+}
+
+func showframe(f *_func, gp *g) bool {
+ g := getg()
+ if g.m.throwing > 0 && gp != nil && (gp == g.m.curg || gp == g.m.caughtsig) {
+ return true
+ }
+ traceback := gotraceback(nil)
+ name := gostringnocopy(funcname(f))
+
+ // Special case: always show runtime.panic frame, so that we can
+ // see where a panic started in the middle of a stack trace.
+ // See golang.org/issue/5832.
+ if name == "runtime.panic" {
+ return true
+ }
+
+ return traceback > 1 || f != nil && contains(name, ".") && !hasprefix(name, "runtime.")
+}
+
+func contains(s, t string) bool {
+ if len(t) == 0 {
+ return true
+ }
+ for i := 0; i < len(s); i++ {
+ if s[i] == t[0] && hasprefix(s[i:], t) {
+ return true
+ }
+ }
+ return false
+}
+
+func hasprefix(s, t string) bool {
+ return len(s) >= len(t) && s[:len(t)] == t
+}
+
+var gStatusStrings = [...]string{
+ _Gidle: "idle",
+ _Grunnable: "runnable",
+ _Grunning: "running",
+ _Gsyscall: "syscall",
+ _Gwaiting: "waiting",
+ _Gdead: "dead",
+ _Genqueue: "enqueue",
+ _Gcopystack: "copystack",
+}
+
+var gScanStatusStrings = [...]string{
+ 0: "scan",
+ _Grunnable: "scanrunnable",
+ _Grunning: "scanrunning",
+ _Gsyscall: "scansyscall",
+ _Gwaiting: "scanwaiting",
+ _Gdead: "scandead",
+ _Genqueue: "scanenqueue",
+}
+
+func goroutineheader(gp *g) {
+ gpstatus := readgstatus(gp)
+
+ // Basic string status
+ var status string
+ if 0 <= gpstatus && gpstatus < uint32(len(gStatusStrings)) {
+ status = gStatusStrings[gpstatus]
+ } else if gpstatus&_Gscan != 0 && 0 <= gpstatus&^_Gscan && gpstatus&^_Gscan < uint32(len(gStatusStrings)) {
+ status = gStatusStrings[gpstatus&^_Gscan]
+ } else {
+ status = "???"
+ }
+
+ // Override.
+ if (gpstatus == _Gwaiting || gpstatus == _Gscanwaiting) && gp.waitreason != "" {
+ status = gp.waitreason
+ }
+
+ // approx time the G is blocked, in minutes
+ var waitfor int64
+ gpstatus &^= _Gscan // drop the scan bit
+ if (gpstatus == _Gwaiting || gpstatus == _Gsyscall) && gp.waitsince != 0 {
+ waitfor = (nanotime() - gp.waitsince) / 60e9
+ }
+ print("goroutine ", gp.goid, " [", status)
+ if waitfor >= 1 {
+ print(", ", waitfor, " minutes")
+ }
+ if gp.lockedm != nil {
+ print(", locked to thread")
+ }
+ print("]:\n")
+}
+
+func tracebackothers(me *g) {
+ level := gotraceback(nil)
+
+ // Show the current goroutine first, if we haven't already.
+ g := getg()
+ gp := g.m.curg
+ if gp != nil && gp != me {
+ print("\n")
+ goroutineheader(gp)
+ traceback(^uintptr(0), ^uintptr(0), 0, gp)
+ }
+
+ lock(&allglock)
+ for _, gp := range allgs {
+ if gp == me || gp == g.m.curg || readgstatus(gp) == _Gdead || gp.issystem && level < 2 {
+ continue
+ }
+ print("\n")
+ goroutineheader(gp)
+ if readgstatus(gp)&^_Gscan == _Grunning {
+ print("\tgoroutine running on other thread; stack unavailable\n")
+ printcreatedby(gp)
+ } else {
+ traceback(^uintptr(0), ^uintptr(0), 0, gp)
+ }
+ }
+ unlock(&allglock)
+}
+
+// Does f mark the top of a goroutine stack?
+func topofstack(f *_func) bool {
+ pc := f.entry
+ return pc == goexitPC ||
+ pc == mstartPC ||
+ pc == mcallPC ||
+ pc == morestackPC ||
+ pc == lessstackPC ||
+ pc == rt0_goPC ||
+ externalthreadhandlerp != 0 && pc == externalthreadhandlerp
+}
diff --git a/src/pkg/runtime/traceback_windows.go b/src/pkg/runtime/traceback_windows.go
new file mode 100644
index 0000000000..89dc1336e3
--- /dev/null
+++ b/src/pkg/runtime/traceback_windows.go
@@ -0,0 +1,63 @@
+// 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"
+
+// sigtrampPC is the PC at the beginning of the jmpdefer assembly function.
+// The traceback needs to recognize it on link register architectures.
+var sigtrampPC uintptr
+
+func sigtramp()
+
+func init() {
+ sigtrampPC = funcPC(sigtramp)
+ systraceback = traceback_windows
+}
+
+func traceback_windows(f *_func, frame *stkframe, gp *g, printing bool, callback func(*stkframe, unsafe.Pointer) bool, v unsafe.Pointer) (changed, aborted bool) {
+ // The main traceback thinks it has found a function. Check this.
+
+ // Windows exception handlers run on the actual g stack (there is room
+ // dedicated to this below the usual "bottom of stack"), not on a separate
+ // stack. As a result, we have to be able to unwind past the exception
+ // handler when called to unwind during stack growth inside the handler.
+ // Recognize the frame at the call to sighandler in sigtramp and unwind
+ // using the context argument passed to the call. This is awful.
+ if f != nil && f.entry == sigtrampPC && frame.pc > f.entry {
+ var r *context
+ // Invoke callback so that stack copier sees an uncopyable frame.
+ if callback != nil {
+ frame.continpc = frame.pc
+ frame.argp = 0
+ frame.arglen = 0
+ if !callback(frame, v) {
+ aborted = true
+ return
+ }
+ }
+ r = (*context)(unsafe.Pointer(frame.sp + ptrSize))
+ frame.pc = contextPC(r)
+ frame.sp = contextSP(r)
+ frame.lr = 0
+ frame.fp = 0
+ frame.fn = nil
+ if printing && showframe(nil, gp) {
+ print("----- exception handler -----\n")
+ }
+ f = findfunc(frame.pc)
+ if f == nil {
+ print("runtime: unknown pc ", hex(frame.pc), " after exception handler\n")
+ if callback != nil {
+ gothrow("unknown pc")
+ }
+ }
+ frame.fn = f
+ changed = true
+ return
+ }
+
+ return
+}
diff --git a/src/pkg/runtime/traceback_x86.c b/src/pkg/runtime/traceback_x86.c
deleted file mode 100644
index a88e9372db..0000000000
--- a/src/pkg/runtime/traceback_x86.c
+++ /dev/null
@@ -1,436 +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 amd64 amd64p32 386
-
-#include "runtime.h"
-#include "arch_GOARCH.h"
-#include "malloc.h"
-#include "funcdata.h"
-#ifdef GOOS_windows
-#include "defs_GOOS_GOARCH.h"
-#endif
-
-void runtime·sigpanic(void);
-void runtime·newproc(void);
-void runtime·deferproc(void);
-
-#ifdef GOOS_windows
-void runtime·sigtramp(void);
-#endif
-
-// This code is also used for the 386 tracebacks.
-// Use uintptr for an appropriate word-sized integer.
-
-// Generic traceback. Handles runtime stack prints (pcbuf == nil),
-// the runtime.Callers function (pcbuf != nil), as well as the garbage
-// collector (callback != nil). A little clunky to merge these, but avoids
-// duplicating the code and all its subtlety.
-int32
-runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip, uintptr *pcbuf, int32 max, bool (*callback)(Stkframe*, void*), void *v, bool printall)
-{
- int32 i, n, nprint, line, gotraceback;
- uintptr tracepc, sparg;
- bool waspanic, wasnewproc, printing;
- Func *f, *flr;
- Stkframe frame;
- Stktop *stk;
- String file;
- Panic *panic;
- Defer *defer;
-
- USED(lr0);
-
- gotraceback = runtime·gotraceback(nil);
-
- if(pc0 == ~(uintptr)0 && sp0 == ~(uintptr)0) { // Signal to fetch saved values from gp.
- if(gp->syscallstack != (uintptr)nil) {
- pc0 = gp->syscallpc;
- sp0 = gp->syscallsp;
- } else {
- pc0 = gp->sched.pc;
- sp0 = gp->sched.sp;
- }
- }
-
- nprint = 0;
- runtime·memclr((byte*)&frame, sizeof frame);
- frame.pc = pc0;
- frame.sp = sp0;
- waspanic = false;
- wasnewproc = false;
- printing = pcbuf==nil && callback==nil;
- panic = gp->panic;
- defer = gp->defer;
-
- while(defer != nil && defer->argp == NoArgs)
- defer = defer->link;
- while(panic != nil && panic->defer == nil)
- panic = panic->link;
-
- // If the PC is zero, it's likely a nil function call.
- // Start in the caller's frame.
- if(frame.pc == 0) {
- frame.pc = *(uintptr*)frame.sp;
- frame.sp += sizeof(uintreg);
- }
-
- f = runtime·findfunc(frame.pc);
- if(f == nil) {
- if(callback != nil) {
- runtime·printf("runtime: unknown pc %p\n", frame.pc);
- runtime·throw("unknown pc");
- }
- return 0;
- }
- frame.fn = f;
-
- n = 0;
- stk = (Stktop*)gp->stackbase;
- while(n < max) {
- // Typically:
- // pc is the PC of the running function.
- // sp is the stack pointer at that program counter.
- // fp is the frame pointer (caller's stack pointer) at that program counter, or nil if unknown.
- // stk is the stack containing sp.
- // The caller's program counter is lr, unless lr is zero, in which case it is *(uintptr*)sp.
-
- if(frame.pc == (uintptr)runtime·lessstack) {
- // Hit top of stack segment. Unwind to next segment.
- frame.pc = stk->gobuf.pc;
- frame.sp = stk->gobuf.sp;
- frame.lr = 0;
- frame.fp = 0;
- frame.fn = nil;
- if(printing && runtime·showframe(nil, gp))
- runtime·printf("----- stack segment boundary -----\n");
- stk = (Stktop*)stk->stackbase;
-
- f = runtime·findfunc(frame.pc);
- if(f == nil) {
- runtime·printf("runtime: unknown pc %p after stack split\n", frame.pc);
- if(callback != nil)
- runtime·throw("unknown pc");
- }
- frame.fn = f;
- continue;
- }
-
- f = frame.fn;
-
-#ifdef GOOS_windows
- // Windows exception handlers run on the actual g stack (there is room
- // dedicated to this below the usual "bottom of stack"), not on a separate
- // stack. As a result, we have to be able to unwind past the exception
- // handler when called to unwind during stack growth inside the handler.
- // Recognize the frame at the call to sighandler in sigtramp and unwind
- // using the context argument passed to the call. This is awful.
- if(f != nil && f->entry == (uintptr)runtime·sigtramp && frame.pc > f->entry) {
- Context *r;
-
- // Invoke callback so that stack copier sees an uncopyable frame.
- if(callback != nil) {
- frame.continpc = frame.pc;
- frame.argp = nil;
- frame.arglen = 0;
- if(!callback(&frame, v))
- return n;
- }
- r = (Context*)((uintptr*)frame.sp)[1];
-#ifdef GOARCH_amd64
- frame.pc = r->Rip;
- frame.sp = r->Rsp;
-#else
- frame.pc = r->Eip;
- frame.sp = r->Esp;
-#endif
- frame.lr = 0;
- frame.fp = 0;
- frame.fn = nil;
- if(printing && runtime·showframe(nil, gp))
- runtime·printf("----- exception handler -----\n");
- f = runtime·findfunc(frame.pc);
- if(f == nil) {
- runtime·printf("runtime: unknown pc %p after exception handler\n", frame.pc);
- if(callback != nil)
- runtime·throw("unknown pc");
- }
- frame.fn = f;
- continue;
- }
-#endif
-
- // Found an actual function.
- // Derive frame pointer and link register.
- if(frame.fp == 0) {
- frame.fp = frame.sp + runtime·funcspdelta(f, frame.pc);
- frame.fp += sizeof(uintreg); // caller PC
- }
- if(runtime·topofstack(f)) {
- frame.lr = 0;
- flr = nil;
- } else {
- if(frame.lr == 0)
- frame.lr = ((uintreg*)frame.fp)[-1];
- flr = runtime·findfunc(frame.lr);
- if(flr == nil) {
- runtime·printf("runtime: unexpected return pc for %s called from %p\n", runtime·funcname(f), frame.lr);
- if(callback != nil)
- runtime·throw("unknown caller pc");
- }
- }
-
- frame.varp = (byte*)frame.fp - sizeof(uintreg);
-
- // Derive size of arguments.
- // Most functions have a fixed-size argument block,
- // so we can use metadata about the function f.
- // Not all, though: there are some variadic functions
- // in package runtime and reflect, and for those we use call-specific
- // metadata recorded by f's caller.
- if(callback != nil || printing) {
- frame.argp = (byte*)frame.fp;
- if(f->args != ArgsSizeUnknown)
- frame.arglen = f->args;
- else if(flr == nil)
- frame.arglen = 0;
- else if(frame.lr == (uintptr)runtime·lessstack)
- frame.arglen = stk->argsize;
- else if((i = runtime·funcarglen(flr, frame.lr)) >= 0)
- frame.arglen = i;
- else {
- runtime·printf("runtime: unknown argument frame size for %s called from %p [%s]\n",
- runtime·funcname(f), frame.lr, flr ? runtime·funcname(flr) : "?");
- if(callback != nil)
- runtime·throw("invalid stack");
- frame.arglen = 0;
- }
- }
-
- // Determine function SP where deferproc would find its arguments.
- // On x86 that's just the standard bottom-of-stack, so SP exactly.
- // If the previous frame was a direct call to newproc/deferproc, however,
- // the SP is two words lower than normal.
- sparg = frame.sp;
- if(wasnewproc)
- sparg += 2*sizeof(uintptr);
-
- // Determine frame's 'continuation PC', where it can continue.
- // Normally this is the return address on the stack, but if sigpanic
- // is immediately below this function on the stack, then the frame
- // stopped executing due to a trap, and frame.pc is probably not
- // a safe point for looking up liveness information. In this panicking case,
- // the function either doesn't return at all (if it has no defers or if the
- // defers do not recover) or it returns from one of the calls to
- // deferproc a second time (if the corresponding deferred func recovers).
- // It suffices to assume that the most recent deferproc is the one that
- // returns; everything live at earlier deferprocs is still live at that one.
- frame.continpc = frame.pc;
- if(waspanic) {
- if(panic != nil && panic->defer->argp == (byte*)sparg)
- frame.continpc = (uintptr)panic->defer->pc;
- else if(defer != nil && defer->argp == (byte*)sparg)
- frame.continpc = (uintptr)defer->pc;
- else
- frame.continpc = 0;
- }
-
- // Unwind our local panic & defer stacks past this frame.
- while(panic != nil && (panic->defer == nil || panic->defer->argp == (byte*)sparg || panic->defer->argp == NoArgs))
- panic = panic->link;
- while(defer != nil && (defer->argp == (byte*)sparg || defer->argp == NoArgs))
- defer = defer->link;
-
- if(skip > 0) {
- skip--;
- goto skipped;
- }
-
- if(pcbuf != nil)
- pcbuf[n] = frame.pc;
- if(callback != nil) {
- if(!callback(&frame, v))
- return n;
- }
- if(printing) {
- if(printall || runtime·showframe(f, gp)) {
- // Print during crash.
- // main(0x1, 0x2, 0x3)
- // /home/rsc/go/src/runtime/x.go:23 +0xf
- //
- tracepc = frame.pc; // back up to CALL instruction for funcline.
- if(n > 0 && frame.pc > f->entry && !waspanic)
- tracepc--;
- runtime·printf("%s(", runtime·funcname(f));
- for(i = 0; i < frame.arglen/sizeof(uintptr); i++) {
- if(i >= 10) {
- runtime·prints(", ...");
- break;
- }
- if(i != 0)
- runtime·prints(", ");
- runtime·printhex_c(((uintptr*)frame.argp)[i]);
- }
- runtime·prints(")\n");
- line = runtime·funcline(f, tracepc, &file);
- runtime·printf("\t%S:%d", file, line);
- if(frame.pc > f->entry)
- runtime·printf(" +%p", (uintptr)(frame.pc - f->entry));
- if(g->m->throwing > 0 && gp == g->m->curg || gotraceback >= 2)
- runtime·printf(" fp=%p sp=%p", frame.fp, frame.sp);
- runtime·printf("\n");
- nprint++;
- }
- }
- n++;
-
- skipped:
- waspanic = f->entry == (uintptr)runtime·sigpanic;
- wasnewproc = f->entry == (uintptr)runtime·newproc || f->entry == (uintptr)runtime·deferproc;
-
- // Do not unwind past the bottom of the stack.
- if(flr == nil)
- break;
-
- // Unwind to next frame.
- frame.fn = flr;
- frame.pc = frame.lr;
- frame.lr = 0;
- frame.sp = frame.fp;
- frame.fp = 0;
- }
-
- if(pcbuf == nil && callback == nil)
- n = nprint;
-
- // If callback != nil, we're being called to gather stack information during
- // garbage collection or stack growth. In that context, require that we used
- // up the entire defer stack. If not, then there is a bug somewhere and the
- // garbage collection or stack growth may not have seen the correct picture
- // of the stack. Crash now instead of silently executing the garbage collection
- // or stack copy incorrectly and setting up for a mysterious crash later.
- //
- // Note that panic != nil is okay here: there can be leftover panics,
- // because the defers on the panic stack do not nest in frame order as
- // they do on the defer stack. If you have:
- //
- // frame 1 defers d1
- // frame 2 defers d2
- // frame 3 defers d3
- // frame 4 panics
- // frame 4's panic starts running defers
- // frame 5, running d3, defers d4
- // frame 5 panics
- // frame 5's panic starts running defers
- // frame 6, running d4, garbage collects
- // frame 6, running d2, garbage collects
- //
- // During the execution of d4, the panic stack is d4 -> d3, which
- // is nested properly, and we'll treat frame 3 as resumable, because we
- // can find d3. (And in fact frame 3 is resumable. If d4 recovers
- // and frame 5 continues running, d3, d3 can recover and we'll
- // resume execution in (returning from) frame 3.)
- //
- // During the execution of d2, however, the panic stack is d2 -> d3,
- // which is inverted. The scan will match d2 to frame 2 but having
- // d2 on the stack until then means it will not match d3 to frame 3.
- // This is okay: if we're running d2, then all the defers after d2 have
- // completed and their corresponding frames are dead. Not finding d3
- // for frame 3 means we'll set frame 3's continpc == 0, which is correct
- // (frame 3 is dead). At the end of the walk the panic stack can thus
- // contain defers (d3 in this case) for dead frames. The inversion here
- // always indicates a dead frame, and the effect of the inversion on the
- // scan is to hide those dead frames, so the scan is still okay:
- // what's left on the panic stack are exactly (and only) the dead frames.
- //
- // We require callback != nil here because only when callback != nil
- // do we know that gentraceback is being called in a "must be correct"
- // context as opposed to a "best effort" context. The tracebacks with
- // callbacks only happen when everything is stopped nicely.
- // At other times, such as when gathering a stack for a profiling signal
- // or when printing a traceback during a crash, everything may not be
- // stopped nicely, and the stack walk may not be able to complete.
- // It's okay in those situations not to use up the entire defer stack:
- // incomplete information then is still better than nothing.
- if(callback != nil && n < max && defer != nil) {
- if(defer != nil)
- runtime·printf("runtime: g%D: leftover defer argp=%p pc=%p\n", gp->goid, defer->argp, defer->pc);
- if(panic != nil)
- runtime·printf("runtime: g%D: leftover panic argp=%p pc=%p\n", gp->goid, panic->defer->argp, panic->defer->pc);
- for(defer = gp->defer; defer != nil; defer = defer->link)
- runtime·printf("\tdefer %p argp=%p pc=%p\n", defer, defer->argp, defer->pc);
- for(panic = gp->panic; panic != nil; panic = panic->link) {
- runtime·printf("\tpanic %p defer %p", panic, panic->defer);
- if(panic->defer != nil)
- runtime·printf(" argp=%p pc=%p", panic->defer->argp, panic->defer->pc);
- runtime·printf("\n");
- }
- runtime·throw("traceback has leftover defers or panics");
- }
-
- return n;
-}
-
-void
-runtime·printcreatedby(G *gp)
-{
- int32 line;
- uintptr pc, tracepc;
- Func *f;
- String file;
-
- // Show what created goroutine, except main goroutine (goid 1).
- if((pc = gp->gopc) != 0 && (f = runtime·findfunc(pc)) != nil &&
- runtime·showframe(f, gp) && gp->goid != 1) {
- runtime·printf("created by %s\n", runtime·funcname(f));
- tracepc = pc; // back up to CALL instruction for funcline.
- if(pc > f->entry)
- tracepc -= PCQuantum;
- line = runtime·funcline(f, tracepc, &file);
- runtime·printf("\t%S:%d", file, line);
- if(pc > f->entry)
- runtime·printf(" +%p", (uintptr)(pc - f->entry));
- runtime·printf("\n");
- }
-}
-
-void
-runtime·traceback(uintptr pc, uintptr sp, uintptr lr, G *gp)
-{
- int32 n;
-
- USED(lr);
-
- if(gp->status == Gsyscall) {
- // Override signal registers if blocked in system call.
- pc = gp->syscallpc;
- sp = gp->syscallsp;
- }
-
- // Print traceback. By default, omits runtime frames.
- // If that means we print nothing at all, repeat forcing all frames printed.
- n = runtime·gentraceback(pc, sp, 0, gp, 0, nil, TracebackMaxFrames, nil, nil, false);
- if(n == 0)
- n = runtime·gentraceback(pc, sp, 0, gp, 0, nil, TracebackMaxFrames, nil, nil, true);
- if(n == TracebackMaxFrames)
- runtime·printf("...additional frames elided...\n");
- runtime·printcreatedby(gp);
-}
-
-int32
-runtime·callers(int32 skip, uintptr *pcbuf, int32 m)
-{
- uintptr pc, sp;
-
- sp = runtime·getcallersp(&skip);
- pc = (uintptr)runtime·getcallerpc(&skip);
-
- return runtime·gentraceback(pc, sp, 0, g, skip, pcbuf, m, nil, nil, false);
-}
-
-int32
-runtime·gcallers(G *gp, int32 skip, uintptr *pcbuf, int32 m)
-{
- return runtime·gentraceback(~(uintptr)0, ~(uintptr)0, 0, gp, skip, pcbuf, m, nil, nil, false);
-}
diff --git a/src/pkg/runtime/type.h b/src/pkg/runtime/type.h
index 2eda291aed..de82e886f2 100644
--- a/src/pkg/runtime/type.h
+++ b/src/pkg/runtime/type.h
@@ -21,7 +21,7 @@ struct Type
uint8 align;
uint8 fieldAlign;
uint8 kind;
- Alg *alg;
+ void* alg;
// gc stores type info required for garbage collector.
// If (kind&KindGCProg)==0, then gc directly contains sparse GC bitmap
// (no indirection), 4 bits per word.
@@ -66,14 +66,14 @@ struct IMethod
struct InterfaceType
{
- Type;
+ Type typ;
Slice mhdr;
IMethod m[];
};
struct MapType
{
- Type;
+ Type typ;
Type *key;
Type *elem;
Type *bucket; // internal type representing a hash bucket
@@ -87,20 +87,20 @@ struct MapType
struct ChanType
{
- Type;
+ Type typ;
Type *elem;
uintptr dir;
};
struct SliceType
{
- Type;
+ Type typ;
Type *elem;
};
struct FuncType
{
- Type;
+ Type typ;
bool dotdotdot;
Slice in;
Slice out;
@@ -108,6 +108,6 @@ struct FuncType
struct PtrType
{
- Type;
+ Type typ;
Type *elem;
};
diff --git a/src/pkg/runtime/typekind.go b/src/pkg/runtime/typekind.go
new file mode 100644
index 0000000000..5985536289
--- /dev/null
+++ b/src/pkg/runtime/typekind.go
@@ -0,0 +1,44 @@
+// 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
+
+const (
+ kindBool = 1 + iota
+ kindInt
+ kindInt8
+ kindInt16
+ kindInt32
+ kindInt64
+ kindUint
+ kindUint8
+ kindUint16
+ kindUint32
+ kindUint64
+ kindUintptr
+ kindFloat32
+ kindFloat64
+ kindComplex64
+ kindComplex128
+ kindArray
+ kindChan
+ kindFunc
+ kindInterface
+ kindMap
+ kindPtr
+ kindSlice
+ kindString
+ kindStruct
+ kindUnsafePointer
+
+ kindDirectIface = 1 << 5
+ kindGCProg = 1 << 6 // Type.gc points to GC program
+ kindNoPointers = 1 << 7
+ kindMask = (1 << 5) - 1
+)
+
+// isDirectIface reports whether t is stored directly in an interface value.
+func isDirectIface(t *_type) bool {
+ return t.kind&kindDirectIface != 0
+}
diff --git a/src/pkg/runtime/typekind.h b/src/pkg/runtime/typekind.h
index bf6ade08d6..7c611e8ba6 100644
--- a/src/pkg/runtime/typekind.h
+++ b/src/pkg/runtime/typekind.h
@@ -33,8 +33,9 @@ enum {
KindStruct,
KindUnsafePointer,
+ KindDirectIface = 1<<5,
KindGCProg = 1<<6, // Type.gc points to GC program
KindNoPointers = 1<<7,
- KindMask = (1<<6)-1,
+ KindMask = (1<<5)-1,
};
diff --git a/src/pkg/runtime/vlop_386.s b/src/pkg/runtime/vlop_386.s
index 9783fdc936..ce8e7d0643 100644
--- a/src/pkg/runtime/vlop_386.s
+++ b/src/pkg/runtime/vlop_386.s
@@ -23,15 +23,15 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
/*
* C runtime for 64-bit divide.
*/
-// _mul64x32(r *uint64, a uint64, b uint32)
+// runtime·_mul64x32(r *uint64, a uint64, b uint32) uint32
// sets *r = low 64 bits of 96-bit product a*b; returns high 32 bits.
-TEXT _mul64by32(SB), NOSPLIT, $0
+TEXT runtime·_mul64by32(SB), NOSPLIT, $0
MOVL r+0(FP), CX
MOVL a+4(FP), AX
MULL b+12(FP)
@@ -43,12 +43,14 @@ TEXT _mul64by32(SB), NOSPLIT, $0
ADCL $0, DX
MOVL BX, 4(CX)
MOVL DX, AX
+ MOVL AX, ret+16(FP)
RET
-TEXT _div64by32(SB), NOSPLIT, $0
+TEXT runtime·_div64by32(SB), NOSPLIT, $0
MOVL r+12(FP), CX
MOVL a+0(FP), AX
MOVL a+4(FP), DX
DIVL b+8(FP)
MOVL DX, 0(CX)
+ MOVL AX, ret+16(FP)
RET
diff --git a/src/pkg/runtime/vlop_arm.s b/src/pkg/runtime/vlop_arm.s
index 02bab3107e..b4b905bb7a 100644
--- a/src/pkg/runtime/vlop_arm.s
+++ b/src/pkg/runtime/vlop_arm.s
@@ -24,36 +24,35 @@
// THE SOFTWARE.
#include "zasm_GOOS_GOARCH.h"
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
arg=0
/* replaced use of R10 by R11 because the former can be the data segment base register */
TEXT _mulv(SB), NOSPLIT, $0
- MOVW 0(FP), R0
- MOVW 4(FP), R2 /* l0 */
- MOVW 8(FP), R11 /* h0 */
- MOVW 12(FP), R4 /* l1 */
- MOVW 16(FP), R5 /* h1 */
+ MOVW l0+0(FP), R2 /* l0 */
+ MOVW h0+4(FP), R11 /* h0 */
+ MOVW l1+8(FP), R4 /* l1 */
+ MOVW h1+12(FP), R5 /* h1 */
MULLU R4, R2, (R7,R6)
MUL R11, R4, R8
ADD R8, R7
MUL R2, R5, R8
ADD R8, R7
- MOVW R6, 0(R(arg))
- MOVW R7, 4(R(arg))
+ MOVW R6, ret_lo+16(FP)
+ MOVW R7, ret_hi+20(FP)
RET
// trampoline for _sfloat2. passes LR as arg0 and
// saves registers R0-R13 and CPSR on the stack. R0-R12 and CPSR flags can
// be changed by _sfloat2.
-TEXT _sfloat(SB), NOSPLIT, $64-0 // 4 arg + 14*4 saved regs + cpsr
+TEXT _sfloat(SB), NOSPLIT, $68-0 // 4 arg + 14*4 saved regs + cpsr + return value
MOVW R14, 4(R13)
MOVW R0, 8(R13)
MOVW $12(R13), R0
MOVM.IA.W [R1-R12], (R0)
- MOVW $68(R13), R1 // correct for frame size
+ MOVW $72(R13), R1 // correct for frame size
MOVW R1, 60(R13)
WORD $0xe10f1000 // mrs r1, cpsr
MOVW R1, 64(R13)
@@ -79,6 +78,7 @@ TEXT _sfloat(SB), NOSPLIT, $64-0 // 4 arg + 14*4 saved regs + cpsr
MOVW $1, R1
MOVW R1, m_softfloat(R8)
BL runtime·_sfloat2(SB)
+ MOVW 68(R13), R0
MOVW g_m(g), R8
MOVW m_locks(R8), R1
SUB $1, R1
@@ -94,6 +94,18 @@ TEXT _sfloat(SB), NOSPLIT, $64-0 // 4 arg + 14*4 saved regs + cpsr
MOVW 8(R13), R0
RET
+// trampoline for _sfloat2 panic.
+// _sfloat2 instructs _sfloat to return here.
+// We need to push a fake saved LR onto the stack,
+// load the signal fault address into LR, and jump
+// to the real sigpanic.
+// This simulates what sighandler does for a memory fault.
+TEXT _sfloatpanic(SB),NOSPLIT,$-4
+ MOVW $0, R0
+ MOVW.W R0, -4(R13)
+ MOVW g_sigpc(g), LR
+ B runtime·sigpanic(SB)
+
// func udiv(n, d uint32) (q, r uint32)
// Reference:
// Sloss, Andrew et. al; ARM System Developer's Guide: Designing and Optimizing System Software
@@ -294,3 +306,12 @@ out:
MOVW 12(R13), R(s)
MOVW 16(R13), R(M)
RET
+
+// _mul64by32 and _div64by32 not implemented on arm
+TEXT runtime·_mul64by32(SB), NOSPLIT, $0
+ MOVW $0, R0
+ MOVW (R0), R1 // crash
+
+TEXT runtime·_div64by32(SB), NOSPLIT, $0
+ MOVW $0, R0
+ MOVW (R0), R1 // crash
diff --git a/src/pkg/runtime/vlrt_386.c b/src/pkg/runtime/vlrt.c
index ace1beb4cc..cb0d147961 100644
--- a/src/pkg/runtime/vlrt_386.c
+++ b/src/pkg/runtime/vlrt.c
@@ -23,7 +23,9 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-#include "../../cmd/ld/textflag.h"
+// +build arm 386
+
+#include "textflag.h"
/*
* C runtime for 64-bit divide, others.
@@ -32,7 +34,7 @@
* to generate the code directly now. Find and remove.
*/
-extern void runtime·panicdivide(void);
+void runtime·panicdivide(void);
typedef unsigned long ulong;
typedef unsigned int uint;
@@ -45,38 +47,58 @@ typedef signed char schar;
typedef struct Vlong Vlong;
struct Vlong
{
- union
- {
- long long v;
- struct
- {
- ulong lo;
- ulong hi;
- };
- struct
- {
- ushort lols;
- ushort loms;
- ushort hils;
- ushort hims;
- };
- };
+ ulong lo;
+ ulong hi;
+};
+
+typedef union Vlong64 Vlong64;
+union Vlong64
+{
+ long long v;
+ Vlong v2;
};
void runtime·abort(void);
-void
-_d2v(Vlong *y, double d)
+#pragma textflag NOSPLIT
+Vlong
+_addv(Vlong a, Vlong b)
+{
+ Vlong r;
+
+ r.lo = a.lo + b.lo;
+ r.hi = a.hi + b.hi;
+ if(r.lo < a.lo)
+ r.hi++;
+ return r;
+}
+
+#pragma textflag NOSPLIT
+Vlong
+_subv(Vlong a, Vlong b)
+{
+ Vlong r;
+
+ r.lo = a.lo - b.lo;
+ r.hi = a.hi - b.hi;
+ if(r.lo > a.lo)
+ r.hi--;
+ return r;
+}
+
+Vlong
+_d2v(double d)
{
- union { double d; struct Vlong; } x;
+ union { double d; Vlong vl; } x;
ulong xhi, xlo, ylo, yhi;
int sh;
+ Vlong y;
x.d = d;
- xhi = (x.hi & 0xfffff) | 0x100000;
- xlo = x.lo;
- sh = 1075 - ((x.hi >> 20) & 0x7ff);
+ xhi = (x.vl.hi & 0xfffff) | 0x100000;
+ xlo = x.vl.lo;
+ sh = 1075 - ((x.vl.hi >> 20) & 0x7ff);
ylo = 0;
yhi = 0;
@@ -101,7 +123,7 @@ _d2v(Vlong *y, double d)
} else {
/* v = (hi||lo) << -sh */
sh = -sh;
- if(sh <= 10) {
+ if(sh <= 10) { /* NOTE: sh <= 11 on ARM??? */
ylo = xlo << sh;
yhi = (xhi << sh) | (xlo >> (32-sh));
} else {
@@ -109,7 +131,7 @@ _d2v(Vlong *y, double d)
yhi = d; /* causes something awful */
}
}
- if(x.hi & SIGN(32)) {
+ if(x.vl.hi & SIGN(32)) {
if(ylo != 0) {
ylo = -ylo;
yhi = ~yhi;
@@ -117,15 +139,26 @@ _d2v(Vlong *y, double d)
yhi = -yhi;
}
- y->hi = yhi;
- y->lo = ylo;
+ y.hi = yhi;
+ y.lo = ylo;
+ return y;
}
-void
-_f2v(Vlong *y, float f)
+Vlong
+_f2v(float f)
{
+ return _d2v(f);
+}
- _d2v(y, f);
+double
+_ul2d(ulong u)
+{
+ // compensate for bug in c
+ if(u & SIGN(32)) {
+ u ^= SIGN(32);
+ return 2147483648. + u;
+ }
+ return u;
}
double
@@ -137,7 +170,7 @@ _v2d(Vlong x)
x.hi = ~x.hi;
} else
x.hi = -x.hi;
- return -((long)x.hi*4294967296. + x.lo);
+ return -(_ul2d(x.hi)*4294967296. + _ul2d(x.lo));
}
return (long)x.hi*4294967296. + x.lo;
}
@@ -148,8 +181,8 @@ _v2f(Vlong x)
return _v2d(x);
}
-ulong _div64by32(Vlong, ulong, ulong*);
-int _mul64by32(Vlong*, Vlong, ulong);
+ulong runtime·_div64by32(Vlong, ulong, ulong*);
+int runtime·_mul64by32(Vlong*, Vlong, ulong);
static void
slowdodiv(Vlong num, Vlong den, Vlong *q, Vlong *r)
@@ -166,7 +199,7 @@ slowdodiv(Vlong num, Vlong den, Vlong *q, Vlong *r)
* get a divide by zero
*/
if(denlo==0 && denhi==0) {
- numlo = numlo / denlo;
+ runtime·panicdivide();
}
/*
@@ -213,12 +246,21 @@ slowdodiv(Vlong num, Vlong den, Vlong *q, Vlong *r)
}
}
+#ifdef GOARCH_arm
+static void
+dodiv(Vlong num, Vlong den, Vlong *qp, Vlong *rp)
+{
+ slowdodiv(num, den, qp, rp);
+}
+#endif
+
+#ifdef GOARCH_386
static void
dodiv(Vlong num, Vlong den, Vlong *qp, Vlong *rp)
{
ulong n;
Vlong x, q, r;
-
+
if(den.hi > num.hi || (den.hi == num.hi && den.lo > num.lo)){
if(qp) {
qp->hi = 0;
@@ -234,11 +276,11 @@ dodiv(Vlong num, Vlong den, Vlong *qp, Vlong *rp)
if(den.hi != 0){
q.hi = 0;
n = num.hi/den.hi;
- if(_mul64by32(&x, den, n) || x.hi > num.hi || (x.hi == num.hi && x.lo > num.lo))
+ if(runtime·_mul64by32(&x, den, n) || x.hi > num.hi || (x.hi == num.hi && x.lo > num.lo))
slowdodiv(num, den, &q, &r);
else {
q.lo = n;
- r.v = num.v - x.v;
+ *(long long*)&r = *(long long*)&num - *(long long*)&x;
}
} else {
if(num.hi >= den.lo){
@@ -249,7 +291,7 @@ dodiv(Vlong num, Vlong den, Vlong *qp, Vlong *rp)
} else {
q.hi = 0;
}
- q.lo = _div64by32(num, den.lo, &r.lo);
+ q.lo = runtime·_div64by32(num, den.lo, &r.lo);
r.hi = 0;
}
if(qp) {
@@ -261,45 +303,38 @@ dodiv(Vlong num, Vlong den, Vlong *qp, Vlong *rp)
rp->hi = r.hi;
}
}
+#endif
-void
-_divvu(Vlong *q, Vlong n, Vlong d)
+Vlong
+_divvu(Vlong n, Vlong d)
{
+ Vlong q;
if(n.hi == 0 && d.hi == 0) {
if(d.lo == 0)
runtime·panicdivide();
- q->hi = 0;
- q->lo = n.lo / d.lo;
- return;
+ q.hi = 0;
+ q.lo = n.lo / d.lo;
+ return q;
}
- dodiv(n, d, q, 0);
-}
-
-void
-runtime·uint64div(Vlong n, Vlong d, Vlong q)
-{
- _divvu(&q, n, d);
+ dodiv(n, d, &q, 0);
+ return q;
}
-void
-_modvu(Vlong *r, Vlong n, Vlong d)
+Vlong
+_modvu(Vlong n, Vlong d)
{
+ Vlong r;
if(n.hi == 0 && d.hi == 0) {
if(d.lo == 0)
runtime·panicdivide();
- r->hi = 0;
- r->lo = n.lo % d.lo;
- return;
+ r.hi = 0;
+ r.lo = n.lo % d.lo;
+ return r;
}
- dodiv(n, d, 0, r);
-}
-
-void
-runtime·uint64mod(Vlong n, Vlong d, Vlong q)
-{
- _modvu(&q, n, d);
+ dodiv(n, d, 0, &r);
+ return r;
}
static void
@@ -314,24 +349,25 @@ vneg(Vlong *v)
v->hi = ~v->hi;
}
-void
-_divv(Vlong *q, Vlong n, Vlong d)
+Vlong
+_divv(Vlong n, Vlong d)
{
long nneg, dneg;
+ Vlong q;
if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) {
if((long)n.lo == -0x80000000 && (long)d.lo == -1) {
// special case: 32-bit -0x80000000 / -1 causes divide error,
// but it's okay in this 64-bit context.
- q->lo = 0x80000000;
- q->hi = 0;
- return;
+ q.lo = 0x80000000;
+ q.hi = 0;
+ return q;
}
if(d.lo == 0)
runtime·panicdivide();
- q->lo = (long)n.lo / (long)d.lo;
- q->hi = ((long)q->lo) >> 31;
- return;
+ q.lo = (long)n.lo / (long)d.lo;
+ q.hi = ((long)q.lo) >> 31;
+ return q;
}
nneg = n.hi >> 31;
if(nneg)
@@ -339,35 +375,31 @@ _divv(Vlong *q, Vlong n, Vlong d)
dneg = d.hi >> 31;
if(dneg)
vneg(&d);
- dodiv(n, d, q, 0);
+ dodiv(n, d, &q, 0);
if(nneg != dneg)
- vneg(q);
-}
-
-void
-runtime·int64div(Vlong n, Vlong d, Vlong q)
-{
- _divv(&q, n, d);
+ vneg(&q);
+ return q;
}
-void
-_modv(Vlong *r, Vlong n, Vlong d)
+Vlong
+_modv(Vlong n, Vlong d)
{
long nneg, dneg;
+ Vlong r;
if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) {
if((long)n.lo == -0x80000000 && (long)d.lo == -1) {
// special case: 32-bit -0x80000000 % -1 causes divide error,
// but it's okay in this 64-bit context.
- r->lo = 0;
- r->hi = 0;
- return;
+ r.lo = 0;
+ r.hi = 0;
+ return r;
}
if(d.lo == 0)
runtime·panicdivide();
- r->lo = (long)n.lo % (long)d.lo;
- r->hi = ((long)r->lo) >> 31;
- return;
+ r.lo = (long)n.lo % (long)d.lo;
+ r.hi = ((long)r.lo) >> 31;
+ return r;
}
nneg = n.hi >> 31;
if(nneg)
@@ -375,160 +407,171 @@ _modv(Vlong *r, Vlong n, Vlong d)
dneg = d.hi >> 31;
if(dneg)
vneg(&d);
- dodiv(n, d, 0, r);
+ dodiv(n, d, 0, &r);
if(nneg)
- vneg(r);
+ vneg(&r);
+ return r;
}
-void
-runtime·int64mod(Vlong n, Vlong d, Vlong q)
-{
- _modv(&q, n, d);
-}
-
-void
-_rshav(Vlong *r, Vlong a, int b)
+#pragma textflag NOSPLIT
+Vlong
+_rshav(Vlong a, int b)
{
long t;
+ Vlong r;
t = a.hi;
if(b >= 32) {
- r->hi = t>>31;
+ r.hi = t>>31;
if(b >= 64) {
/* this is illegal re C standard */
- r->lo = t>>31;
- return;
+ r.lo = t>>31;
+ return r;
}
- r->lo = t >> (b-32);
- return;
+ r.lo = t >> (b-32);
+ return r;
}
if(b <= 0) {
- r->hi = t;
- r->lo = a.lo;
- return;
+ r.hi = t;
+ r.lo = a.lo;
+ return r;
}
- r->hi = t >> b;
- r->lo = (t << (32-b)) | (a.lo >> b);
+ r.hi = t >> b;
+ r.lo = (t << (32-b)) | (a.lo >> b);
+ return r;
}
-void
-_rshlv(Vlong *r, Vlong a, int b)
+#pragma textflag NOSPLIT
+Vlong
+_rshlv(Vlong a, int b)
{
ulong t;
+ Vlong r;
t = a.hi;
if(b >= 32) {
- r->hi = 0;
+ r.hi = 0;
if(b >= 64) {
/* this is illegal re C standard */
- r->lo = 0;
- return;
+ r.lo = 0;
+ return r;
}
- r->lo = t >> (b-32);
- return;
+ r.lo = t >> (b-32);
+ return r;
}
if(b <= 0) {
- r->hi = t;
- r->lo = a.lo;
- return;
+ r.hi = t;
+ r.lo = a.lo;
+ return r;
}
- r->hi = t >> b;
- r->lo = (t << (32-b)) | (a.lo >> b);
+ r.hi = t >> b;
+ r.lo = (t << (32-b)) | (a.lo >> b);
+ return r;
}
#pragma textflag NOSPLIT
-void
-_lshv(Vlong *r, Vlong a, int b)
+Vlong
+_lshv(Vlong a, int b)
{
ulong t;
t = a.lo;
if(b >= 32) {
- r->lo = 0;
if(b >= 64) {
/* this is illegal re C standard */
- r->hi = 0;
- return;
+ return (Vlong){0, 0};
}
- r->hi = t << (b-32);
- return;
+ return (Vlong){0, t<<(b-32)};
}
if(b <= 0) {
- r->lo = t;
- r->hi = a.hi;
- return;
+ return (Vlong){t, a.hi};
}
- r->lo = t << b;
- r->hi = (t >> (32-b)) | (a.hi << b);
+ return (Vlong){t<<b, (t >> (32-b)) | (a.hi << b)};
}
-void
-_andv(Vlong *r, Vlong a, Vlong b)
+#pragma textflag NOSPLIT
+Vlong
+_andv(Vlong a, Vlong b)
{
- r->hi = a.hi & b.hi;
- r->lo = a.lo & b.lo;
+ Vlong r;
+
+ r.hi = a.hi & b.hi;
+ r.lo = a.lo & b.lo;
+ return r;
}
-void
-_orv(Vlong *r, Vlong a, Vlong b)
+#pragma textflag NOSPLIT
+Vlong
+_orv(Vlong a, Vlong b)
{
- r->hi = a.hi | b.hi;
- r->lo = a.lo | b.lo;
+ Vlong r;
+
+ r.hi = a.hi | b.hi;
+ r.lo = a.lo | b.lo;
+ return r;
}
-void
-_xorv(Vlong *r, Vlong a, Vlong b)
+#pragma textflag NOSPLIT
+Vlong
+_xorv(Vlong a, Vlong b)
{
- r->hi = a.hi ^ b.hi;
- r->lo = a.lo ^ b.lo;
+ Vlong r;
+
+ r.hi = a.hi ^ b.hi;
+ r.lo = a.lo ^ b.lo;
+ return r;
}
-void
-_vpp(Vlong *l, Vlong *r)
+Vlong
+_vpp(Vlong *r)
{
+ Vlong l;
- l->hi = r->hi;
- l->lo = r->lo;
+ l = *r;
r->lo++;
if(r->lo == 0)
r->hi++;
+ return l;
}
-void
-_vmm(Vlong *l, Vlong *r)
+#pragma textflag NOSPLIT
+Vlong
+_vmm(Vlong *r)
{
+ Vlong l;
- l->hi = r->hi;
- l->lo = r->lo;
+ l = *r;
if(r->lo == 0)
r->hi--;
r->lo--;
+ return l;
}
-void
-_ppv(Vlong *l, Vlong *r)
+#pragma textflag NOSPLIT
+Vlong
+_ppv(Vlong *r)
{
r->lo++;
if(r->lo == 0)
r->hi++;
- l->hi = r->hi;
- l->lo = r->lo;
+ return *r;
}
-void
-_mmv(Vlong *l, Vlong *r)
+#pragma textflag NOSPLIT
+Vlong
+_mmv(Vlong *r)
{
if(r->lo == 0)
r->hi--;
r->lo--;
- l->hi = r->hi;
- l->lo = r->lo;
+ return *r;
}
-void
-_vasop(Vlong *ret, void *lv, void fn(Vlong*, Vlong, Vlong), int type, Vlong rv)
+#pragma textflag NOSPLIT
+Vlong
+_vasop(void *lv, Vlong fn(Vlong, Vlong), int type, Vlong rv)
{
Vlong t, u;
@@ -542,158 +585,183 @@ _vasop(Vlong *ret, void *lv, void fn(Vlong*, Vlong, Vlong), int type, Vlong rv)
case 1: /* schar */
t.lo = *(schar*)lv;
t.hi = t.lo >> 31;
- fn(&u, t, rv);
+ u = fn(t, rv);
*(schar*)lv = u.lo;
break;
case 2: /* uchar */
t.lo = *(uchar*)lv;
t.hi = 0;
- fn(&u, t, rv);
+ u = fn(t, rv);
*(uchar*)lv = u.lo;
break;
case 3: /* short */
t.lo = *(short*)lv;
t.hi = t.lo >> 31;
- fn(&u, t, rv);
+ u = fn(t, rv);
*(short*)lv = u.lo;
break;
case 4: /* ushort */
t.lo = *(ushort*)lv;
t.hi = 0;
- fn(&u, t, rv);
+ u = fn(t, rv);
*(ushort*)lv = u.lo;
break;
case 9: /* int */
t.lo = *(int*)lv;
t.hi = t.lo >> 31;
- fn(&u, t, rv);
+ u = fn(t, rv);
*(int*)lv = u.lo;
break;
case 10: /* uint */
t.lo = *(uint*)lv;
t.hi = 0;
- fn(&u, t, rv);
+ u = fn(t, rv);
*(uint*)lv = u.lo;
break;
case 5: /* long */
t.lo = *(long*)lv;
t.hi = t.lo >> 31;
- fn(&u, t, rv);
+ u = fn(t, rv);
*(long*)lv = u.lo;
break;
case 6: /* ulong */
t.lo = *(ulong*)lv;
t.hi = 0;
- fn(&u, t, rv);
+ u = fn(t, rv);
*(ulong*)lv = u.lo;
break;
case 7: /* vlong */
case 8: /* uvlong */
- fn(&u, *(Vlong*)lv, rv);
+ if((void*)fn == _lshv || (void*)fn == _rshav || (void*)fn == _rshlv)
+ u = ((Vlong(*)(Vlong,int))fn)(*(Vlong*)lv, *(int*)&rv);
+ else
+ u = fn(*(Vlong*)lv, rv);
*(Vlong*)lv = u;
break;
}
- *ret = u;
+ return u;
}
-void
-_p2v(Vlong *ret, void *p)
+#pragma textflag NOSPLIT
+Vlong
+_p2v(void *p)
{
long t;
+ Vlong ret;
t = (ulong)p;
- ret->lo = t;
- ret->hi = 0;
+ ret.lo = t;
+ ret.hi = 0;
+ return ret;
}
-void
-_sl2v(Vlong *ret, long sl)
+#pragma textflag NOSPLIT
+Vlong
+_sl2v(long sl)
{
long t;
+ Vlong ret;
t = sl;
- ret->lo = t;
- ret->hi = t >> 31;
+ ret.lo = t;
+ ret.hi = t >> 31;
+ return ret;
}
-void
-_ul2v(Vlong *ret, ulong ul)
+#pragma textflag NOSPLIT
+Vlong
+_ul2v(ulong ul)
{
long t;
+ Vlong ret;
t = ul;
- ret->lo = t;
- ret->hi = 0;
+ ret.lo = t;
+ ret.hi = 0;
+ return ret;
}
-void
-_si2v(Vlong *ret, int si)
+#pragma textflag NOSPLIT
+Vlong
+_si2v(int si)
{
- long t;
-
- t = si;
- ret->lo = t;
- ret->hi = t >> 31;
+ return (Vlong){si, si>>31};
}
-void
-_ui2v(Vlong *ret, uint ui)
+#pragma textflag NOSPLIT
+Vlong
+_ui2v(uint ui)
{
long t;
+ Vlong ret;
t = ui;
- ret->lo = t;
- ret->hi = 0;
+ ret.lo = t;
+ ret.hi = 0;
+ return ret;
}
-void
-_sh2v(Vlong *ret, long sh)
+#pragma textflag NOSPLIT
+Vlong
+_sh2v(long sh)
{
long t;
+ Vlong ret;
t = (sh << 16) >> 16;
- ret->lo = t;
- ret->hi = t >> 31;
+ ret.lo = t;
+ ret.hi = t >> 31;
+ return ret;
}
-void
-_uh2v(Vlong *ret, ulong ul)
+#pragma textflag NOSPLIT
+Vlong
+_uh2v(ulong ul)
{
long t;
+ Vlong ret;
t = ul & 0xffff;
- ret->lo = t;
- ret->hi = 0;
+ ret.lo = t;
+ ret.hi = 0;
+ return ret;
}
-void
-_sc2v(Vlong *ret, long uc)
+#pragma textflag NOSPLIT
+Vlong
+_sc2v(long uc)
{
long t;
+ Vlong ret;
t = (uc << 24) >> 24;
- ret->lo = t;
- ret->hi = t >> 31;
+ ret.lo = t;
+ ret.hi = t >> 31;
+ return ret;
}
-void
-_uc2v(Vlong *ret, ulong ul)
+#pragma textflag NOSPLIT
+Vlong
+_uc2v(ulong ul)
{
long t;
+ Vlong ret;
t = ul & 0xff;
- ret->lo = t;
- ret->hi = 0;
+ ret.lo = t;
+ ret.hi = 0;
+ return ret;
}
+#pragma textflag NOSPLIT
long
_v2sc(Vlong rv)
{
@@ -703,6 +771,7 @@ _v2sc(Vlong rv)
return (t << 24) >> 24;
}
+#pragma textflag NOSPLIT
long
_v2uc(Vlong rv)
{
@@ -710,6 +779,7 @@ _v2uc(Vlong rv)
return rv.lo & 0xff;
}
+#pragma textflag NOSPLIT
long
_v2sh(Vlong rv)
{
@@ -719,6 +789,7 @@ _v2sh(Vlong rv)
return (t << 16) >> 16;
}
+#pragma textflag NOSPLIT
long
_v2uh(Vlong rv)
{
@@ -726,6 +797,7 @@ _v2uh(Vlong rv)
return rv.lo & 0xffff;
}
+#pragma textflag NOSPLIT
long
_v2sl(Vlong rv)
{
@@ -733,6 +805,7 @@ _v2sl(Vlong rv)
return rv.lo;
}
+#pragma textflag NOSPLIT
long
_v2ul(Vlong rv)
{
@@ -740,13 +813,14 @@ _v2ul(Vlong rv)
return rv.lo;
}
+#pragma textflag NOSPLIT
long
_v2si(Vlong rv)
{
-
return rv.lo;
}
+#pragma textflag NOSPLIT
long
_v2ui(Vlong rv)
{
@@ -754,24 +828,28 @@ _v2ui(Vlong rv)
return rv.lo;
}
+#pragma textflag NOSPLIT
int
_testv(Vlong rv)
{
return rv.lo || rv.hi;
}
+#pragma textflag NOSPLIT
int
_eqv(Vlong lv, Vlong rv)
{
return lv.lo == rv.lo && lv.hi == rv.hi;
}
+#pragma textflag NOSPLIT
int
_nev(Vlong lv, Vlong rv)
{
return lv.lo != rv.lo || lv.hi != rv.hi;
}
+#pragma textflag NOSPLIT
int
_ltv(Vlong lv, Vlong rv)
{
@@ -779,6 +857,7 @@ _ltv(Vlong lv, Vlong rv)
(lv.hi == rv.hi && lv.lo < rv.lo);
}
+#pragma textflag NOSPLIT
int
_lev(Vlong lv, Vlong rv)
{
@@ -786,6 +865,7 @@ _lev(Vlong lv, Vlong rv)
(lv.hi == rv.hi && lv.lo <= rv.lo);
}
+#pragma textflag NOSPLIT
int
_gtv(Vlong lv, Vlong rv)
{
@@ -793,6 +873,7 @@ _gtv(Vlong lv, Vlong rv)
(lv.hi == rv.hi && lv.lo > rv.lo);
}
+#pragma textflag NOSPLIT
int
_gev(Vlong lv, Vlong rv)
{
@@ -800,6 +881,7 @@ _gev(Vlong lv, Vlong rv)
(lv.hi == rv.hi && lv.lo >= rv.lo);
}
+#pragma textflag NOSPLIT
int
_lov(Vlong lv, Vlong rv)
{
@@ -807,6 +889,7 @@ _lov(Vlong lv, Vlong rv)
(lv.hi == rv.hi && lv.lo < rv.lo);
}
+#pragma textflag NOSPLIT
int
_lsv(Vlong lv, Vlong rv)
{
@@ -814,6 +897,7 @@ _lsv(Vlong lv, Vlong rv)
(lv.hi == rv.hi && lv.lo <= rv.lo);
}
+#pragma textflag NOSPLIT
int
_hiv(Vlong lv, Vlong rv)
{
@@ -821,6 +905,7 @@ _hiv(Vlong lv, Vlong rv)
(lv.hi == rv.hi && lv.lo > rv.lo);
}
+#pragma textflag NOSPLIT
int
_hsv(Vlong lv, Vlong rv)
{
diff --git a/src/pkg/runtime/vlrt.go b/src/pkg/runtime/vlrt.go
new file mode 100644
index 0000000000..6370732ca0
--- /dev/null
+++ b/src/pkg/runtime/vlrt.go
@@ -0,0 +1,258 @@
+// Inferno's libkern/vlrt-arm.c
+// http://code.google.com/p/inferno-os/source/browse/libkern/vlrt-arm.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Revisions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com). All rights reserved.
+// 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.
+
+// +build arm 386
+
+package runtime
+
+import "unsafe"
+
+const (
+ sign32 = 1 << (32 - 1)
+ sign64 = 1 << (64 - 1)
+)
+
+func float64toint64(d float64) (y uint64) {
+ _d2v(&y, d)
+ return
+}
+
+func float64touint64(d float64) (y uint64) {
+ _d2v(&y, d)
+ return
+}
+
+func int64tofloat64(y int64) float64 {
+ if y < 0 {
+ return -uint64tofloat64(-uint64(y))
+ }
+ return uint64tofloat64(uint64(y))
+}
+
+func uint64tofloat64(y uint64) float64 {
+ hi := float64(uint32(y >> 32))
+ lo := float64(uint32(y))
+ d := hi*(1<<32) + lo
+ return d
+}
+
+func _d2v(y *uint64, d float64) {
+ x := *(*uint64)(unsafe.Pointer(&d))
+
+ xhi := uint32(x>>32)&0xfffff | 0x100000
+ xlo := uint32(x)
+ sh := 1075 - int32(uint32(x>>52)&0x7ff)
+
+ var ylo, yhi uint32
+ if sh >= 0 {
+ sh := uint32(sh)
+ /* v = (hi||lo) >> sh */
+ if sh < 32 {
+ if sh == 0 {
+ ylo = xlo
+ yhi = xhi
+ } else {
+ ylo = xlo>>sh | xhi<<(32-sh)
+ yhi = xhi >> sh
+ }
+ } else {
+ if sh == 32 {
+ ylo = xhi
+ } else if sh < 64 {
+ ylo = xhi >> (sh - 32)
+ }
+ }
+ } else {
+ /* v = (hi||lo) << -sh */
+ sh := uint32(-sh)
+ if sh <= 11 {
+ ylo = xlo << sh
+ yhi = xhi<<sh | xlo>>(32-sh)
+ } else {
+ /* overflow */
+ yhi = uint32(d) /* causes something awful */
+ }
+ }
+ if x&sign64 != 0 {
+ if ylo != 0 {
+ ylo = -ylo
+ yhi = ^yhi
+ } else {
+ yhi = -yhi
+ }
+ }
+
+ *y = uint64(yhi)<<32 | uint64(ylo)
+}
+
+func uint64div(n, d uint64) uint64 {
+ // Check for 32 bit operands
+ if uint32(n>>32) == 0 && uint32(d>>32) == 0 {
+ if uint32(d) == 0 {
+ panicdivide()
+ }
+ return uint64(uint32(n) / uint32(d))
+ }
+ q, _ := dodiv(n, d)
+ return q
+}
+
+func uint64mod(n, d uint64) uint64 {
+ // Check for 32 bit operands
+ if uint32(n>>32) == 0 && uint32(d>>32) == 0 {
+ if uint32(d) == 0 {
+ panicdivide()
+ }
+ return uint64(uint32(n) % uint32(d))
+ }
+ _, r := dodiv(n, d)
+ return r
+}
+
+func int64div(n, d int64) int64 {
+ // Check for 32 bit operands
+ if int64(int32(n)) == n && int64(int32(d)) == d {
+ if int32(n) == -0x80000000 && int32(d) == -1 {
+ // special case: 32-bit -0x80000000 / -1 = -0x80000000,
+ // but 64-bit -0x80000000 / -1 = 0x80000000.
+ return 0x80000000
+ }
+ if int32(d) == 0 {
+ panicdivide()
+ }
+ return int64(int32(n) / int32(d))
+ }
+
+ nneg := n < 0
+ dneg := d < 0
+ if nneg {
+ n = -n
+ }
+ if dneg {
+ d = -d
+ }
+ uq, _ := dodiv(uint64(n), uint64(d))
+ q := int64(uq)
+ if nneg != dneg {
+ q = -q
+ }
+ return q
+}
+
+func int64mod(n, d int64) int64 {
+ // Check for 32 bit operands
+ if int64(int32(n)) == n && int64(int32(d)) == d {
+ if int32(d) == 0 {
+ panicdivide()
+ }
+ return int64(int32(n) % int32(d))
+ }
+
+ nneg := n < 0
+ if nneg {
+ n = -n
+ }
+ if d < 0 {
+ d = -d
+ }
+ _, ur := dodiv(uint64(n), uint64(d))
+ r := int64(ur)
+ if nneg {
+ r = -r
+ }
+ return r
+}
+
+//go:noescape
+func _mul64by32(lo64 *uint64, a uint64, b uint32) (hi32 uint32)
+
+//go:noescape
+func _div64by32(a uint64, b uint32, r *uint32) (q uint32)
+
+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)
+ }
+
+ if d > n {
+ return 0, n
+ }
+
+ if uint32(d>>32) != 0 {
+ t := uint32(n>>32) / uint32(d>>32)
+ var lo64 uint64
+ hi32 := _mul64by32(&lo64, d, t)
+ if hi32 != 0 || lo64 > n {
+ return slowdodiv(n, d)
+ }
+ return uint64(t), n - lo64
+ }
+
+ // d is 32 bit
+ var qhi uint32
+ if uint32(n>>32) >= uint32(d) {
+ if uint32(d) == 0 {
+ panicdivide()
+ }
+ qhi = uint32(n>>32) / uint32(d)
+ n -= uint64(uint32(d)*qhi) << 32
+ } else {
+ qhi = 0
+ }
+
+ var rlo uint32
+ qlo := _div64by32(n, uint32(d), &rlo)
+ return uint64(qhi)<<32 + uint64(qlo), uint64(rlo)
+}
+
+func slowdodiv(n, d uint64) (q, r uint64) {
+ if d == 0 {
+ panicdivide()
+ }
+
+ // Set up the divisor and find the number of iterations needed.
+ capn := n
+ if n >= sign64 {
+ capn = sign64
+ }
+ i := 0
+ for d < capn {
+ d <<= 1
+ i++
+ }
+
+ for ; i >= 0; i-- {
+ q <<= 1
+ if n >= d {
+ n -= d
+ q |= 1
+ }
+ d >>= 1
+ }
+ return q, n
+}
diff --git a/src/pkg/runtime/vlrt_arm.c b/src/pkg/runtime/vlrt_arm.c
deleted file mode 100644
index 016fd7a357..0000000000
--- a/src/pkg/runtime/vlrt_arm.c
+++ /dev/null
@@ -1,808 +0,0 @@
-// Inferno's libkern/vlrt-arm.c
-// http://code.google.com/p/inferno-os/source/browse/libkern/vlrt-arm.c
-//
-// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
-// Revisions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com). All rights reserved.
-// 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.
-
-#include "../../cmd/ld/textflag.h"
-
-// declared here to avoid include of runtime.h
-void runtime·panicstring(char*);
-void runtime·panicdivide(void);
-
-typedef unsigned long ulong;
-typedef unsigned int uint;
-typedef unsigned short ushort;
-typedef unsigned char uchar;
-typedef signed char schar;
-
-#define SIGN(n) (1UL<<(n-1))
-
-typedef struct Vlong Vlong;
-struct Vlong
-{
- union
- {
- struct
- {
- ulong lo;
- ulong hi;
- };
- struct
- {
- ushort lols;
- ushort loms;
- ushort hils;
- ushort hims;
- };
- };
-};
-
-void runtime·abort(void);
-
-#pragma textflag NOSPLIT
-void
-_addv(Vlong *r, Vlong a, Vlong b)
-{
- r->lo = a.lo + b.lo;
- r->hi = a.hi + b.hi;
- if(r->lo < a.lo)
- r->hi++;
-}
-
-#pragma textflag NOSPLIT
-void
-_subv(Vlong *r, Vlong a, Vlong b)
-{
- r->lo = a.lo - b.lo;
- r->hi = a.hi - b.hi;
- if(r->lo > a.lo)
- r->hi--;
-}
-
-void
-_d2v(Vlong *y, double d)
-{
- union { double d; struct Vlong; } x;
- ulong xhi, xlo, ylo, yhi;
- int sh;
-
- x.d = d;
-
- xhi = (x.hi & 0xfffff) | 0x100000;
- xlo = x.lo;
- sh = 1075 - ((x.hi >> 20) & 0x7ff);
-
- ylo = 0;
- yhi = 0;
- if(sh >= 0) {
- /* v = (hi||lo) >> sh */
- if(sh < 32) {
- if(sh == 0) {
- ylo = xlo;
- yhi = xhi;
- } else {
- ylo = (xlo >> sh) | (xhi << (32-sh));
- yhi = xhi >> sh;
- }
- } else {
- if(sh == 32) {
- ylo = xhi;
- } else
- if(sh < 64) {
- ylo = xhi >> (sh-32);
- }
- }
- } else {
- /* v = (hi||lo) << -sh */
- sh = -sh;
- if(sh <= 11) {
- ylo = xlo << sh;
- yhi = (xhi << sh) | (xlo >> (32-sh));
- } else {
- /* overflow */
- yhi = d; /* causes something awful */
- }
- }
- if(x.hi & SIGN(32)) {
- if(ylo != 0) {
- ylo = -ylo;
- yhi = ~yhi;
- } else
- yhi = -yhi;
- }
-
- y->hi = yhi;
- y->lo = ylo;
-}
-
-void
-_f2v(Vlong *y, float f)
-{
- _d2v(y, f);
-}
-
-void
-runtime·float64toint64(double d, Vlong y)
-{
- _d2v(&y, d);
-}
-
-void
-runtime·float64touint64(double d, Vlong y)
-{
- _d2v(&y, d);
-}
-
-double
-_ul2d(ulong u)
-{
- // compensate for bug in c
- if(u & SIGN(32)) {
- u ^= SIGN(32);
- return 2147483648. + u;
- }
- return u;
-}
-
-double
-_v2d(Vlong x)
-{
- if(x.hi & SIGN(32)) {
- if(x.lo) {
- x.lo = -x.lo;
- x.hi = ~x.hi;
- } else
- x.hi = -x.hi;
- return -(_ul2d(x.hi)*4294967296. + _ul2d(x.lo));
- }
- return x.hi*4294967296. + _ul2d(x.lo);
-}
-
-float
-_v2f(Vlong x)
-{
- return _v2d(x);
-}
-
-void
-runtime·int64tofloat64(Vlong y, double d)
-{
- d = _v2d(y);
- USED(&d); // FLUSH
-}
-
-void
-runtime·uint64tofloat64(Vlong y, double d)
-{
- d = _ul2d(y.hi)*4294967296. + _ul2d(y.lo);
- USED(&d); // FLUSH
-}
-
-static void
-dodiv(Vlong num, Vlong den, Vlong *q, Vlong *r)
-{
- ulong numlo, numhi, denhi, denlo, quohi, quolo, t;
- int i;
-
- numhi = num.hi;
- numlo = num.lo;
- denhi = den.hi;
- denlo = den.lo;
-
- /*
- * get a divide by zero
- */
- if(denlo==0 && denhi==0) {
- runtime·panicdivide();
- }
-
- /*
- * set up the divisor and find the number of iterations needed
- */
- if(numhi >= SIGN(32)) {
- quohi = SIGN(32);
- quolo = 0;
- } else {
- quohi = numhi;
- quolo = numlo;
- }
- i = 0;
- while(denhi < quohi || (denhi == quohi && denlo < quolo)) {
- denhi = (denhi<<1) | (denlo>>31);
- denlo <<= 1;
- i++;
- }
-
- quohi = 0;
- quolo = 0;
- for(; i >= 0; i--) {
- quohi = (quohi<<1) | (quolo>>31);
- quolo <<= 1;
- if(numhi > denhi || (numhi == denhi && numlo >= denlo)) {
- t = numlo;
- numlo -= denlo;
- if(numlo > t)
- numhi--;
- numhi -= denhi;
- quolo |= 1;
- }
- denlo = (denlo>>1) | (denhi<<31);
- denhi >>= 1;
- }
-
- if(q) {
- q->lo = quolo;
- q->hi = quohi;
- }
- if(r) {
- r->lo = numlo;
- r->hi = numhi;
- }
-}
-
-void
-_divvu(Vlong *q, Vlong n, Vlong d)
-{
-
- if(n.hi == 0 && d.hi == 0) {
- q->hi = 0;
- q->lo = n.lo / d.lo;
- return;
- }
- dodiv(n, d, q, 0);
-}
-
-void
-runtime·uint64div(Vlong n, Vlong d, Vlong q)
-{
- _divvu(&q, n, d);
-}
-
-void
-_modvu(Vlong *r, Vlong n, Vlong d)
-{
-
- if(n.hi == 0 && d.hi == 0) {
- r->hi = 0;
- r->lo = n.lo % d.lo;
- return;
- }
- dodiv(n, d, 0, r);
-}
-
-void
-runtime·uint64mod(Vlong n, Vlong d, Vlong q)
-{
- _modvu(&q, n, d);
-}
-
-static void
-vneg(Vlong *v)
-{
-
- if(v->lo == 0) {
- v->hi = -v->hi;
- return;
- }
- v->lo = -v->lo;
- v->hi = ~v->hi;
-}
-
-void
-_divv(Vlong *q, Vlong n, Vlong d)
-{
- long nneg, dneg;
-
- if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) {
- if((long)n.lo == -0x80000000 && (long)d.lo == -1) {
- // special case: 32-bit -0x80000000 / -1 causes wrong sign
- q->lo = 0x80000000;
- q->hi = 0;
- return;
- }
- q->lo = (long)n.lo / (long)d.lo;
- q->hi = ((long)q->lo) >> 31;
- return;
- }
- nneg = n.hi >> 31;
- if(nneg)
- vneg(&n);
- dneg = d.hi >> 31;
- if(dneg)
- vneg(&d);
- dodiv(n, d, q, 0);
- if(nneg != dneg)
- vneg(q);
-}
-
-void
-runtime·int64div(Vlong n, Vlong d, Vlong q)
-{
- _divv(&q, n, d);
-}
-
-void
-_modv(Vlong *r, Vlong n, Vlong d)
-{
- long nneg, dneg;
-
- if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) {
- r->lo = (long)n.lo % (long)d.lo;
- r->hi = ((long)r->lo) >> 31;
- return;
- }
- nneg = n.hi >> 31;
- if(nneg)
- vneg(&n);
- dneg = d.hi >> 31;
- if(dneg)
- vneg(&d);
- dodiv(n, d, 0, r);
- if(nneg)
- vneg(r);
-}
-
-void
-runtime·int64mod(Vlong n, Vlong d, Vlong q)
-{
- _modv(&q, n, d);
-}
-
-void
-_rshav(Vlong *r, Vlong a, int b)
-{
- long t;
-
- t = a.hi;
- if(b >= 32) {
- r->hi = t>>31;
- if(b >= 64) {
- /* this is illegal re C standard */
- r->lo = t>>31;
- return;
- }
- r->lo = t >> (b-32);
- return;
- }
- if(b <= 0) {
- r->hi = t;
- r->lo = a.lo;
- return;
- }
- r->hi = t >> b;
- r->lo = (t << (32-b)) | (a.lo >> b);
-}
-
-void
-_rshlv(Vlong *r, Vlong a, int b)
-{
- ulong t;
-
- t = a.hi;
- if(b >= 32) {
- r->hi = 0;
- if(b >= 64) {
- /* this is illegal re C standard */
- r->lo = 0;
- return;
- }
- r->lo = t >> (b-32);
- return;
- }
- if(b <= 0) {
- r->hi = t;
- r->lo = a.lo;
- return;
- }
- r->hi = t >> b;
- r->lo = (t << (32-b)) | (a.lo >> b);
-}
-
-#pragma textflag NOSPLIT
-void
-_lshv(Vlong *r, Vlong a, int b)
-{
- if(b >= 32) {
- r->lo = 0;
- if(b >= 64) {
- /* this is illegal re C standard */
- r->hi = 0;
- return;
- }
- r->hi = a.lo << (b-32);
- return;
- }
- if(b <= 0) {
- r->lo = a.lo;
- r->hi = a.hi;
- return;
- }
- r->lo = a.lo << b;
- r->hi = (a.lo >> (32-b)) | (a.hi << b);
-}
-
-void
-_andv(Vlong *r, Vlong a, Vlong b)
-{
- r->hi = a.hi & b.hi;
- r->lo = a.lo & b.lo;
-}
-
-void
-_orv(Vlong *r, Vlong a, Vlong b)
-{
- r->hi = a.hi | b.hi;
- r->lo = a.lo | b.lo;
-}
-
-void
-_xorv(Vlong *r, Vlong a, Vlong b)
-{
- r->hi = a.hi ^ b.hi;
- r->lo = a.lo ^ b.lo;
-}
-
-void
-_vpp(Vlong *l, Vlong *r)
-{
-
- l->hi = r->hi;
- l->lo = r->lo;
- r->lo++;
- if(r->lo == 0)
- r->hi++;
-}
-
-void
-_vmm(Vlong *l, Vlong *r)
-{
-
- l->hi = r->hi;
- l->lo = r->lo;
- if(r->lo == 0)
- r->hi--;
- r->lo--;
-}
-
-void
-_ppv(Vlong *l, Vlong *r)
-{
-
- r->lo++;
- if(r->lo == 0)
- r->hi++;
- l->hi = r->hi;
- l->lo = r->lo;
-}
-
-void
-_mmv(Vlong *l, Vlong *r)
-{
-
- if(r->lo == 0)
- r->hi--;
- r->lo--;
- l->hi = r->hi;
- l->lo = r->lo;
-}
-
-#pragma textflag NOSPLIT
-void
-_vasop(Vlong *ret, void *lv, void fn(Vlong*, Vlong, Vlong), int type, Vlong rv)
-{
- Vlong t, u;
-
- u = *ret;
- switch(type) {
- default:
- runtime·abort();
- break;
-
- case 1: /* schar */
- t.lo = *(schar*)lv;
- t.hi = t.lo >> 31;
- fn(&u, t, rv);
- *(schar*)lv = u.lo;
- break;
-
- case 2: /* uchar */
- t.lo = *(uchar*)lv;
- t.hi = 0;
- fn(&u, t, rv);
- *(uchar*)lv = u.lo;
- break;
-
- case 3: /* short */
- t.lo = *(short*)lv;
- t.hi = t.lo >> 31;
- fn(&u, t, rv);
- *(short*)lv = u.lo;
- break;
-
- case 4: /* ushort */
- t.lo = *(ushort*)lv;
- t.hi = 0;
- fn(&u, t, rv);
- *(ushort*)lv = u.lo;
- break;
-
- case 9: /* int */
- t.lo = *(int*)lv;
- t.hi = t.lo >> 31;
- fn(&u, t, rv);
- *(int*)lv = u.lo;
- break;
-
- case 10: /* uint */
- t.lo = *(uint*)lv;
- t.hi = 0;
- fn(&u, t, rv);
- *(uint*)lv = u.lo;
- break;
-
- case 5: /* long */
- t.lo = *(long*)lv;
- t.hi = t.lo >> 31;
- fn(&u, t, rv);
- *(long*)lv = u.lo;
- break;
-
- case 6: /* ulong */
- t.lo = *(ulong*)lv;
- t.hi = 0;
- fn(&u, t, rv);
- *(ulong*)lv = u.lo;
- break;
-
- case 7: /* vlong */
- case 8: /* uvlong */
- fn(&u, *(Vlong*)lv, rv);
- *(Vlong*)lv = u;
- break;
- }
- *ret = u;
-}
-
-void
-_p2v(Vlong *ret, void *p)
-{
- long t;
-
- t = (ulong)p;
- ret->lo = t;
- ret->hi = 0;
-}
-
-void
-_sl2v(Vlong *ret, long sl)
-{
- long t;
-
- t = sl;
- ret->lo = t;
- ret->hi = t >> 31;
-}
-
-void
-_ul2v(Vlong *ret, ulong ul)
-{
- long t;
-
- t = ul;
- ret->lo = t;
- ret->hi = 0;
-}
-
-#pragma textflag NOSPLIT
-void
-_si2v(Vlong *ret, int si)
-{
- ret->lo = (long)si;
- ret->hi = (long)si >> 31;
-}
-
-void
-_ui2v(Vlong *ret, uint ui)
-{
- long t;
-
- t = ui;
- ret->lo = t;
- ret->hi = 0;
-}
-
-void
-_sh2v(Vlong *ret, long sh)
-{
- long t;
-
- t = (sh << 16) >> 16;
- ret->lo = t;
- ret->hi = t >> 31;
-}
-
-void
-_uh2v(Vlong *ret, ulong ul)
-{
- long t;
-
- t = ul & 0xffff;
- ret->lo = t;
- ret->hi = 0;
-}
-
-void
-_sc2v(Vlong *ret, long uc)
-{
- long t;
-
- t = (uc << 24) >> 24;
- ret->lo = t;
- ret->hi = t >> 31;
-}
-
-void
-_uc2v(Vlong *ret, ulong ul)
-{
- long t;
-
- t = ul & 0xff;
- ret->lo = t;
- ret->hi = 0;
-}
-
-long
-_v2sc(Vlong rv)
-{
- long t;
-
- t = rv.lo & 0xff;
- return (t << 24) >> 24;
-}
-
-long
-_v2uc(Vlong rv)
-{
-
- return rv.lo & 0xff;
-}
-
-long
-_v2sh(Vlong rv)
-{
- long t;
-
- t = rv.lo & 0xffff;
- return (t << 16) >> 16;
-}
-
-long
-_v2uh(Vlong rv)
-{
-
- return rv.lo & 0xffff;
-}
-
-long
-_v2sl(Vlong rv)
-{
-
- return rv.lo;
-}
-
-long
-_v2ul(Vlong rv)
-{
-
- return rv.lo;
-}
-
-#pragma textflag NOSPLIT
-long
-_v2si(Vlong rv)
-{
-
- return rv.lo;
-}
-
-long
-_v2ui(Vlong rv)
-{
-
- return rv.lo;
-}
-
-int
-_testv(Vlong rv)
-{
- return rv.lo || rv.hi;
-}
-
-int
-_eqv(Vlong lv, Vlong rv)
-{
- return lv.lo == rv.lo && lv.hi == rv.hi;
-}
-
-int
-_nev(Vlong lv, Vlong rv)
-{
- return lv.lo != rv.lo || lv.hi != rv.hi;
-}
-
-int
-_ltv(Vlong lv, Vlong rv)
-{
- return (long)lv.hi < (long)rv.hi ||
- (lv.hi == rv.hi && lv.lo < rv.lo);
-}
-
-int
-_lev(Vlong lv, Vlong rv)
-{
- return (long)lv.hi < (long)rv.hi ||
- (lv.hi == rv.hi && lv.lo <= rv.lo);
-}
-
-int
-_gtv(Vlong lv, Vlong rv)
-{
- return (long)lv.hi > (long)rv.hi ||
- (lv.hi == rv.hi && lv.lo > rv.lo);
-}
-
-#pragma textflag NOSPLIT
-int
-_gev(Vlong lv, Vlong rv)
-{
- return (long)lv.hi > (long)rv.hi ||
- (lv.hi == rv.hi && lv.lo >= rv.lo);
-}
-
-int
-_lov(Vlong lv, Vlong rv)
-{
- return lv.hi < rv.hi ||
- (lv.hi == rv.hi && lv.lo < rv.lo);
-}
-
-int
-_lsv(Vlong lv, Vlong rv)
-{
- return lv.hi < rv.hi ||
- (lv.hi == rv.hi && lv.lo <= rv.lo);
-}
-
-int
-_hiv(Vlong lv, Vlong rv)
-{
- return lv.hi > rv.hi ||
- (lv.hi == rv.hi && lv.lo > rv.lo);
-}
-
-int
-_hsv(Vlong lv, Vlong rv)
-{
- return lv.hi > rv.hi ||
- (lv.hi == rv.hi && lv.lo >= rv.lo);
-}
diff --git a/src/pkg/strconv/isprint.go b/src/pkg/strconv/isprint.go
index 91f1795356..80738ed711 100644
--- a/src/pkg/strconv/isprint.go
+++ b/src/pkg/strconv/isprint.go
@@ -3,20 +3,19 @@
// license that can be found in the LICENSE file.
// DO NOT EDIT. GENERATED BY
-// go run makeisprint.go >x && mv x isprint.go
+// go run makeisprint.go -output isprint.go
package strconv
-// (470+136+60)*2 + (218)*4 = 2204 bytes
+// (468+138+67)*2 + (326)*4 = 2650 bytes
var isPrint16 = []uint16{
0x0020, 0x007e,
0x00a1, 0x0377,
- 0x037a, 0x037e,
- 0x0384, 0x0527,
- 0x0531, 0x0556,
+ 0x037a, 0x037f,
+ 0x0384, 0x0556,
0x0559, 0x058a,
- 0x058f, 0x05c7,
+ 0x058d, 0x05c7,
0x05d0, 0x05ea,
0x05f0, 0x05f4,
0x0606, 0x061b,
@@ -27,7 +26,7 @@ var isPrint16 = []uint16{
0x0800, 0x082d,
0x0830, 0x085b,
0x085e, 0x085e,
- 0x08a0, 0x08ac,
+ 0x08a0, 0x08b2,
0x08e4, 0x098c,
0x098f, 0x0990,
0x0993, 0x09b2,
@@ -72,18 +71,17 @@ var isPrint16 = []uint16{
0x0bd0, 0x0bd0,
0x0bd7, 0x0bd7,
0x0be6, 0x0bfa,
- 0x0c01, 0x0c39,
+ 0x0c00, 0x0c39,
0x0c3d, 0x0c4d,
0x0c55, 0x0c59,
0x0c60, 0x0c63,
0x0c66, 0x0c6f,
- 0x0c78, 0x0c7f,
- 0x0c82, 0x0cb9,
+ 0x0c78, 0x0cb9,
0x0cbc, 0x0ccd,
0x0cd5, 0x0cd6,
0x0cde, 0x0ce3,
0x0ce6, 0x0cf2,
- 0x0d02, 0x0d3a,
+ 0x0d01, 0x0d3a,
0x0d3d, 0x0d4e,
0x0d57, 0x0d57,
0x0d60, 0x0d63,
@@ -94,6 +92,7 @@ var isPrint16 = []uint16{
0x0dc0, 0x0dc6,
0x0dca, 0x0dca,
0x0dcf, 0x0ddf,
+ 0x0de6, 0x0def,
0x0df2, 0x0df4,
0x0e01, 0x0e3a,
0x0e3f, 0x0e5b,
@@ -120,7 +119,7 @@ var isPrint16 = []uint16{
0x1380, 0x1399,
0x13a0, 0x13f4,
0x1400, 0x169c,
- 0x16a0, 0x16f0,
+ 0x16a0, 0x16f8,
0x1700, 0x1714,
0x1720, 0x1736,
0x1740, 0x1753,
@@ -133,8 +132,7 @@ var isPrint16 = []uint16{
0x1820, 0x1877,
0x1880, 0x18aa,
0x18b0, 0x18f5,
- 0x1900, 0x191c,
- 0x1920, 0x192b,
+ 0x1900, 0x192b,
0x1930, 0x193b,
0x1940, 0x1940,
0x1944, 0x196d,
@@ -147,6 +145,7 @@ var isPrint16 = []uint16{
0x1a7f, 0x1a89,
0x1a90, 0x1a99,
0x1aa0, 0x1aad,
+ 0x1ab0, 0x1abe,
0x1b00, 0x1b4b,
0x1b50, 0x1b7c,
0x1b80, 0x1bf3,
@@ -154,8 +153,8 @@ var isPrint16 = []uint16{
0x1c3b, 0x1c49,
0x1c4d, 0x1c7f,
0x1cc0, 0x1cc7,
- 0x1cd0, 0x1cf6,
- 0x1d00, 0x1de6,
+ 0x1cd0, 0x1cf9,
+ 0x1d00, 0x1df5,
0x1dfc, 0x1f15,
0x1f18, 0x1f1d,
0x1f20, 0x1f45,
@@ -168,21 +167,23 @@ var isPrint16 = []uint16{
0x2030, 0x205e,
0x2070, 0x2071,
0x2074, 0x209c,
- 0x20a0, 0x20ba,
+ 0x20a0, 0x20bd,
0x20d0, 0x20f0,
0x2100, 0x2189,
- 0x2190, 0x23f3,
+ 0x2190, 0x23fa,
0x2400, 0x2426,
0x2440, 0x244a,
- 0x2460, 0x2b4c,
- 0x2b50, 0x2b59,
+ 0x2460, 0x2b73,
+ 0x2b76, 0x2b95,
+ 0x2b98, 0x2bb9,
+ 0x2bbd, 0x2bd1,
0x2c00, 0x2cf3,
0x2cf9, 0x2d27,
0x2d2d, 0x2d2d,
0x2d30, 0x2d67,
0x2d6f, 0x2d70,
0x2d7f, 0x2d96,
- 0x2da0, 0x2e3b,
+ 0x2da0, 0x2e42,
0x2e80, 0x2ef3,
0x2f00, 0x2fd5,
0x2ff0, 0x2ffb,
@@ -196,11 +197,10 @@ var isPrint16 = []uint16{
0xa000, 0xa48c,
0xa490, 0xa4c6,
0xa4d0, 0xa62b,
- 0xa640, 0xa697,
- 0xa69f, 0xa6f7,
- 0xa700, 0xa793,
- 0xa7a0, 0xa7aa,
- 0xa7f8, 0xa82b,
+ 0xa640, 0xa6f7,
+ 0xa700, 0xa7ad,
+ 0xa7b0, 0xa7b1,
+ 0xa7f7, 0xa82b,
0xa830, 0xa839,
0xa840, 0xa877,
0xa880, 0xa8c4,
@@ -209,17 +209,16 @@ var isPrint16 = []uint16{
0xa900, 0xa953,
0xa95f, 0xa97c,
0xa980, 0xa9d9,
- 0xa9de, 0xa9df,
- 0xaa00, 0xaa36,
+ 0xa9de, 0xaa36,
0xaa40, 0xaa4d,
0xaa50, 0xaa59,
- 0xaa5c, 0xaa7b,
- 0xaa80, 0xaac2,
+ 0xaa5c, 0xaac2,
0xaadb, 0xaaf6,
0xab01, 0xab06,
0xab09, 0xab0e,
0xab11, 0xab16,
- 0xab20, 0xab2e,
+ 0xab20, 0xab5f,
+ 0xab64, 0xab65,
0xabc0, 0xabed,
0xabf0, 0xabf9,
0xac00, 0xd7a3,
@@ -235,7 +234,7 @@ var isPrint16 = []uint16{
0xfd92, 0xfdc7,
0xfdf0, 0xfdfd,
0xfe00, 0xfe19,
- 0xfe20, 0xfe26,
+ 0xfe20, 0xfe2d,
0xfe30, 0xfe6b,
0xfe70, 0xfefc,
0xff01, 0xffbe,
@@ -252,15 +251,12 @@ var isNotPrint16 = []uint16{
0x038b,
0x038d,
0x03a2,
+ 0x0530,
0x0560,
0x0588,
0x0590,
0x06dd,
0x083f,
- 0x08a1,
- 0x08ff,
- 0x0978,
- 0x0980,
0x0984,
0x09a9,
0x09b1,
@@ -294,10 +290,10 @@ var isNotPrint16 = []uint16{
0x0c0d,
0x0c11,
0x0c29,
- 0x0c34,
0x0c45,
0x0c49,
0x0c57,
+ 0x0c80,
0x0c84,
0x0c8d,
0x0c91,
@@ -345,7 +341,9 @@ var isNotPrint16 = []uint16{
0x170d,
0x176d,
0x1771,
+ 0x191f,
0x1a5f,
+ 0x1cf7,
0x1f58,
0x1f5a,
0x1f5c,
@@ -355,7 +353,7 @@ var isNotPrint16 = []uint16{
0x1fdc,
0x1ff5,
0x208f,
- 0x2700,
+ 0x2bc9,
0x2c2f,
0x2c5f,
0x2d26,
@@ -372,9 +370,12 @@ var isNotPrint16 = []uint16{
0x318f,
0x321f,
0x32ff,
+ 0xa69e,
0xa78f,
0xa9ce,
+ 0xa9ff,
0xab27,
+ 0xab2f,
0xfb37,
0xfb3d,
0xfb3f,
@@ -392,21 +393,31 @@ var isPrint32 = []uint32{
0x010080, 0x0100fa,
0x010100, 0x010102,
0x010107, 0x010133,
- 0x010137, 0x01018a,
+ 0x010137, 0x01018c,
0x010190, 0x01019b,
+ 0x0101a0, 0x0101a0,
0x0101d0, 0x0101fd,
0x010280, 0x01029c,
0x0102a0, 0x0102d0,
+ 0x0102e0, 0x0102fb,
0x010300, 0x010323,
0x010330, 0x01034a,
+ 0x010350, 0x01037a,
0x010380, 0x0103c3,
0x0103c8, 0x0103d5,
0x010400, 0x01049d,
0x0104a0, 0x0104a9,
+ 0x010500, 0x010527,
+ 0x010530, 0x010563,
+ 0x01056f, 0x01056f,
+ 0x010600, 0x010736,
+ 0x010740, 0x010755,
+ 0x010760, 0x010767,
0x010800, 0x010805,
0x010808, 0x010838,
0x01083c, 0x01083c,
- 0x01083f, 0x01085f,
+ 0x01083f, 0x01089e,
+ 0x0108a7, 0x0108af,
0x010900, 0x01091b,
0x01091f, 0x010939,
0x01093f, 0x01093f,
@@ -417,32 +428,72 @@ var isPrint32 = []uint32{
0x010a38, 0x010a3a,
0x010a3f, 0x010a47,
0x010a50, 0x010a58,
- 0x010a60, 0x010a7f,
+ 0x010a60, 0x010a9f,
+ 0x010ac0, 0x010ae6,
+ 0x010aeb, 0x010af6,
0x010b00, 0x010b35,
0x010b39, 0x010b55,
0x010b58, 0x010b72,
- 0x010b78, 0x010b7f,
+ 0x010b78, 0x010b91,
+ 0x010b99, 0x010b9c,
+ 0x010ba9, 0x010baf,
0x010c00, 0x010c48,
0x010e60, 0x010e7e,
0x011000, 0x01104d,
0x011052, 0x01106f,
- 0x011080, 0x0110c1,
+ 0x01107f, 0x0110c1,
0x0110d0, 0x0110e8,
0x0110f0, 0x0110f9,
0x011100, 0x011143,
+ 0x011150, 0x011176,
0x011180, 0x0111c8,
- 0x0111d0, 0x0111d9,
+ 0x0111cd, 0x0111cd,
+ 0x0111d0, 0x0111da,
+ 0x0111e1, 0x0111f4,
+ 0x011200, 0x01123d,
+ 0x0112b0, 0x0112ea,
+ 0x0112f0, 0x0112f9,
+ 0x011301, 0x01130c,
+ 0x01130f, 0x011310,
+ 0x011313, 0x011339,
+ 0x01133c, 0x011344,
+ 0x011347, 0x011348,
+ 0x01134b, 0x01134d,
+ 0x011357, 0x011357,
+ 0x01135d, 0x011363,
+ 0x011366, 0x01136c,
+ 0x011370, 0x011374,
+ 0x011480, 0x0114c7,
+ 0x0114d0, 0x0114d9,
+ 0x011580, 0x0115b5,
+ 0x0115b8, 0x0115c9,
+ 0x011600, 0x011644,
+ 0x011650, 0x011659,
0x011680, 0x0116b7,
0x0116c0, 0x0116c9,
- 0x012000, 0x01236e,
- 0x012400, 0x012462,
- 0x012470, 0x012473,
+ 0x0118a0, 0x0118f2,
+ 0x0118ff, 0x0118ff,
+ 0x011ac0, 0x011af8,
+ 0x012000, 0x012398,
+ 0x012400, 0x012474,
0x013000, 0x01342e,
0x016800, 0x016a38,
+ 0x016a40, 0x016a69,
+ 0x016a6e, 0x016a6f,
+ 0x016ad0, 0x016aed,
+ 0x016af0, 0x016af5,
+ 0x016b00, 0x016b45,
+ 0x016b50, 0x016b77,
+ 0x016b7d, 0x016b8f,
0x016f00, 0x016f44,
0x016f50, 0x016f7e,
0x016f8f, 0x016f9f,
0x01b000, 0x01b001,
+ 0x01bc00, 0x01bc6a,
+ 0x01bc70, 0x01bc7c,
+ 0x01bc80, 0x01bc88,
+ 0x01bc90, 0x01bc99,
+ 0x01bc9c, 0x01bc9f,
0x01d000, 0x01d0f5,
0x01d100, 0x01d126,
0x01d129, 0x01d172,
@@ -458,6 +509,8 @@ var isPrint32 = []uint32{
0x01d54a, 0x01d6a5,
0x01d6a8, 0x01d7cb,
0x01d7ce, 0x01d7ff,
+ 0x01e800, 0x01e8c4,
+ 0x01e8c7, 0x01e8d6,
0x01ee00, 0x01ee24,
0x01ee27, 0x01ee3b,
0x01ee42, 0x01ee42,
@@ -469,28 +522,30 @@ var isPrint32 = []uint32{
0x01f000, 0x01f02b,
0x01f030, 0x01f093,
0x01f0a0, 0x01f0ae,
- 0x01f0b1, 0x01f0be,
- 0x01f0c1, 0x01f0df,
- 0x01f100, 0x01f10a,
+ 0x01f0b1, 0x01f0f5,
+ 0x01f100, 0x01f10c,
0x01f110, 0x01f16b,
0x01f170, 0x01f19a,
0x01f1e6, 0x01f202,
0x01f210, 0x01f23a,
0x01f240, 0x01f248,
0x01f250, 0x01f251,
- 0x01f300, 0x01f320,
- 0x01f330, 0x01f37c,
- 0x01f380, 0x01f393,
- 0x01f3a0, 0x01f3ca,
- 0x01f3e0, 0x01f3f0,
- 0x01f400, 0x01f4fc,
- 0x01f500, 0x01f53d,
- 0x01f540, 0x01f543,
- 0x01f550, 0x01f567,
- 0x01f5fb, 0x01f640,
- 0x01f645, 0x01f64f,
- 0x01f680, 0x01f6c5,
+ 0x01f300, 0x01f32c,
+ 0x01f330, 0x01f37d,
+ 0x01f380, 0x01f3ce,
+ 0x01f3d4, 0x01f3f7,
+ 0x01f400, 0x01f54a,
+ 0x01f550, 0x01f642,
+ 0x01f645, 0x01f6cf,
+ 0x01f6e0, 0x01f6ec,
+ 0x01f6f0, 0x01f6f3,
0x01f700, 0x01f773,
+ 0x01f780, 0x01f7d4,
+ 0x01f800, 0x01f80b,
+ 0x01f810, 0x01f847,
+ 0x01f850, 0x01f859,
+ 0x01f860, 0x01f887,
+ 0x01f890, 0x01f8ad,
0x020000, 0x02a6d6,
0x02a700, 0x02b734,
0x02b740, 0x02b81d,
@@ -503,7 +558,6 @@ var isNotPrint32 = []uint16{ // add 0x10000 to each entry
0x0027,
0x003b,
0x003e,
- 0x031f,
0x039e,
0x0809,
0x0836,
@@ -513,6 +567,15 @@ var isNotPrint32 = []uint16{ // add 0x10000 to each entry
0x0a18,
0x10bd,
0x1135,
+ 0x1212,
+ 0x1304,
+ 0x1329,
+ 0x1331,
+ 0x1334,
+ 0x246f,
+ 0x6a5f,
+ 0x6b5a,
+ 0x6b62,
0xd455,
0xd49d,
0xd4ad,
@@ -552,11 +615,10 @@ var isNotPrint32 = []uint16{ // add 0x10000 to each entry
0xee8a,
0xeea4,
0xeeaa,
+ 0xf0c0,
0xf0d0,
0xf12f,
- 0xf336,
- 0xf3c5,
- 0xf43f,
- 0xf441,
- 0xf4f8,
+ 0xf4ff,
+ 0xf57a,
+ 0xf5a4,
}
diff --git a/src/pkg/strconv/makeisprint.go b/src/pkg/strconv/makeisprint.go
index 216159cc02..588d0a00b5 100644
--- a/src/pkg/strconv/makeisprint.go
+++ b/src/pkg/strconv/makeisprint.go
@@ -4,15 +4,26 @@
// +build ignore
-// makeisprint generates the tables for strconv's compact isPrint.
+//
+// usage:
+//
+// go run makeisprint.go -output isprint.go
+//
+
package main
import (
+ "bytes"
+ "flag"
"fmt"
- "os"
+ "go/format"
+ "io/ioutil"
+ "log"
"unicode"
)
+var filename = flag.String("output", "isprint.go", "output file name")
+
var (
range16 []uint16
except16 []uint16
@@ -110,6 +121,8 @@ func to16(x []uint32) []uint16 {
}
func main() {
+ flag.Parse()
+
rang, except := scan(0, 0xFFFF)
range16 = to16(rang)
except16 = to16(except)
@@ -117,49 +130,58 @@ func main() {
for i := rune(0); i <= unicode.MaxRune; i++ {
if isPrint(i) != unicode.IsPrint(i) {
- fmt.Fprintf(os.Stderr, "%U: isPrint=%v, want %v\n", i, isPrint(i), unicode.IsPrint(i))
- return
+ log.Fatalf("%U: isPrint=%v, want %v\n", i, isPrint(i), unicode.IsPrint(i))
}
}
- fmt.Printf(`// Copyright 2013 The Go Authors. All rights reserved.
+ var buf bytes.Buffer
+
+ fmt.Fprintf(&buf, `// 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.` + "\n\n")
- fmt.Printf("// DO NOT EDIT. GENERATED BY\n")
- fmt.Printf("// go run makeisprint.go >x && mv x isprint.go\n\n")
- fmt.Printf("package strconv\n\n")
+// license that can be found in the LICENSE file.`+"\n\n")
+ fmt.Fprintf(&buf, "// DO NOT EDIT. GENERATED BY\n")
+ fmt.Fprintf(&buf, "// go run makeisprint.go -output isprint.go\n\n")
+ fmt.Fprintf(&buf, "package strconv\n\n")
- fmt.Printf("// (%d+%d+%d)*2 + (%d)*4 = %d bytes\n\n",
+ fmt.Fprintf(&buf, "// (%d+%d+%d)*2 + (%d)*4 = %d bytes\n\n",
len(range16), len(except16), len(except32),
len(range32),
(len(range16)+len(except16)+len(except32))*2+
(len(range32))*4)
- fmt.Printf("var isPrint16 = []uint16{\n")
+ fmt.Fprintf(&buf, "var isPrint16 = []uint16{\n")
for i := 0; i < len(range16); i += 2 {
- fmt.Printf("\t%#04x, %#04x,\n", range16[i], range16[i+1])
+ fmt.Fprintf(&buf, "\t%#04x, %#04x,\n", range16[i], range16[i+1])
}
- fmt.Printf("}\n\n")
+ fmt.Fprintf(&buf, "}\n\n")
- fmt.Printf("var isNotPrint16 = []uint16{\n")
+ fmt.Fprintf(&buf, "var isNotPrint16 = []uint16{\n")
for _, r := range except16 {
- fmt.Printf("\t%#04x,\n", r)
+ fmt.Fprintf(&buf, "\t%#04x,\n", r)
}
- fmt.Printf("}\n\n")
+ fmt.Fprintf(&buf, "}\n\n")
- fmt.Printf("var isPrint32 = []uint32{\n")
+ fmt.Fprintf(&buf, "var isPrint32 = []uint32{\n")
for i := 0; i < len(range32); i += 2 {
- fmt.Printf("\t%#06x, %#06x,\n", range32[i], range32[i+1])
+ fmt.Fprintf(&buf, "\t%#06x, %#06x,\n", range32[i], range32[i+1])
}
- fmt.Printf("}\n\n")
+ fmt.Fprintf(&buf, "}\n\n")
- fmt.Printf("var isNotPrint32 = []uint16{ // add 0x10000 to each entry\n")
+ fmt.Fprintf(&buf, "var isNotPrint32 = []uint16{ // add 0x10000 to each entry\n")
for _, r := range except32 {
if r >= 0x20000 {
- fmt.Fprintf(os.Stderr, "%U too big for isNotPrint32\n", r)
- return
+ log.Fatalf("%U too big for isNotPrint32\n", r)
}
- fmt.Printf("\t%#04x,\n", r-0x10000)
+ fmt.Fprintf(&buf, "\t%#04x,\n", r-0x10000)
+ }
+ fmt.Fprintf(&buf, "}\n")
+
+ data, err := format.Source(buf.Bytes())
+ if err != nil {
+ log.Fatal(err)
+ }
+ err = ioutil.WriteFile(*filename, data, 0644)
+ if err != nil {
+ log.Fatal(err)
}
- fmt.Printf("}\n")
}
diff --git a/src/pkg/strconv/quote.go b/src/pkg/strconv/quote.go
index 8cdfc472f3..4469c688b0 100644
--- a/src/pkg/strconv/quote.go
+++ b/src/pkg/strconv/quote.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:generate go run makeisprint.go -output isprint.go
+
package strconv
import (
diff --git a/src/pkg/strings/strings.go b/src/pkg/strings/strings.go
index 53bcd6b98a..761f32a068 100644
--- a/src/pkg/strings/strings.go
+++ b/src/pkg/strings/strings.go
@@ -43,13 +43,29 @@ func explode(s string, n int) []string {
// primeRK is the prime base used in Rabin-Karp algorithm.
const primeRK = 16777619
-// hashstr returns the hash and the appropriate multiplicative
+// hashStr returns the hash and the appropriate multiplicative
// factor for use in Rabin-Karp algorithm.
-func hashstr(sep string) (uint32, uint32) {
+func hashStr(sep string) (uint32, uint32) {
hash := uint32(0)
for i := 0; i < len(sep); i++ {
hash = hash*primeRK + uint32(sep[i])
+ }
+ var pow, sq uint32 = 1, primeRK
+ for i := len(sep); i > 0; i >>= 1 {
+ if i&1 != 0 {
+ pow *= sq
+ }
+ sq *= sq
+ }
+ return hash, pow
+}
+// hashStrRev returns the hash of the reverse of sep and the
+// appropriate multiplicative factor for use in Rabin-Karp algorithm.
+func hashStrRev(sep string) (uint32, uint32) {
+ hash := uint32(0)
+ for i := len(sep) - 1; i >= 0; i-- {
+ hash = hash*primeRK + uint32(sep[i])
}
var pow, sq uint32 = 1, primeRK
for i := len(sep); i > 0; i >>= 1 {
@@ -85,7 +101,8 @@ func Count(s, sep string) int {
}
return 0
}
- hashsep, pow := hashstr(sep)
+ // Rabin-Karp search
+ hashsep, pow := hashStr(sep)
h := uint32(0)
for i := 0; i < len(sep); i++ {
h = h*primeRK + uint32(s[i])
@@ -139,8 +156,8 @@ func Index(s, sep string) int {
case n > len(s):
return -1
}
- // Hash sep.
- hashsep, pow := hashstr(sep)
+ // Rabin-Karp search
+ hashsep, pow := hashStr(sep)
var h uint32
for i := 0; i < n; i++ {
h = h*primeRK + uint32(s[i])
@@ -163,22 +180,41 @@ func Index(s, sep string) int {
// LastIndex returns the index of the last instance of sep in s, or -1 if sep is not present in s.
func LastIndex(s, sep string) int {
n := len(sep)
- if n == 0 {
+ switch {
+ case n == 0:
return len(s)
- }
- c := sep[0]
- if n == 1 {
+ case n == 1:
// special case worth making fast
+ c := sep[0]
for i := len(s) - 1; i >= 0; i-- {
if s[i] == c {
return i
}
}
return -1
+ case n == len(s):
+ if sep == s {
+ return 0
+ }
+ return -1
+ case n > len(s):
+ return -1
+ }
+ // Rabin-Karp search from the end of the string
+ hashsep, pow := hashStrRev(sep)
+ last := len(s) - n
+ var h uint32
+ for i := len(s) - 1; i >= last; i-- {
+ h = h*primeRK + uint32(s[i])
+ }
+ if h == hashsep && s[last:] == sep {
+ return last
}
- // n > 1
- for i := len(s) - n; i >= 0; i-- {
- if s[i] == c && s[i:i+n] == sep {
+ for i := last - 1; i >= 0; i-- {
+ h *= primeRK
+ h += uint32(s[i])
+ h -= pow * uint32(s[i+n])
+ if h == hashsep && s[i:i+n] == sep {
return i
}
}
@@ -635,6 +671,9 @@ func TrimSuffix(s, suffix string) string {
// Replace returns a copy of the string s with the first n
// non-overlapping instances of old replaced by new.
+// If old is empty, it matches at the beginning of the string
+// and after each UTF-8 sequence, yielding up to k+1 replacements
+// for a k-rune string.
// If n < 0, there is no limit on the number of replacements.
func Replace(s, old, new string, n int) string {
if old == new || n == 0 {
diff --git a/src/pkg/strings/strings_test.go b/src/pkg/strings/strings_test.go
index 27c0314fe8..7bb81ef3ca 100644
--- a/src/pkg/strings/strings_test.go
+++ b/src/pkg/strings/strings_test.go
@@ -168,6 +168,15 @@ func BenchmarkIndex(b *testing.B) {
}
}
+func BenchmarkLastIndex(b *testing.B) {
+ if got := Index(benchmarkString, "v"); got != 17 {
+ b.Fatalf("wrong index: expected 17, got=%d", got)
+ }
+ for i := 0; i < b.N; i++ {
+ LastIndex(benchmarkString, "v")
+ }
+}
+
func BenchmarkIndexByte(b *testing.B) {
if got := IndexByte(benchmarkString, 'v'); got != 17 {
b.Fatalf("wrong index: expected 17, got=%d", got)
@@ -1087,6 +1096,12 @@ func benchmarkIndexHard(b *testing.B, sep string) {
}
}
+func benchmarkLastIndexHard(b *testing.B, sep string) {
+ for i := 0; i < b.N; i++ {
+ LastIndex(benchInputHard, sep)
+ }
+}
+
func benchmarkCountHard(b *testing.B, sep string) {
for i := 0; i < b.N; i++ {
Count(benchInputHard, sep)
@@ -1097,6 +1112,10 @@ func BenchmarkIndexHard1(b *testing.B) { benchmarkIndexHard(b, "<>") }
func BenchmarkIndexHard2(b *testing.B) { benchmarkIndexHard(b, "</pre>") }
func BenchmarkIndexHard3(b *testing.B) { benchmarkIndexHard(b, "<b>hello world</b>") }
+func BenchmarkLastIndexHard1(b *testing.B) { benchmarkLastIndexHard(b, "<>") }
+func BenchmarkLastIndexHard2(b *testing.B) { benchmarkLastIndexHard(b, "</pre>") }
+func BenchmarkLastIndexHard3(b *testing.B) { benchmarkLastIndexHard(b, "<b>hello world</b>") }
+
func BenchmarkCountHard1(b *testing.B) { benchmarkCountHard(b, "<>") }
func BenchmarkCountHard2(b *testing.B) { benchmarkCountHard(b, "</pre>") }
func BenchmarkCountHard3(b *testing.B) { benchmarkCountHard(b, "<b>hello world</b>") }
diff --git a/src/pkg/sync/atomic/asm_386.s b/src/pkg/sync/atomic/asm_386.s
index 807c2f873b..740dfe76ba 100644
--- a/src/pkg/sync/atomic/asm_386.s
+++ b/src/pkg/sync/atomic/asm_386.s
@@ -4,7 +4,7 @@
// +build !race
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·SwapInt32(SB),NOSPLIT,$0-12
JMP ·SwapUint32(SB)
diff --git a/src/pkg/sync/atomic/asm_amd64.s b/src/pkg/sync/atomic/asm_amd64.s
index 77afa129ed..6e53ebedd2 100644
--- a/src/pkg/sync/atomic/asm_amd64.s
+++ b/src/pkg/sync/atomic/asm_amd64.s
@@ -4,7 +4,7 @@
// +build !race
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·SwapInt32(SB),NOSPLIT,$0-20
JMP ·SwapUint32(SB)
diff --git a/src/pkg/sync/atomic/asm_amd64p32.s b/src/pkg/sync/atomic/asm_amd64p32.s
index b24ae7a59e..d77cc2c088 100644
--- a/src/pkg/sync/atomic/asm_amd64p32.s
+++ b/src/pkg/sync/atomic/asm_amd64p32.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·SwapInt32(SB),NOSPLIT,$0-12
JMP ·SwapUint32(SB)
diff --git a/src/pkg/sync/atomic/asm_arm.s b/src/pkg/sync/atomic/asm_arm.s
index 7c8620a51b..47639a80ea 100644
--- a/src/pkg/sync/atomic/asm_arm.s
+++ b/src/pkg/sync/atomic/asm_arm.s
@@ -4,7 +4,7 @@
// +build !race
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// ARM atomic operations, for use by asm_$(GOOS)_arm.s.
diff --git a/src/pkg/sync/atomic/asm_freebsd_arm.s b/src/pkg/sync/atomic/asm_freebsd_arm.s
index db37f73bc4..06b975e897 100644
--- a/src/pkg/sync/atomic/asm_freebsd_arm.s
+++ b/src/pkg/sync/atomic/asm_freebsd_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// FreeBSD/ARM atomic operations.
// TODO(minux): this only supports ARMv6K or higher.
diff --git a/src/pkg/sync/atomic/asm_linux_arm.s b/src/pkg/sync/atomic/asm_linux_arm.s
index 27be57aa1d..63f1f9e38e 100644
--- a/src/pkg/sync/atomic/asm_linux_arm.s
+++ b/src/pkg/sync/atomic/asm_linux_arm.s
@@ -4,7 +4,7 @@
// +build !race
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// Linux/ARM atomic operations.
@@ -121,28 +121,8 @@ TEXT kernelCAS64<>(SB),NOSPLIT,$0-21
MOVW R0, 20(FP)
RET
-TEXT ·generalCAS64(SB),NOSPLIT,$20-21
- // bool runtime·cas64(uint64 volatile *addr, uint64 old, uint64 new)
- MOVW addr+0(FP), R0
- // trigger potential paging fault here,
- // because a fault in runtime.cas64 will hang.
- MOVW (R0), R2
- // make unaligned atomic access panic
- AND.S $7, R0, R1
- BEQ 2(PC)
- MOVW R1, (R1)
- MOVW R0, 4(R13)
- MOVW old_lo+4(FP), R1
- MOVW R1, 8(R13)
- MOVW old_hi+8(FP), R1
- MOVW R1, 12(R13)
- MOVW new_lo+12(FP), R2
- MOVW R2, 16(R13)
- MOVW new_hi+16(FP), R3
- MOVW R3, 20(R13)
- BL runtime·cas64(SB)
- MOVB R0, ret+20(FP)
- RET
+TEXT ·generalCAS64(SB),NOSPLIT,$0-21
+ B runtime·cas64(SB)
GLOBL armCAS64(SB), $4
diff --git a/src/pkg/sync/atomic/asm_nacl_arm.s b/src/pkg/sync/atomic/asm_nacl_arm.s
index 084ab71f82..76f6233364 100644
--- a/src/pkg/sync/atomic/asm_nacl_arm.s
+++ b/src/pkg/sync/atomic/asm_nacl_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// NaCl/ARM atomic operations.
// NaCl/ARM explicitly targets ARMv7A.
diff --git a/src/pkg/sync/atomic/asm_netbsd_arm.s b/src/pkg/sync/atomic/asm_netbsd_arm.s
index 64f4dbe714..dbe80898fd 100644
--- a/src/pkg/sync/atomic/asm_netbsd_arm.s
+++ b/src/pkg/sync/atomic/asm_netbsd_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../../cmd/ld/textflag.h"
+#include "textflag.h"
// NetBSD/ARM atomic operations.
// TODO(minux): this only supports ARMv6K or higher.
diff --git a/src/pkg/sync/atomic/doc.go b/src/pkg/sync/atomic/doc.go
index 17ba72fa17..10fb8c9177 100644
--- a/src/pkg/sync/atomic/doc.go
+++ b/src/pkg/sync/atomic/doc.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 !race
-
// Package atomic provides low-level atomic memory primitives
// useful for implementing synchronization algorithms.
//
diff --git a/src/pkg/sync/atomic/race.go b/src/pkg/sync/atomic/race.go
deleted file mode 100644
index 6cbbf12cb6..0000000000
--- a/src/pkg/sync/atomic/race.go
+++ /dev/null
@@ -1,276 +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.
-
-// +build race
-
-package atomic
-
-import (
- "runtime"
- "unsafe"
-)
-
-// We use runtime.RaceRead() inside of atomic operations to catch races
-// between atomic and non-atomic operations. It will also catch races
-// between Mutex.Lock() and mutex overwrite (mu = Mutex{}). Since we use
-// only RaceRead() we won't catch races with non-atomic loads.
-// Otherwise (if we use RaceWrite()) we will report races
-// between atomic operations (false positives).
-
-var mtx uint32 = 1 // same for all
-
-func SwapInt32(addr *int32, new int32) (old int32) {
- return int32(SwapUint32((*uint32)(unsafe.Pointer(addr)), uint32(new)))
-}
-
-func SwapUint32(addr *uint32, new uint32) (old uint32) {
- _ = *addr
- runtime.RaceSemacquire(&mtx)
- runtime.RaceRead(unsafe.Pointer(addr))
- runtime.RaceAcquire(unsafe.Pointer(addr))
- old = *addr
- *addr = new
- runtime.RaceReleaseMerge(unsafe.Pointer(addr))
- runtime.RaceSemrelease(&mtx)
- return
-}
-
-func SwapInt64(addr *int64, new int64) (old int64) {
- return int64(SwapUint64((*uint64)(unsafe.Pointer(addr)), uint64(new)))
-}
-
-func SwapUint64(addr *uint64, new uint64) (old uint64) {
- _ = *addr
- runtime.RaceSemacquire(&mtx)
- runtime.RaceRead(unsafe.Pointer(addr))
- runtime.RaceAcquire(unsafe.Pointer(addr))
- old = *addr
- *addr = new
- runtime.RaceReleaseMerge(unsafe.Pointer(addr))
- runtime.RaceSemrelease(&mtx)
- return
-}
-
-func SwapUintptr(addr *uintptr, new uintptr) (old uintptr) {
- return uintptr(SwapPointer((*unsafe.Pointer)(unsafe.Pointer(addr)), unsafe.Pointer(new)))
-}
-
-func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer) {
- _ = *addr
- runtime.RaceSemacquire(&mtx)
- runtime.RaceRead(unsafe.Pointer(addr))
- runtime.RaceAcquire(unsafe.Pointer(addr))
- old = *addr
- *addr = new
- runtime.RaceReleaseMerge(unsafe.Pointer(addr))
- runtime.RaceSemrelease(&mtx)
- return
-}
-
-func CompareAndSwapInt32(val *int32, old, new int32) bool {
- return CompareAndSwapUint32((*uint32)(unsafe.Pointer(val)), uint32(old), uint32(new))
-}
-
-func CompareAndSwapUint32(val *uint32, old, new uint32) (swapped bool) {
- _ = *val
- swapped = false
- runtime.RaceSemacquire(&mtx)
- runtime.RaceRead(unsafe.Pointer(val))
- runtime.RaceAcquire(unsafe.Pointer(val))
- if *val == old {
- *val = new
- swapped = true
- runtime.RaceReleaseMerge(unsafe.Pointer(val))
- }
- runtime.RaceSemrelease(&mtx)
- return
-}
-
-func CompareAndSwapInt64(val *int64, old, new int64) bool {
- return CompareAndSwapUint64((*uint64)(unsafe.Pointer(val)), uint64(old), uint64(new))
-}
-
-func CompareAndSwapUint64(val *uint64, old, new uint64) (swapped bool) {
- _ = *val
- swapped = false
- runtime.RaceSemacquire(&mtx)
- runtime.RaceRead(unsafe.Pointer(val))
- runtime.RaceAcquire(unsafe.Pointer(val))
- if *val == old {
- *val = new
- swapped = true
- runtime.RaceReleaseMerge(unsafe.Pointer(val))
- }
- runtime.RaceSemrelease(&mtx)
- return
-}
-
-func CompareAndSwapPointer(val *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool) {
- _ = *val
- swapped = false
- runtime.RaceSemacquire(&mtx)
- runtime.RaceRead(unsafe.Pointer(val))
- runtime.RaceAcquire(unsafe.Pointer(val))
- if *val == old {
- *val = new
- swapped = true
- runtime.RaceReleaseMerge(unsafe.Pointer(val))
- }
- runtime.RaceSemrelease(&mtx)
- return
-}
-
-func CompareAndSwapUintptr(val *uintptr, old, new uintptr) (swapped bool) {
- _ = *val
- swapped = false
- runtime.RaceSemacquire(&mtx)
- runtime.RaceRead(unsafe.Pointer(val))
- runtime.RaceAcquire(unsafe.Pointer(val))
- if *val == old {
- *val = new
- swapped = true
- runtime.RaceReleaseMerge(unsafe.Pointer(val))
- }
- runtime.RaceSemrelease(&mtx)
- return
-}
-
-func AddInt32(val *int32, delta int32) int32 {
- return int32(AddUint32((*uint32)(unsafe.Pointer(val)), uint32(delta)))
-}
-
-func AddUint32(val *uint32, delta uint32) (new uint32) {
- _ = *val
- runtime.RaceSemacquire(&mtx)
- runtime.RaceRead(unsafe.Pointer(val))
- runtime.RaceAcquire(unsafe.Pointer(val))
- *val = *val + delta
- new = *val
- runtime.RaceReleaseMerge(unsafe.Pointer(val))
- runtime.RaceSemrelease(&mtx)
-
- return
-}
-
-func AddInt64(val *int64, delta int64) int64 {
- return int64(AddUint64((*uint64)(unsafe.Pointer(val)), uint64(delta)))
-}
-
-func AddUint64(val *uint64, delta uint64) (new uint64) {
- _ = *val
- runtime.RaceSemacquire(&mtx)
- runtime.RaceRead(unsafe.Pointer(val))
- runtime.RaceAcquire(unsafe.Pointer(val))
- *val = *val + delta
- new = *val
- runtime.RaceReleaseMerge(unsafe.Pointer(val))
- runtime.RaceSemrelease(&mtx)
-
- return
-}
-
-func AddUintptr(val *uintptr, delta uintptr) (new uintptr) {
- _ = *val
- runtime.RaceSemacquire(&mtx)
- runtime.RaceRead(unsafe.Pointer(val))
- runtime.RaceAcquire(unsafe.Pointer(val))
- *val = *val + delta
- new = *val
- runtime.RaceReleaseMerge(unsafe.Pointer(val))
- runtime.RaceSemrelease(&mtx)
-
- return
-}
-
-func LoadInt32(addr *int32) int32 {
- return int32(LoadUint32((*uint32)(unsafe.Pointer(addr))))
-}
-
-func LoadUint32(addr *uint32) (val uint32) {
- _ = *addr
- runtime.RaceSemacquire(&mtx)
- runtime.RaceRead(unsafe.Pointer(addr))
- runtime.RaceAcquire(unsafe.Pointer(addr))
- val = *addr
- runtime.RaceSemrelease(&mtx)
- return
-}
-
-func LoadInt64(addr *int64) int64 {
- return int64(LoadUint64((*uint64)(unsafe.Pointer(addr))))
-}
-
-func LoadUint64(addr *uint64) (val uint64) {
- _ = *addr
- runtime.RaceSemacquire(&mtx)
- runtime.RaceRead(unsafe.Pointer(addr))
- runtime.RaceAcquire(unsafe.Pointer(addr))
- val = *addr
- runtime.RaceSemrelease(&mtx)
- return
-}
-
-func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer) {
- _ = *addr
- runtime.RaceSemacquire(&mtx)
- runtime.RaceRead(unsafe.Pointer(addr))
- runtime.RaceAcquire(unsafe.Pointer(addr))
- val = *addr
- runtime.RaceSemrelease(&mtx)
- return
-}
-
-func LoadUintptr(addr *uintptr) (val uintptr) {
- _ = *addr
- runtime.RaceSemacquire(&mtx)
- runtime.RaceRead(unsafe.Pointer(addr))
- runtime.RaceAcquire(unsafe.Pointer(addr))
- val = *addr
- runtime.RaceSemrelease(&mtx)
- return
-}
-
-func StoreInt32(addr *int32, val int32) {
- StoreUint32((*uint32)(unsafe.Pointer(addr)), uint32(val))
-}
-
-func StoreUint32(addr *uint32, val uint32) {
- _ = *addr
- runtime.RaceSemacquire(&mtx)
- runtime.RaceRead(unsafe.Pointer(addr))
- *addr = val
- runtime.RaceRelease(unsafe.Pointer(addr))
- runtime.RaceSemrelease(&mtx)
-}
-
-func StoreInt64(addr *int64, val int64) {
- StoreUint64((*uint64)(unsafe.Pointer(addr)), uint64(val))
-}
-
-func StoreUint64(addr *uint64, val uint64) {
- _ = *addr
- runtime.RaceSemacquire(&mtx)
- runtime.RaceRead(unsafe.Pointer(addr))
- *addr = val
- runtime.RaceRelease(unsafe.Pointer(addr))
- runtime.RaceSemrelease(&mtx)
-}
-
-func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer) {
- _ = *addr
- runtime.RaceSemacquire(&mtx)
- runtime.RaceRead(unsafe.Pointer(addr))
- *addr = val
- runtime.RaceRelease(unsafe.Pointer(addr))
- runtime.RaceSemrelease(&mtx)
-}
-
-func StoreUintptr(addr *uintptr, val uintptr) {
- _ = *addr
- runtime.RaceSemacquire(&mtx)
- runtime.RaceRead(unsafe.Pointer(addr))
- *addr = val
- runtime.RaceRelease(unsafe.Pointer(addr))
- runtime.RaceSemrelease(&mtx)
-}
diff --git a/src/pkg/sync/atomic/race.s b/src/pkg/sync/atomic/race.s
new file mode 100644
index 0000000000..4dadc9ed7e
--- /dev/null
+++ b/src/pkg/sync/atomic/race.s
@@ -0,0 +1,8 @@
+// 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 race
+
+// This file is here only to allow external functions.
+// The operations are implemented in src/pkg/runtime/race_amd64.s
diff --git a/src/pkg/sync/runtime.go b/src/pkg/sync/runtime.go
index 3bf47ea52a..3b866303a9 100644
--- a/src/pkg/sync/runtime.go
+++ b/src/pkg/sync/runtime.go
@@ -19,8 +19,12 @@ func runtime_Semacquire(s *uint32)
// library and should not be used directly.
func runtime_Semrelease(s *uint32)
-// Opaque representation of SyncSema in runtime/sema.goc.
-type syncSema [3]uintptr
+// Approximation of syncSema in runtime/sema.go.
+type syncSema struct {
+ lock uintptr
+ head unsafe.Pointer
+ tail unsafe.Pointer
+}
// Syncsemacquire waits for a pairing Syncsemrelease on the same semaphore s.
func runtime_Syncsemacquire(s *syncSema)
diff --git a/src/pkg/sync/waitgroup.go b/src/pkg/sync/waitgroup.go
index 4c64dca393..92cc57d2cc 100644
--- a/src/pkg/sync/waitgroup.go
+++ b/src/pkg/sync/waitgroup.go
@@ -37,10 +37,13 @@ type WaitGroup struct {
// If the counter becomes zero, all goroutines blocked on Wait are released.
// If the counter goes negative, Add panics.
//
-// Note that calls with positive delta must happen before the call to Wait,
-// or else Wait may wait for too small a group. Typically this means the calls
-// to Add should execute before the statement creating the goroutine or
-// other event to be waited for. See the WaitGroup example.
+// Note that calls with a positive delta that occur when the counter is zero
+// must happen before a Wait. Calls with a negative delta, or calls with a
+// positive delta that start when the counter is greater than zero, may happen
+// at any time.
+// Typically this means the calls to Add should execute before the statement
+// creating the goroutine or other event to be waited for.
+// See the WaitGroup example.
func (wg *WaitGroup) Add(delta int) {
if raceenabled {
_ = wg.m.state // trigger nil deref early
diff --git a/src/pkg/syscall/asm_darwin_386.s b/src/pkg/syscall/asm_darwin_386.s
index 9b4dfa81df..dfe94fb526 100644
--- a/src/pkg/syscall/asm_darwin_386.s
+++ b/src/pkg/syscall/asm_darwin_386.s
@@ -5,7 +5,7 @@
// TODO(rsc): Rewrite all nn(SP) references into name+(nn-8)(FP)
// so that go vet can check that they are correct.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
//
// System call support for 386, Darwin
diff --git a/src/pkg/syscall/asm_darwin_amd64.s b/src/pkg/syscall/asm_darwin_amd64.s
index 19ea05be72..d6f1c96f5f 100644
--- a/src/pkg/syscall/asm_darwin_amd64.s
+++ b/src/pkg/syscall/asm_darwin_amd64.s
@@ -5,7 +5,7 @@
// TODO(rsc): Rewrite all nn(SP) references into name+(nn-8)(FP)
// so that go vet can check that they are correct.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
//
// System call support for AMD64, Darwin
diff --git a/src/pkg/syscall/asm_dragonfly_386.s b/src/pkg/syscall/asm_dragonfly_386.s
index d24216fddc..37d655fba9 100644
--- a/src/pkg/syscall/asm_dragonfly_386.s
+++ b/src/pkg/syscall/asm_dragonfly_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
//
// System call support for 386, FreeBSD
diff --git a/src/pkg/syscall/asm_dragonfly_amd64.s b/src/pkg/syscall/asm_dragonfly_amd64.s
index 31d107490a..c8434f96b7 100644
--- a/src/pkg/syscall/asm_dragonfly_amd64.s
+++ b/src/pkg/syscall/asm_dragonfly_amd64.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
//
// System call support for AMD64, DragonFly
diff --git a/src/pkg/syscall/asm_freebsd_386.s b/src/pkg/syscall/asm_freebsd_386.s
index 91a46b1062..f50b5a09bf 100644
--- a/src/pkg/syscall/asm_freebsd_386.s
+++ b/src/pkg/syscall/asm_freebsd_386.s
@@ -5,7 +5,7 @@
// TODO(rsc): Rewrite all nn(SP) references into name+(nn-8)(FP)
// so that go vet can check that they are correct.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
//
// System call support for 386, FreeBSD
diff --git a/src/pkg/syscall/asm_freebsd_amd64.s b/src/pkg/syscall/asm_freebsd_amd64.s
index 7abb36828a..58cbfe1a99 100644
--- a/src/pkg/syscall/asm_freebsd_amd64.s
+++ b/src/pkg/syscall/asm_freebsd_amd64.s
@@ -5,7 +5,7 @@
// TODO(rsc): Rewrite all nn(SP) references into name+(nn-8)(FP)
// so that go vet can check that they are correct.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
//
// System call support for AMD64, FreeBSD
diff --git a/src/pkg/syscall/asm_freebsd_arm.s b/src/pkg/syscall/asm_freebsd_arm.s
index c01ce6febf..5eb40334b9 100644
--- a/src/pkg/syscall/asm_freebsd_arm.s
+++ b/src/pkg/syscall/asm_freebsd_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
//
// System call support for ARM, FreeBSD
diff --git a/src/pkg/syscall/asm_linux_386.s b/src/pkg/syscall/asm_linux_386.s
index 30b22073df..05cf89aa22 100644
--- a/src/pkg/syscall/asm_linux_386.s
+++ b/src/pkg/syscall/asm_linux_386.s
@@ -5,7 +5,7 @@
// TODO(rsc): Rewrite all nn(SP) references into name+(nn-8)(FP)
// so that go vet can check that they are correct.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
//
// System calls for 386, Linux
diff --git a/src/pkg/syscall/asm_linux_amd64.s b/src/pkg/syscall/asm_linux_amd64.s
index 995b60ecd0..514693283b 100644
--- a/src/pkg/syscall/asm_linux_amd64.s
+++ b/src/pkg/syscall/asm_linux_amd64.s
@@ -5,7 +5,7 @@
// TODO(rsc): Rewrite all nn(SP) references into name+(nn-8)(FP)
// so that go vet can check that they are correct.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
//
// System calls for AMD64, Linux
diff --git a/src/pkg/syscall/asm_linux_arm.s b/src/pkg/syscall/asm_linux_arm.s
index a28bc6cfc0..3be0a13e17 100644
--- a/src/pkg/syscall/asm_linux_arm.s
+++ b/src/pkg/syscall/asm_linux_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
//
// System calls for arm, Linux
@@ -18,6 +18,9 @@ TEXT ·Syscall(SB),NOSPLIT,$0-28
MOVW 8(SP), R0
MOVW 12(SP), R1
MOVW 16(SP), R2
+ MOVW $0, R3
+ MOVW $0, R4
+ MOVW $0, R5
SWI $0
MOVW $0xfffff001, R1
CMP R1, R0
diff --git a/src/pkg/syscall/asm_nacl_386.s b/src/pkg/syscall/asm_nacl_386.s
index de7c3cc5da..3c86decd39 100644
--- a/src/pkg/syscall/asm_nacl_386.s
+++ b/src/pkg/syscall/asm_nacl_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
#include "../runtime/syscall_nacl.h"
//
diff --git a/src/pkg/syscall/asm_nacl_amd64p32.s b/src/pkg/syscall/asm_nacl_amd64p32.s
index de030ec806..b91e09bd6d 100644
--- a/src/pkg/syscall/asm_nacl_amd64p32.s
+++ b/src/pkg/syscall/asm_nacl_amd64p32.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
#include "../runtime/syscall_nacl.h"
//
diff --git a/src/pkg/syscall/asm_nacl_arm.s b/src/pkg/syscall/asm_nacl_arm.s
index ffc48ceaa8..cc4b9cd2d6 100644
--- a/src/pkg/syscall/asm_nacl_arm.s
+++ b/src/pkg/syscall/asm_nacl_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
#include "../runtime/syscall_nacl.h"
//
diff --git a/src/pkg/syscall/asm_netbsd_386.s b/src/pkg/syscall/asm_netbsd_386.s
index 40b30b4053..c58263254a 100644
--- a/src/pkg/syscall/asm_netbsd_386.s
+++ b/src/pkg/syscall/asm_netbsd_386.s
@@ -5,7 +5,7 @@
// TODO(rsc): Rewrite all nn(SP) references into name+(nn-8)(FP)
// so that go vet can check that they are correct.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
//
// System call support for 386, NetBSD
diff --git a/src/pkg/syscall/asm_netbsd_amd64.s b/src/pkg/syscall/asm_netbsd_amd64.s
index 94ad0284ad..8285382ce2 100644
--- a/src/pkg/syscall/asm_netbsd_amd64.s
+++ b/src/pkg/syscall/asm_netbsd_amd64.s
@@ -5,7 +5,7 @@
// TODO(rsc): Rewrite all nn(SP) references into name+(nn-8)(FP)
// so that go vet can check that they are correct.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
//
// System call support for AMD64, NetBSD
diff --git a/src/pkg/syscall/asm_netbsd_arm.s b/src/pkg/syscall/asm_netbsd_arm.s
index 2c0d65401a..b061180491 100644
--- a/src/pkg/syscall/asm_netbsd_arm.s
+++ b/src/pkg/syscall/asm_netbsd_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
//
// System call support for ARM, NetBSD
diff --git a/src/pkg/syscall/asm_openbsd_386.s b/src/pkg/syscall/asm_openbsd_386.s
index 7dd2e373f7..17fbb65c80 100644
--- a/src/pkg/syscall/asm_openbsd_386.s
+++ b/src/pkg/syscall/asm_openbsd_386.s
@@ -5,7 +5,7 @@
// TODO(rsc): Rewrite all nn(SP) references into name+(nn-8)(FP)
// so that go vet can check that they are correct.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
//
// System call support for 386, OpenBSD
diff --git a/src/pkg/syscall/asm_openbsd_amd64.s b/src/pkg/syscall/asm_openbsd_amd64.s
index e127bf220c..fe61482cd5 100644
--- a/src/pkg/syscall/asm_openbsd_amd64.s
+++ b/src/pkg/syscall/asm_openbsd_amd64.s
@@ -5,7 +5,7 @@
// TODO(rsc): Rewrite all nn(SP) references into name+(nn-8)(FP)
// so that go vet can check that they are correct.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
//
// System call support for AMD64, OpenBSD
diff --git a/src/pkg/syscall/asm_plan9_386.s b/src/pkg/syscall/asm_plan9_386.s
index f8c07c407a..aaa8b9a692 100644
--- a/src/pkg/syscall/asm_plan9_386.s
+++ b/src/pkg/syscall/asm_plan9_386.s
@@ -5,7 +5,7 @@
// TODO(rsc): Rewrite all nn(SP) references into name+(nn-8)(FP)
// so that go vet can check that they are correct.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
//
// System call support for 386, Plan 9
diff --git a/src/pkg/syscall/asm_plan9_amd64.s b/src/pkg/syscall/asm_plan9_amd64.s
index 220ea68002..22dc5f9576 100644
--- a/src/pkg/syscall/asm_plan9_amd64.s
+++ b/src/pkg/syscall/asm_plan9_amd64.s
@@ -5,7 +5,7 @@
// TODO(rsc): Rewrite all nn(SP) references into name+(nn-8)(FP)
// so that go vet can check that they are correct.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
//
// System call support for Plan 9
diff --git a/src/pkg/syscall/asm_solaris_amd64.s b/src/pkg/syscall/asm_solaris_amd64.s
index 3735890fa9..d0d271c76b 100644
--- a/src/pkg/syscall/asm_solaris_amd64.s
+++ b/src/pkg/syscall/asm_solaris_amd64.s
@@ -2,6 +2,80 @@
// 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 amd64, Solaris are implemented in ../runtime/syscall_solaris.goc
+// System calls for solaris/amd64 are implemented in ../runtime/syscall_solaris.go
//
+
+TEXT ·sysvicall6(SB),NOSPLIT,$0
+ JMP runtime·syscall_sysvicall6(SB)
+
+TEXT ·rawSysvicall6(SB),NOSPLIT,$0
+ JMP runtime·syscall_rawsysvicall6(SB)
+
+TEXT ·chdir(SB),NOSPLIT,$0
+ JMP runtime·syscall_chdir(SB)
+
+TEXT ·chroot1(SB),NOSPLIT,$0
+ JMP runtime·syscall_chroot(SB)
+
+TEXT ·close(SB),NOSPLIT,$0
+ JMP runtime·syscall_close(SB)
+
+TEXT ·dlopen(SB),NOSPLIT,$0
+ JMP runtime·syscall_dlopen(SB)
+
+TEXT ·dlclose(SB),NOSPLIT,$0
+ JMP runtime·syscall_dlclose(SB)
+
+TEXT ·dlsym(SB),NOSPLIT,$0
+ JMP runtime·syscall_dlsym(SB)
+
+TEXT ·execve(SB),NOSPLIT,$0
+ JMP runtime·syscall_execve(SB)
+
+TEXT ·exit(SB),NOSPLIT,$0
+ JMP runtime·syscall_exit(SB)
+
+TEXT ·fcntl1(SB),NOSPLIT,$0
+ JMP runtime·syscall_fcntl(SB)
+
+TEXT ·forkx(SB),NOSPLIT,$0
+ JMP runtime·syscall_forkx(SB)
+
+TEXT ·gethostname(SB),NOSPLIT,$0
+ JMP runtime·syscall_gethostname(SB)
+
+TEXT ·ioctl(SB),NOSPLIT,$0
+ JMP runtime·syscall_ioctl(SB)
+
+TEXT ·pipe(SB),NOSPLIT,$0
+ JMP runtime·syscall_pipe(SB)
+
+TEXT ·RawSyscall(SB),NOSPLIT,$0
+ JMP runtime·syscall_rawsyscall(SB)
+
+TEXT ·setgid(SB),NOSPLIT,$0
+ JMP runtime·syscall_setgid(SB)
+
+TEXT ·setgroups1(SB),NOSPLIT,$0
+ JMP runtime·syscall_setgroups(SB)
+
+TEXT ·setsid(SB),NOSPLIT,$0
+ JMP runtime·syscall_setsid(SB)
+
+TEXT ·setuid(SB),NOSPLIT,$0
+ JMP runtime·syscall_setuid(SB)
+
+TEXT ·setpgid(SB),NOSPLIT,$0
+ JMP runtime·syscall_setpgid(SB)
+
+TEXT ·Syscall(SB),NOSPLIT,$0
+ JMP runtime·syscall_syscall(SB)
+
+TEXT ·wait4(SB),NOSPLIT,$0
+ JMP runtime·syscall_wait4(SB)
+
+TEXT ·write1(SB),NOSPLIT,$0
+ JMP runtime·syscall_write(SB)
diff --git a/src/pkg/syscall/asm_windows.s b/src/pkg/syscall/asm_windows.s
new file mode 100644
index 0000000000..abb6641a25
--- /dev/null
+++ b/src/pkg/syscall/asm_windows.s
@@ -0,0 +1,13 @@
+// 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.
+
+//
+// System calls for Windows are implemented in ../runtime/syscall_windows.goc
+//
+
+#include "textflag.h"
+
+// func compileCallback(fn interface{}, cleanstack bool) uintptr
+TEXT ·compileCallback(SB),NOSPLIT,$0
+ JMP runtime·compileCallback(SB)
diff --git a/src/pkg/syscall/asm_windows_386.s b/src/pkg/syscall/asm_windows_386.s
deleted file mode 100644
index 8b52fa9851..0000000000
--- a/src/pkg/syscall/asm_windows_386.s
+++ /dev/null
@@ -1,7 +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.
-
-//
-// System calls for 386, Windows are implemented in ../runtime/syscall_windows.goc
-//
diff --git a/src/pkg/syscall/asm_windows_amd64.s b/src/pkg/syscall/asm_windows_amd64.s
deleted file mode 100644
index 5813404d17..0000000000
--- a/src/pkg/syscall/asm_windows_amd64.s
+++ /dev/null
@@ -1,7 +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.
-
-//
-// System calls for amd64, Windows are implemented in ../runtime/syscall_windows.goc
-//
diff --git a/src/pkg/syscall/exec_windows.go b/src/pkg/syscall/exec_windows.go
index 82abc0715e..936aeb577b 100644
--- a/src/pkg/syscall/exec_windows.go
+++ b/src/pkg/syscall/exec_windows.go
@@ -129,9 +129,8 @@ func SetNonblock(fd Handle, nonblocking bool) (err error) {
return nil
}
-// getFullPath retrieves the full path of the specified file.
-// Just a wrapper for Windows GetFullPathName api.
-func getFullPath(name string) (path string, err error) {
+// FullPath retrieves the full path of the specified file.
+func FullPath(name string) (path string, err error) {
p, err := UTF16PtrFromString(name)
if err != nil {
return "", err
@@ -160,7 +159,7 @@ func isSlash(c uint8) bool {
}
func normalizeDir(dir string) (name string, err error) {
- ndir, err := getFullPath(dir)
+ ndir, err := FullPath(dir)
if err != nil {
return "", err
}
@@ -199,9 +198,9 @@ func joinExeDirAndFName(dir, p string) (name string, err error) {
return "", err
}
if volToUpper(int(p[0])) == volToUpper(int(d[0])) {
- return getFullPath(d + "\\" + p[2:])
+ return FullPath(d + "\\" + p[2:])
} else {
- return getFullPath(p)
+ return FullPath(p)
}
}
} else {
@@ -211,9 +210,9 @@ func joinExeDirAndFName(dir, p string) (name string, err error) {
return "", err
}
if isSlash(p[0]) {
- return getFullPath(d[:2] + p)
+ return FullPath(d[:2] + p)
} else {
- return getFullPath(d + "\\" + p)
+ return FullPath(d + "\\" + p)
}
}
// we shouldn't be here
diff --git a/src/pkg/syscall/mkall.sh b/src/pkg/syscall/mkall.sh
index ac1c20f036..febd5a2625 100755
--- a/src/pkg/syscall/mkall.sh
+++ b/src/pkg/syscall/mkall.sh
@@ -259,7 +259,7 @@ esac
case "$GOOS" in
windows)
echo "GOOS= GOARCH= go build mksyscall_windows.go"
- echo "./mksyscall_windows syscall_windows.go security_windows.go $GOOSARCH_in |gofmt >zsyscall_$GOOSARCH.go"
+ echo "./mksyscall_windows syscall_windows.go security_windows.go |gofmt >zsyscall_windows.go"
echo "rm -f ./mksyscall_windows"
;;
*)
diff --git a/src/pkg/syscall/mkall_windows.bat b/src/pkg/syscall/mkall_windows.bat
index a4a3f16748..9c91a1064d 100644
--- a/src/pkg/syscall/mkall_windows.bat
+++ b/src/pkg/syscall/mkall_windows.bat
@@ -8,14 +8,8 @@ echo mkall_windows.bat must be run from src\pkg\syscall directory
goto :end
:dirok
-if "%1"=="386" goto :paramok
-if "%1"=="amd64" goto :paramok
-echo parameters must be 386 or amd64
-goto :end
-:paramok
-
go build mksyscall_windows.go
-.\mksyscall_windows syscall_windows.go security_windows.go syscall_windows_%1.go |gofmt >zsyscall_windows_%1.go
+.\mksyscall_windows syscall_windows.go security_windows.go |gofmt >zsyscall_windows.go
del mksyscall_windows.exe
-:end \ No newline at end of file
+:end
diff --git a/src/pkg/syscall/net_nacl.go b/src/pkg/syscall/net_nacl.go
index b9488f48d1..b5cb530306 100644
--- a/src/pkg/syscall/net_nacl.go
+++ b/src/pkg/syscall/net_nacl.go
@@ -18,11 +18,12 @@ import (
// Really for use by package time, but we cannot import time here.
type runtimeTimer struct {
- i int32
+ i int
when int64
period int64
- f func(int64, interface{}) // NOTE: must not be closure
+ f func(interface{}, uintptr) // NOTE: must not be closure
arg interface{}
+ seq uintptr
}
func startTimer(*runtimeTimer)
@@ -49,7 +50,7 @@ func (t *timer) stop() {
stopTimer(&t.r)
}
-func timerExpired(now int64, i interface{}) {
+func timerExpired(i interface{}, seq uintptr) {
t := i.(*timer)
go func() {
t.q.Lock()
diff --git a/src/pkg/syscall/so_solaris.go b/src/pkg/syscall/so_solaris.go
index 659cd67c16..bf1b756049 100644
--- a/src/pkg/syscall/so_solaris.go
+++ b/src/pkg/syscall/so_solaris.go
@@ -19,7 +19,7 @@ type soError struct {
func (e *soError) Error() string { return e.Msg }
-// Implemented in ../runtime/syscall_solaris.goc.
+// Implemented in asm_solaris_amd64.s.
func rawSysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
func sysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
func dlclose(handle uintptr) (err Errno)
diff --git a/src/pkg/syscall/syscall.go b/src/pkg/syscall/syscall.go
index f7473fd5aa..3fd95798f3 100644
--- a/src/pkg/syscall/syscall.go
+++ b/src/pkg/syscall/syscall.go
@@ -17,6 +17,13 @@
// These calls return err == nil to indicate success; otherwise
// err is an operating system error describing the failure.
// On most systems, that error has type syscall.Errno.
+//
+// NOTE: This package is locked down. Code outside the standard
+// Go repository should be migrated to use the corresponding
+// package in the go.sys subrepository. That is also where updates
+// required by new systems or versions should be applied.
+// See https://golang.org/s/go1.4-syscall for more information.
+//
package syscall
// StringByteSlice is deprecated. Use ByteSliceFromString instead.
diff --git a/src/pkg/syscall/syscall_bsd.go b/src/pkg/syscall/syscall_bsd.go
index af563910b1..2556fa8746 100644
--- a/src/pkg/syscall/syscall_bsd.go
+++ b/src/pkg/syscall/syscall_bsd.go
@@ -68,7 +68,40 @@ func ReadDirent(fd int, buf []byte) (n int, err error) {
// actual system call is getdirentries64, 64 is a good guess.
// TODO(rsc): Can we use a single global basep for all calls?
var base = (*uintptr)(unsafe.Pointer(new(uint64)))
- return Getdirentries(fd, buf, base)
+ n, err = Getdirentries(fd, buf, base)
+
+ // On OS X 10.10 Yosemite, if you have a directory that can be returned
+ // in a single getdirentries64 call (for example, a directory with one file),
+ // and you read from the directory at EOF twice, you get EOF both times:
+ // fd = open("dir")
+ // getdirentries64(fd) // returns data
+ // getdirentries64(fd) // returns 0 (EOF)
+ // getdirentries64(fd) // returns 0 (EOF)
+ //
+ // But if you remove the file in the middle between the two calls, the
+ // second call returns an error instead.
+ // fd = open("dir")
+ // getdirentries64(fd) // returns data
+ // getdirentries64(fd) // returns 0 (EOF)
+ // remove("dir/file")
+ // getdirentries64(fd) // returns ENOENT/EINVAL
+ //
+ // Whether you get ENOENT or EINVAL depends on exactly what was
+ // in the directory. It is deterministic, just data-dependent.
+ //
+ // This only happens in small directories. A directory containing more data
+ // than fits in a 4k getdirentries64 call will return EOF correctly.
+ // (It's not clear if the criteria is that the directory be split across multiple
+ // getdirentries64 calls or that it be split across multiple file system blocks.)
+ //
+ // We could change package os to avoid the second read at EOF,
+ // and maybe we should, but that's a bit involved.
+ // For now, treat the EINVAL/ENOENT as EOF.
+ if runtime.GOOS == "darwin" && (err == EINVAL || err == ENOENT) {
+ err = nil
+ }
+
+ return
}
// Wait status is 7 bits at bottom, either 0 (exited),
diff --git a/src/pkg/syscall/syscall_dragonfly.go b/src/pkg/syscall/syscall_dragonfly.go
index b17e77da8a..39c51df7ed 100644
--- a/src/pkg/syscall/syscall_dragonfly.go
+++ b/src/pkg/syscall/syscall_dragonfly.go
@@ -183,8 +183,6 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
//sys Setpriority(which int, who int, prio int) (err error)
//sysnb Setregid(rgid int, egid int) (err error)
//sysnb Setreuid(ruid int, euid int) (err error)
-//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
-//sysnb Setresuid(ruid int, euid int, suid int) (err error)
//sysnb Setrlimit(which int, lim *Rlimit) (err error)
//sysnb Setsid() (pid int, err error)
//sysnb Settimeofday(tp *Timeval) (err error)
diff --git a/src/pkg/syscall/syscall_freebsd.go b/src/pkg/syscall/syscall_freebsd.go
index 959d46b4d4..3d834f52b5 100644
--- a/src/pkg/syscall/syscall_freebsd.go
+++ b/src/pkg/syscall/syscall_freebsd.go
@@ -205,8 +205,6 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
//sys Setpriority(which int, who int, prio int) (err error)
//sysnb Setregid(rgid int, egid int) (err error)
//sysnb Setreuid(ruid int, euid int) (err error)
-//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
-//sysnb Setresuid(ruid int, euid int, suid int) (err error)
//sysnb Setrlimit(which int, lim *Rlimit) (err error)
//sysnb Setsid() (pid int, err error)
//sysnb Settimeofday(tp *Timeval) (err error)
diff --git a/src/pkg/syscall/syscall_openbsd.go b/src/pkg/syscall/syscall_openbsd.go
index a46f6ae56e..8d3f825f8f 100644
--- a/src/pkg/syscall/syscall_openbsd.go
+++ b/src/pkg/syscall/syscall_openbsd.go
@@ -182,8 +182,6 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
//sys Setpriority(which int, who int, prio int) (err error)
//sysnb Setregid(rgid int, egid int) (err error)
//sysnb Setreuid(ruid int, euid int) (err error)
-//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
-//sysnb Setresuid(ruid int, euid int, suid int) (err error)
//sysnb Setrlimit(which int, lim *Rlimit) (err error)
//sysnb Setsid() (pid int, err error)
//sysnb Settimeofday(tp *Timeval) (err error)
@@ -274,6 +272,8 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
// semop
// setgroups
// setitimer
+// setresgid
+// setresuid
// setrtable
// setsockopt
// shmat
diff --git a/src/pkg/syscall/syscall_windows.go b/src/pkg/syscall/syscall_windows.go
index 1fe1ae0fab..bda8214c3c 100644
--- a/src/pkg/syscall/syscall_windows.go
+++ b/src/pkg/syscall/syscall_windows.go
@@ -105,12 +105,22 @@ func (e Errno) Timeout() bool {
return e == EAGAIN || e == EWOULDBLOCK || e == ETIMEDOUT
}
+// Implemented in asm_windows.s
+func compileCallback(fn interface{}, cleanstack bool) uintptr
+
+// Converts a Go function to a function pointer conforming
+// to the stdcall calling convention. This is useful when
+// interoperating with Windows code requiring callbacks.
+func NewCallback(fn interface{}) uintptr {
+ return compileCallback(fn, true)
+}
+
// Converts a Go function to a function pointer conforming
-// to the stdcall or cdecl calling convention. This is useful when
+// to the cdecl calling convention. This is useful when
// interoperating with Windows code requiring callbacks.
-// Implemented in ../runtime/syscall_windows.goc
-func NewCallback(fn interface{}) uintptr
-func NewCallbackCDecl(fn interface{}) uintptr
+func NewCallbackCDecl(fn interface{}) uintptr {
+ return compileCallback(fn, false)
+}
// windows api calls
@@ -549,6 +559,7 @@ const socket_error = uintptr(^uint32(0))
//sys GetProtoByName(name string) (p *Protoent, err error) [failretval==nil] = ws2_32.getprotobyname
//sys DnsQuery(name string, qtype uint16, options uint32, extra *byte, qrs **DNSRecord, pr *byte) (status error) = dnsapi.DnsQuery_W
//sys DnsRecordListFree(rl *DNSRecord, freetype uint32) = dnsapi.DnsRecordListFree
+//sys DnsNameCompare(name1 *uint16, name2 *uint16) (same bool) = dnsapi.DnsNameCompare_W
//sys GetAddrInfoW(nodename *uint16, servicename *uint16, hints *AddrinfoW, result **AddrinfoW) (sockerr error) = ws2_32.GetAddrInfoW
//sys FreeAddrInfoW(addrinfo *AddrinfoW) = ws2_32.FreeAddrInfoW
//sys GetIfEntry(pIfRow *MibIfRow) (errcode error) = iphlpapi.GetIfEntry
diff --git a/src/pkg/syscall/time_nacl_386.s b/src/pkg/syscall/time_nacl_386.s
index b5a22d31b5..c0c89dccc0 100644
--- a/src/pkg/syscall/time_nacl_386.s
+++ b/src/pkg/syscall/time_nacl_386.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·startTimer(SB),NOSPLIT,$0
JMP time·startTimer(SB)
diff --git a/src/pkg/syscall/time_nacl_amd64p32.s b/src/pkg/syscall/time_nacl_amd64p32.s
index b5a22d31b5..c0c89dccc0 100644
--- a/src/pkg/syscall/time_nacl_amd64p32.s
+++ b/src/pkg/syscall/time_nacl_amd64p32.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·startTimer(SB),NOSPLIT,$0
JMP time·startTimer(SB)
diff --git a/src/pkg/syscall/time_nacl_arm.s b/src/pkg/syscall/time_nacl_arm.s
index 99baaf59b6..4f4b4d89ae 100644
--- a/src/pkg/syscall/time_nacl_arm.s
+++ b/src/pkg/syscall/time_nacl_arm.s
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "../../cmd/ld/textflag.h"
+#include "textflag.h"
TEXT ·startTimer(SB),NOSPLIT,$0
B time·startTimer(SB)
diff --git a/src/pkg/syscall/zsyscall_dragonfly_386.go b/src/pkg/syscall/zsyscall_dragonfly_386.go
index 94a716fa56..0ec8132326 100644
--- a/src/pkg/syscall/zsyscall_dragonfly_386.go
+++ b/src/pkg/syscall/zsyscall_dragonfly_386.go
@@ -1059,26 +1059,6 @@ func Setreuid(ruid int, euid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Setresgid(rgid int, egid int, sgid int) (err error) {
- _, _, e1 := RawSyscall(SYS_SETRESGID, uintptr(rgid), uintptr(egid), uintptr(sgid))
- if e1 != 0 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func Setresuid(ruid int, euid int, suid int) (err error) {
- _, _, e1 := RawSyscall(SYS_SETRESUID, uintptr(ruid), uintptr(euid), uintptr(suid))
- if e1 != 0 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Setrlimit(which int, lim *Rlimit) (err error) {
_, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
if e1 != 0 {
diff --git a/src/pkg/syscall/zsyscall_dragonfly_amd64.go b/src/pkg/syscall/zsyscall_dragonfly_amd64.go
index 89c22ed3cc..8c7cce54e7 100644
--- a/src/pkg/syscall/zsyscall_dragonfly_amd64.go
+++ b/src/pkg/syscall/zsyscall_dragonfly_amd64.go
@@ -1059,26 +1059,6 @@ func Setreuid(ruid int, euid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Setresgid(rgid int, egid int, sgid int) (err error) {
- _, _, e1 := RawSyscall(SYS_SETRESGID, uintptr(rgid), uintptr(egid), uintptr(sgid))
- if e1 != 0 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func Setresuid(ruid int, euid int, suid int) (err error) {
- _, _, e1 := RawSyscall(SYS_SETRESUID, uintptr(ruid), uintptr(euid), uintptr(suid))
- if e1 != 0 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Setrlimit(which int, lim *Rlimit) (err error) {
_, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
if e1 != 0 {
diff --git a/src/pkg/syscall/zsyscall_freebsd_386.go b/src/pkg/syscall/zsyscall_freebsd_386.go
index 61e4888ae8..5befe83c6b 100644
--- a/src/pkg/syscall/zsyscall_freebsd_386.go
+++ b/src/pkg/syscall/zsyscall_freebsd_386.go
@@ -1059,26 +1059,6 @@ func Setreuid(ruid int, euid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Setresgid(rgid int, egid int, sgid int) (err error) {
- _, _, e1 := RawSyscall(SYS_SETRESGID, uintptr(rgid), uintptr(egid), uintptr(sgid))
- if e1 != 0 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func Setresuid(ruid int, euid int, suid int) (err error) {
- _, _, e1 := RawSyscall(SYS_SETRESUID, uintptr(ruid), uintptr(euid), uintptr(suid))
- if e1 != 0 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Setrlimit(which int, lim *Rlimit) (err error) {
_, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
if e1 != 0 {
diff --git a/src/pkg/syscall/zsyscall_freebsd_amd64.go b/src/pkg/syscall/zsyscall_freebsd_amd64.go
index 5e3afe19ac..ab2eb80c62 100644
--- a/src/pkg/syscall/zsyscall_freebsd_amd64.go
+++ b/src/pkg/syscall/zsyscall_freebsd_amd64.go
@@ -1059,26 +1059,6 @@ func Setreuid(ruid int, euid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Setresgid(rgid int, egid int, sgid int) (err error) {
- _, _, e1 := RawSyscall(SYS_SETRESGID, uintptr(rgid), uintptr(egid), uintptr(sgid))
- if e1 != 0 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func Setresuid(ruid int, euid int, suid int) (err error) {
- _, _, e1 := RawSyscall(SYS_SETRESUID, uintptr(ruid), uintptr(euid), uintptr(suid))
- if e1 != 0 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Setrlimit(which int, lim *Rlimit) (err error) {
_, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
if e1 != 0 {
diff --git a/src/pkg/syscall/zsyscall_freebsd_arm.go b/src/pkg/syscall/zsyscall_freebsd_arm.go
index da1f2d587f..c1f0f907cd 100644
--- a/src/pkg/syscall/zsyscall_freebsd_arm.go
+++ b/src/pkg/syscall/zsyscall_freebsd_arm.go
@@ -1059,26 +1059,6 @@ func Setreuid(ruid int, euid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Setresgid(rgid int, egid int, sgid int) (err error) {
- _, _, e1 := RawSyscall(SYS_SETRESGID, uintptr(rgid), uintptr(egid), uintptr(sgid))
- if e1 != 0 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func Setresuid(ruid int, euid int, suid int) (err error) {
- _, _, e1 := RawSyscall(SYS_SETRESUID, uintptr(ruid), uintptr(euid), uintptr(suid))
- if e1 != 0 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Setrlimit(which int, lim *Rlimit) (err error) {
_, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
if e1 != 0 {
diff --git a/src/pkg/syscall/zsyscall_openbsd_386.go b/src/pkg/syscall/zsyscall_openbsd_386.go
index 068a1fba7f..785e7c3b8e 100644
--- a/src/pkg/syscall/zsyscall_openbsd_386.go
+++ b/src/pkg/syscall/zsyscall_openbsd_386.go
@@ -1049,26 +1049,6 @@ func Setreuid(ruid int, euid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Setresgid(rgid int, egid int, sgid int) (err error) {
- _, _, e1 := RawSyscall(SYS_SETRESGID, uintptr(rgid), uintptr(egid), uintptr(sgid))
- if e1 != 0 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func Setresuid(ruid int, euid int, suid int) (err error) {
- _, _, e1 := RawSyscall(SYS_SETRESUID, uintptr(ruid), uintptr(euid), uintptr(suid))
- if e1 != 0 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Setrlimit(which int, lim *Rlimit) (err error) {
_, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
if e1 != 0 {
diff --git a/src/pkg/syscall/zsyscall_openbsd_amd64.go b/src/pkg/syscall/zsyscall_openbsd_amd64.go
index 13dc093375..7a8d9b6f1d 100644
--- a/src/pkg/syscall/zsyscall_openbsd_amd64.go
+++ b/src/pkg/syscall/zsyscall_openbsd_amd64.go
@@ -1049,26 +1049,6 @@ func Setreuid(ruid int, euid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Setresgid(rgid int, egid int, sgid int) (err error) {
- _, _, e1 := RawSyscall(SYS_SETRESGID, uintptr(rgid), uintptr(egid), uintptr(sgid))
- if e1 != 0 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func Setresuid(ruid int, euid int, suid int) (err error) {
- _, _, e1 := RawSyscall(SYS_SETRESUID, uintptr(ruid), uintptr(euid), uintptr(suid))
- if e1 != 0 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Setrlimit(which int, lim *Rlimit) (err error) {
_, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
if e1 != 0 {
diff --git a/src/pkg/syscall/zsyscall_windows_386.go b/src/pkg/syscall/zsyscall_windows.go
index d55211ee75..1f44750b7f 100644
--- a/src/pkg/syscall/zsyscall_windows_386.go
+++ b/src/pkg/syscall/zsyscall_windows.go
@@ -1,4 +1,4 @@
-// go build mksyscall_windows.go && ./mksyscall_windows syscall_windows.go security_windows.go syscall_windows_386.go
+// go build mksyscall_windows.go && ./mksyscall_windows syscall_windows.go security_windows.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
package syscall
@@ -139,6 +139,7 @@ var (
procgetprotobyname = modws2_32.NewProc("getprotobyname")
procDnsQuery_W = moddnsapi.NewProc("DnsQuery_W")
procDnsRecordListFree = moddnsapi.NewProc("DnsRecordListFree")
+ procDnsNameCompare_W = moddnsapi.NewProc("DnsNameCompare_W")
procGetAddrInfoW = modws2_32.NewProc("GetAddrInfoW")
procFreeAddrInfoW = modws2_32.NewProc("FreeAddrInfoW")
procGetIfEntry = modiphlpapi.NewProc("GetIfEntry")
@@ -1634,6 +1635,12 @@ func DnsRecordListFree(rl *DNSRecord, freetype uint32) {
return
}
+func DnsNameCompare(name1 *uint16, name2 *uint16) (same bool) {
+ r0, _, _ := Syscall(procDnsNameCompare_W.Addr(), 2, uintptr(unsafe.Pointer(name1)), uintptr(unsafe.Pointer(name2)), 0)
+ same = r0 != 0
+ return
+}
+
func GetAddrInfoW(nodename *uint16, servicename *uint16, hints *AddrinfoW, result **AddrinfoW) (sockerr error) {
r0, _, _ := Syscall6(procGetAddrInfoW.Addr(), 4, uintptr(unsafe.Pointer(nodename)), uintptr(unsafe.Pointer(servicename)), uintptr(unsafe.Pointer(hints)), uintptr(unsafe.Pointer(result)), 0, 0)
if r0 != 0 {
diff --git a/src/pkg/syscall/zsyscall_windows_amd64.go b/src/pkg/syscall/zsyscall_windows_amd64.go
deleted file mode 100644
index 47affab73d..0000000000
--- a/src/pkg/syscall/zsyscall_windows_amd64.go
+++ /dev/null
@@ -1,1839 +0,0 @@
-// go build mksyscall_windows.go && ./mksyscall_windows syscall_windows.go security_windows.go syscall_windows_amd64.go
-// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
-
-package syscall
-
-import "unsafe"
-
-var (
- modkernel32 = NewLazyDLL("kernel32.dll")
- modadvapi32 = NewLazyDLL("advapi32.dll")
- modshell32 = NewLazyDLL("shell32.dll")
- modmswsock = NewLazyDLL("mswsock.dll")
- modcrypt32 = NewLazyDLL("crypt32.dll")
- modws2_32 = NewLazyDLL("ws2_32.dll")
- moddnsapi = NewLazyDLL("dnsapi.dll")
- modiphlpapi = NewLazyDLL("iphlpapi.dll")
- modsecur32 = NewLazyDLL("secur32.dll")
- modnetapi32 = NewLazyDLL("netapi32.dll")
- moduserenv = NewLazyDLL("userenv.dll")
-
- procGetLastError = modkernel32.NewProc("GetLastError")
- procLoadLibraryW = modkernel32.NewProc("LoadLibraryW")
- procFreeLibrary = modkernel32.NewProc("FreeLibrary")
- procGetProcAddress = modkernel32.NewProc("GetProcAddress")
- procGetVersion = modkernel32.NewProc("GetVersion")
- procFormatMessageW = modkernel32.NewProc("FormatMessageW")
- procExitProcess = modkernel32.NewProc("ExitProcess")
- procCreateFileW = modkernel32.NewProc("CreateFileW")
- procReadFile = modkernel32.NewProc("ReadFile")
- procWriteFile = modkernel32.NewProc("WriteFile")
- procSetFilePointer = modkernel32.NewProc("SetFilePointer")
- procCloseHandle = modkernel32.NewProc("CloseHandle")
- procGetStdHandle = modkernel32.NewProc("GetStdHandle")
- procFindFirstFileW = modkernel32.NewProc("FindFirstFileW")
- procFindNextFileW = modkernel32.NewProc("FindNextFileW")
- procFindClose = modkernel32.NewProc("FindClose")
- procGetFileInformationByHandle = modkernel32.NewProc("GetFileInformationByHandle")
- procGetCurrentDirectoryW = modkernel32.NewProc("GetCurrentDirectoryW")
- procSetCurrentDirectoryW = modkernel32.NewProc("SetCurrentDirectoryW")
- procCreateDirectoryW = modkernel32.NewProc("CreateDirectoryW")
- procRemoveDirectoryW = modkernel32.NewProc("RemoveDirectoryW")
- procDeleteFileW = modkernel32.NewProc("DeleteFileW")
- procMoveFileW = modkernel32.NewProc("MoveFileW")
- procGetComputerNameW = modkernel32.NewProc("GetComputerNameW")
- procSetEndOfFile = modkernel32.NewProc("SetEndOfFile")
- procGetSystemTimeAsFileTime = modkernel32.NewProc("GetSystemTimeAsFileTime")
- procGetTimeZoneInformation = modkernel32.NewProc("GetTimeZoneInformation")
- procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
- procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
- procPostQueuedCompletionStatus = modkernel32.NewProc("PostQueuedCompletionStatus")
- procCancelIo = modkernel32.NewProc("CancelIo")
- procCancelIoEx = modkernel32.NewProc("CancelIoEx")
- procCreateProcessW = modkernel32.NewProc("CreateProcessW")
- procOpenProcess = modkernel32.NewProc("OpenProcess")
- procTerminateProcess = modkernel32.NewProc("TerminateProcess")
- procGetExitCodeProcess = modkernel32.NewProc("GetExitCodeProcess")
- procGetStartupInfoW = modkernel32.NewProc("GetStartupInfoW")
- procGetCurrentProcess = modkernel32.NewProc("GetCurrentProcess")
- procGetProcessTimes = modkernel32.NewProc("GetProcessTimes")
- procDuplicateHandle = modkernel32.NewProc("DuplicateHandle")
- procWaitForSingleObject = modkernel32.NewProc("WaitForSingleObject")
- procGetTempPathW = modkernel32.NewProc("GetTempPathW")
- procCreatePipe = modkernel32.NewProc("CreatePipe")
- procGetFileType = modkernel32.NewProc("GetFileType")
- procCryptAcquireContextW = modadvapi32.NewProc("CryptAcquireContextW")
- procCryptReleaseContext = modadvapi32.NewProc("CryptReleaseContext")
- procCryptGenRandom = modadvapi32.NewProc("CryptGenRandom")
- procGetEnvironmentStringsW = modkernel32.NewProc("GetEnvironmentStringsW")
- procFreeEnvironmentStringsW = modkernel32.NewProc("FreeEnvironmentStringsW")
- procGetEnvironmentVariableW = modkernel32.NewProc("GetEnvironmentVariableW")
- procSetEnvironmentVariableW = modkernel32.NewProc("SetEnvironmentVariableW")
- procSetFileTime = modkernel32.NewProc("SetFileTime")
- procGetFileAttributesW = modkernel32.NewProc("GetFileAttributesW")
- procSetFileAttributesW = modkernel32.NewProc("SetFileAttributesW")
- procGetFileAttributesExW = modkernel32.NewProc("GetFileAttributesExW")
- procGetCommandLineW = modkernel32.NewProc("GetCommandLineW")
- procCommandLineToArgvW = modshell32.NewProc("CommandLineToArgvW")
- procLocalFree = modkernel32.NewProc("LocalFree")
- procSetHandleInformation = modkernel32.NewProc("SetHandleInformation")
- procFlushFileBuffers = modkernel32.NewProc("FlushFileBuffers")
- procGetFullPathNameW = modkernel32.NewProc("GetFullPathNameW")
- procGetLongPathNameW = modkernel32.NewProc("GetLongPathNameW")
- procGetShortPathNameW = modkernel32.NewProc("GetShortPathNameW")
- procCreateFileMappingW = modkernel32.NewProc("CreateFileMappingW")
- procMapViewOfFile = modkernel32.NewProc("MapViewOfFile")
- procUnmapViewOfFile = modkernel32.NewProc("UnmapViewOfFile")
- procFlushViewOfFile = modkernel32.NewProc("FlushViewOfFile")
- procVirtualLock = modkernel32.NewProc("VirtualLock")
- procVirtualUnlock = modkernel32.NewProc("VirtualUnlock")
- procTransmitFile = modmswsock.NewProc("TransmitFile")
- procReadDirectoryChangesW = modkernel32.NewProc("ReadDirectoryChangesW")
- procCertOpenSystemStoreW = modcrypt32.NewProc("CertOpenSystemStoreW")
- procCertOpenStore = modcrypt32.NewProc("CertOpenStore")
- procCertEnumCertificatesInStore = modcrypt32.NewProc("CertEnumCertificatesInStore")
- procCertAddCertificateContextToStore = modcrypt32.NewProc("CertAddCertificateContextToStore")
- procCertCloseStore = modcrypt32.NewProc("CertCloseStore")
- procCertGetCertificateChain = modcrypt32.NewProc("CertGetCertificateChain")
- procCertFreeCertificateChain = modcrypt32.NewProc("CertFreeCertificateChain")
- procCertCreateCertificateContext = modcrypt32.NewProc("CertCreateCertificateContext")
- procCertFreeCertificateContext = modcrypt32.NewProc("CertFreeCertificateContext")
- procCertVerifyCertificateChainPolicy = modcrypt32.NewProc("CertVerifyCertificateChainPolicy")
- procRegOpenKeyExW = modadvapi32.NewProc("RegOpenKeyExW")
- procRegCloseKey = modadvapi32.NewProc("RegCloseKey")
- procRegQueryInfoKeyW = modadvapi32.NewProc("RegQueryInfoKeyW")
- procRegEnumKeyExW = modadvapi32.NewProc("RegEnumKeyExW")
- procRegQueryValueExW = modadvapi32.NewProc("RegQueryValueExW")
- procGetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId")
- procGetConsoleMode = modkernel32.NewProc("GetConsoleMode")
- procWriteConsoleW = modkernel32.NewProc("WriteConsoleW")
- procReadConsoleW = modkernel32.NewProc("ReadConsoleW")
- procCreateToolhelp32Snapshot = modkernel32.NewProc("CreateToolhelp32Snapshot")
- procProcess32FirstW = modkernel32.NewProc("Process32FirstW")
- procProcess32NextW = modkernel32.NewProc("Process32NextW")
- procDeviceIoControl = modkernel32.NewProc("DeviceIoControl")
- procCreateSymbolicLinkW = modkernel32.NewProc("CreateSymbolicLinkW")
- procCreateHardLinkW = modkernel32.NewProc("CreateHardLinkW")
- procWSAStartup = modws2_32.NewProc("WSAStartup")
- procWSACleanup = modws2_32.NewProc("WSACleanup")
- procWSAIoctl = modws2_32.NewProc("WSAIoctl")
- procsocket = modws2_32.NewProc("socket")
- procsetsockopt = modws2_32.NewProc("setsockopt")
- procgetsockopt = modws2_32.NewProc("getsockopt")
- procbind = modws2_32.NewProc("bind")
- procconnect = modws2_32.NewProc("connect")
- procgetsockname = modws2_32.NewProc("getsockname")
- procgetpeername = modws2_32.NewProc("getpeername")
- proclisten = modws2_32.NewProc("listen")
- procshutdown = modws2_32.NewProc("shutdown")
- procclosesocket = modws2_32.NewProc("closesocket")
- procAcceptEx = modmswsock.NewProc("AcceptEx")
- procGetAcceptExSockaddrs = modmswsock.NewProc("GetAcceptExSockaddrs")
- procWSARecv = modws2_32.NewProc("WSARecv")
- procWSASend = modws2_32.NewProc("WSASend")
- procWSARecvFrom = modws2_32.NewProc("WSARecvFrom")
- procWSASendTo = modws2_32.NewProc("WSASendTo")
- procgethostbyname = modws2_32.NewProc("gethostbyname")
- procgetservbyname = modws2_32.NewProc("getservbyname")
- procntohs = modws2_32.NewProc("ntohs")
- procgetprotobyname = modws2_32.NewProc("getprotobyname")
- procDnsQuery_W = moddnsapi.NewProc("DnsQuery_W")
- procDnsRecordListFree = moddnsapi.NewProc("DnsRecordListFree")
- procGetAddrInfoW = modws2_32.NewProc("GetAddrInfoW")
- procFreeAddrInfoW = modws2_32.NewProc("FreeAddrInfoW")
- procGetIfEntry = modiphlpapi.NewProc("GetIfEntry")
- procGetAdaptersInfo = modiphlpapi.NewProc("GetAdaptersInfo")
- procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
- procWSAEnumProtocolsW = modws2_32.NewProc("WSAEnumProtocolsW")
- procTranslateNameW = modsecur32.NewProc("TranslateNameW")
- procGetUserNameExW = modsecur32.NewProc("GetUserNameExW")
- procNetUserGetInfo = modnetapi32.NewProc("NetUserGetInfo")
- procNetGetJoinInformation = modnetapi32.NewProc("NetGetJoinInformation")
- procNetApiBufferFree = modnetapi32.NewProc("NetApiBufferFree")
- procLookupAccountSidW = modadvapi32.NewProc("LookupAccountSidW")
- procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW")
- procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW")
- procConvertStringSidToSidW = modadvapi32.NewProc("ConvertStringSidToSidW")
- procGetLengthSid = modadvapi32.NewProc("GetLengthSid")
- procCopySid = modadvapi32.NewProc("CopySid")
- procOpenProcessToken = modadvapi32.NewProc("OpenProcessToken")
- procGetTokenInformation = modadvapi32.NewProc("GetTokenInformation")
- procGetUserProfileDirectoryW = moduserenv.NewProc("GetUserProfileDirectoryW")
-)
-
-func GetLastError() (lasterr error) {
- r0, _, _ := Syscall(procGetLastError.Addr(), 0, 0, 0, 0)
- if r0 != 0 {
- lasterr = Errno(r0)
- }
- return
-}
-
-func LoadLibrary(libname string) (handle Handle, err error) {
- var _p0 *uint16
- _p0, err = UTF16PtrFromString(libname)
- if err != nil {
- return
- }
- r0, _, e1 := Syscall(procLoadLibraryW.Addr(), 1, uintptr(unsafe.Pointer(_p0)), 0, 0)
- handle = Handle(r0)
- if handle == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func FreeLibrary(handle Handle) (err error) {
- r1, _, e1 := Syscall(procFreeLibrary.Addr(), 1, uintptr(handle), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetProcAddress(module Handle, procname string) (proc uintptr, err error) {
- var _p0 *byte
- _p0, err = BytePtrFromString(procname)
- if err != nil {
- return
- }
- r0, _, e1 := Syscall(procGetProcAddress.Addr(), 2, uintptr(module), uintptr(unsafe.Pointer(_p0)), 0)
- proc = uintptr(r0)
- if proc == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetVersion() (ver uint32, err error) {
- r0, _, e1 := Syscall(procGetVersion.Addr(), 0, 0, 0, 0)
- ver = uint32(r0)
- if ver == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func FormatMessage(flags uint32, msgsrc uint32, msgid uint32, langid uint32, buf []uint16, args *byte) (n uint32, err error) {
- var _p0 *uint16
- if len(buf) > 0 {
- _p0 = &buf[0]
- }
- r0, _, e1 := Syscall9(procFormatMessageW.Addr(), 7, uintptr(flags), uintptr(msgsrc), uintptr(msgid), uintptr(langid), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(args)), 0, 0)
- n = uint32(r0)
- if n == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func ExitProcess(exitcode uint32) {
- Syscall(procExitProcess.Addr(), 1, uintptr(exitcode), 0, 0)
- return
-}
-
-func CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile int32) (handle Handle, err error) {
- r0, _, e1 := Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0)
- handle = Handle(r0)
- if handle == InvalidHandle {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func ReadFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (err error) {
- var _p0 *byte
- if len(buf) > 0 {
- _p0 = &buf[0]
- }
- r1, _, e1 := Syscall6(procReadFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(done)), uintptr(unsafe.Pointer(overlapped)), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func WriteFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (err error) {
- var _p0 *byte
- if len(buf) > 0 {
- _p0 = &buf[0]
- }
- r1, _, e1 := Syscall6(procWriteFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(done)), uintptr(unsafe.Pointer(overlapped)), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func SetFilePointer(handle Handle, lowoffset int32, highoffsetptr *int32, whence uint32) (newlowoffset uint32, err error) {
- r0, _, e1 := Syscall6(procSetFilePointer.Addr(), 4, uintptr(handle), uintptr(lowoffset), uintptr(unsafe.Pointer(highoffsetptr)), uintptr(whence), 0, 0)
- newlowoffset = uint32(r0)
- if newlowoffset == 0xffffffff {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func CloseHandle(handle Handle) (err error) {
- r1, _, e1 := Syscall(procCloseHandle.Addr(), 1, uintptr(handle), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetStdHandle(stdhandle int) (handle Handle, err error) {
- r0, _, e1 := Syscall(procGetStdHandle.Addr(), 1, uintptr(stdhandle), 0, 0)
- handle = Handle(r0)
- if handle == InvalidHandle {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func findFirstFile1(name *uint16, data *win32finddata1) (handle Handle, err error) {
- r0, _, e1 := Syscall(procFindFirstFileW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(data)), 0)
- handle = Handle(r0)
- if handle == InvalidHandle {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func findNextFile1(handle Handle, data *win32finddata1) (err error) {
- r1, _, e1 := Syscall(procFindNextFileW.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func FindClose(handle Handle) (err error) {
- r1, _, e1 := Syscall(procFindClose.Addr(), 1, uintptr(handle), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetFileInformationByHandle(handle Handle, data *ByHandleFileInformation) (err error) {
- r1, _, e1 := Syscall(procGetFileInformationByHandle.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetCurrentDirectory(buflen uint32, buf *uint16) (n uint32, err error) {
- r0, _, e1 := Syscall(procGetCurrentDirectoryW.Addr(), 2, uintptr(buflen), uintptr(unsafe.Pointer(buf)), 0)
- n = uint32(r0)
- if n == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func SetCurrentDirectory(path *uint16) (err error) {
- r1, _, e1 := Syscall(procSetCurrentDirectoryW.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func CreateDirectory(path *uint16, sa *SecurityAttributes) (err error) {
- r1, _, e1 := Syscall(procCreateDirectoryW.Addr(), 2, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(sa)), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func RemoveDirectory(path *uint16) (err error) {
- r1, _, e1 := Syscall(procRemoveDirectoryW.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func DeleteFile(path *uint16) (err error) {
- r1, _, e1 := Syscall(procDeleteFileW.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func MoveFile(from *uint16, to *uint16) (err error) {
- r1, _, e1 := Syscall(procMoveFileW.Addr(), 2, uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to)), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetComputerName(buf *uint16, n *uint32) (err error) {
- r1, _, e1 := Syscall(procGetComputerNameW.Addr(), 2, uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(n)), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func SetEndOfFile(handle Handle) (err error) {
- r1, _, e1 := Syscall(procSetEndOfFile.Addr(), 1, uintptr(handle), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetSystemTimeAsFileTime(time *Filetime) {
- Syscall(procGetSystemTimeAsFileTime.Addr(), 1, uintptr(unsafe.Pointer(time)), 0, 0)
- return
-}
-
-func GetTimeZoneInformation(tzi *Timezoneinformation) (rc uint32, err error) {
- r0, _, e1 := Syscall(procGetTimeZoneInformation.Addr(), 1, uintptr(unsafe.Pointer(tzi)), 0, 0)
- rc = uint32(r0)
- if rc == 0xffffffff {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func CreateIoCompletionPort(filehandle Handle, cphandle Handle, key uint32, threadcnt uint32) (handle Handle, err error) {
- r0, _, e1 := Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(filehandle), uintptr(cphandle), uintptr(key), uintptr(threadcnt), 0, 0)
- handle = Handle(r0)
- if handle == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (err error) {
- r1, _, e1 := Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(cphandle), uintptr(unsafe.Pointer(qty)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(overlapped)), uintptr(timeout), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func PostQueuedCompletionStatus(cphandle Handle, qty uint32, key uint32, overlapped *Overlapped) (err error) {
- r1, _, e1 := Syscall6(procPostQueuedCompletionStatus.Addr(), 4, uintptr(cphandle), uintptr(qty), uintptr(key), uintptr(unsafe.Pointer(overlapped)), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func CancelIo(s Handle) (err error) {
- r1, _, e1 := Syscall(procCancelIo.Addr(), 1, uintptr(s), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func CancelIoEx(s Handle, o *Overlapped) (err error) {
- r1, _, e1 := Syscall(procCancelIoEx.Addr(), 2, uintptr(s), uintptr(unsafe.Pointer(o)), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityAttributes, threadSecurity *SecurityAttributes, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (err error) {
- var _p0 uint32
- if inheritHandles {
- _p0 = 1
- } else {
- _p0 = 0
- }
- r1, _, e1 := Syscall12(procCreateProcessW.Addr(), 10, uintptr(unsafe.Pointer(appName)), uintptr(unsafe.Pointer(commandLine)), uintptr(unsafe.Pointer(procSecurity)), uintptr(unsafe.Pointer(threadSecurity)), uintptr(_p0), uintptr(creationFlags), uintptr(unsafe.Pointer(env)), uintptr(unsafe.Pointer(currentDir)), uintptr(unsafe.Pointer(startupInfo)), uintptr(unsafe.Pointer(outProcInfo)), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle Handle, err error) {
- var _p0 uint32
- if inheritHandle {
- _p0 = 1
- } else {
- _p0 = 0
- }
- r0, _, e1 := Syscall(procOpenProcess.Addr(), 3, uintptr(da), uintptr(_p0), uintptr(pid))
- handle = Handle(r0)
- if handle == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func TerminateProcess(handle Handle, exitcode uint32) (err error) {
- r1, _, e1 := Syscall(procTerminateProcess.Addr(), 2, uintptr(handle), uintptr(exitcode), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetExitCodeProcess(handle Handle, exitcode *uint32) (err error) {
- r1, _, e1 := Syscall(procGetExitCodeProcess.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(exitcode)), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetStartupInfo(startupInfo *StartupInfo) (err error) {
- r1, _, e1 := Syscall(procGetStartupInfoW.Addr(), 1, uintptr(unsafe.Pointer(startupInfo)), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetCurrentProcess() (pseudoHandle Handle, err error) {
- r0, _, e1 := Syscall(procGetCurrentProcess.Addr(), 0, 0, 0, 0)
- pseudoHandle = Handle(r0)
- if pseudoHandle == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetProcessTimes(handle Handle, creationTime *Filetime, exitTime *Filetime, kernelTime *Filetime, userTime *Filetime) (err error) {
- r1, _, e1 := Syscall6(procGetProcessTimes.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(creationTime)), uintptr(unsafe.Pointer(exitTime)), uintptr(unsafe.Pointer(kernelTime)), uintptr(unsafe.Pointer(userTime)), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetProcessHandle Handle, lpTargetHandle *Handle, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (err error) {
- var _p0 uint32
- if bInheritHandle {
- _p0 = 1
- } else {
- _p0 = 0
- }
- r1, _, e1 := Syscall9(procDuplicateHandle.Addr(), 7, uintptr(hSourceProcessHandle), uintptr(hSourceHandle), uintptr(hTargetProcessHandle), uintptr(unsafe.Pointer(lpTargetHandle)), uintptr(dwDesiredAccess), uintptr(_p0), uintptr(dwOptions), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func WaitForSingleObject(handle Handle, waitMilliseconds uint32) (event uint32, err error) {
- r0, _, e1 := Syscall(procWaitForSingleObject.Addr(), 2, uintptr(handle), uintptr(waitMilliseconds), 0)
- event = uint32(r0)
- if event == 0xffffffff {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetTempPath(buflen uint32, buf *uint16) (n uint32, err error) {
- r0, _, e1 := Syscall(procGetTempPathW.Addr(), 2, uintptr(buflen), uintptr(unsafe.Pointer(buf)), 0)
- n = uint32(r0)
- if n == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func CreatePipe(readhandle *Handle, writehandle *Handle, sa *SecurityAttributes, size uint32) (err error) {
- r1, _, e1 := Syscall6(procCreatePipe.Addr(), 4, uintptr(unsafe.Pointer(readhandle)), uintptr(unsafe.Pointer(writehandle)), uintptr(unsafe.Pointer(sa)), uintptr(size), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetFileType(filehandle Handle) (n uint32, err error) {
- r0, _, e1 := Syscall(procGetFileType.Addr(), 1, uintptr(filehandle), 0, 0)
- n = uint32(r0)
- if n == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func CryptAcquireContext(provhandle *Handle, container *uint16, provider *uint16, provtype uint32, flags uint32) (err error) {
- r1, _, e1 := Syscall6(procCryptAcquireContextW.Addr(), 5, uintptr(unsafe.Pointer(provhandle)), uintptr(unsafe.Pointer(container)), uintptr(unsafe.Pointer(provider)), uintptr(provtype), uintptr(flags), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func CryptReleaseContext(provhandle Handle, flags uint32) (err error) {
- r1, _, e1 := Syscall(procCryptReleaseContext.Addr(), 2, uintptr(provhandle), uintptr(flags), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func CryptGenRandom(provhandle Handle, buflen uint32, buf *byte) (err error) {
- r1, _, e1 := Syscall(procCryptGenRandom.Addr(), 3, uintptr(provhandle), uintptr(buflen), uintptr(unsafe.Pointer(buf)))
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetEnvironmentStrings() (envs *uint16, err error) {
- r0, _, e1 := Syscall(procGetEnvironmentStringsW.Addr(), 0, 0, 0, 0)
- envs = (*uint16)(unsafe.Pointer(r0))
- if envs == nil {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func FreeEnvironmentStrings(envs *uint16) (err error) {
- r1, _, e1 := Syscall(procFreeEnvironmentStringsW.Addr(), 1, uintptr(unsafe.Pointer(envs)), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetEnvironmentVariable(name *uint16, buffer *uint16, size uint32) (n uint32, err error) {
- r0, _, e1 := Syscall(procGetEnvironmentVariableW.Addr(), 3, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(size))
- n = uint32(r0)
- if n == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func SetEnvironmentVariable(name *uint16, value *uint16) (err error) {
- r1, _, e1 := Syscall(procSetEnvironmentVariableW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(value)), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (err error) {
- r1, _, e1 := Syscall6(procSetFileTime.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(ctime)), uintptr(unsafe.Pointer(atime)), uintptr(unsafe.Pointer(wtime)), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetFileAttributes(name *uint16) (attrs uint32, err error) {
- r0, _, e1 := Syscall(procGetFileAttributesW.Addr(), 1, uintptr(unsafe.Pointer(name)), 0, 0)
- attrs = uint32(r0)
- if attrs == INVALID_FILE_ATTRIBUTES {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func SetFileAttributes(name *uint16, attrs uint32) (err error) {
- r1, _, e1 := Syscall(procSetFileAttributesW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(attrs), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetFileAttributesEx(name *uint16, level uint32, info *byte) (err error) {
- r1, _, e1 := Syscall(procGetFileAttributesExW.Addr(), 3, uintptr(unsafe.Pointer(name)), uintptr(level), uintptr(unsafe.Pointer(info)))
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetCommandLine() (cmd *uint16) {
- r0, _, _ := Syscall(procGetCommandLineW.Addr(), 0, 0, 0, 0)
- cmd = (*uint16)(unsafe.Pointer(r0))
- return
-}
-
-func CommandLineToArgv(cmd *uint16, argc *int32) (argv *[8192]*[8192]uint16, err error) {
- r0, _, e1 := Syscall(procCommandLineToArgvW.Addr(), 2, uintptr(unsafe.Pointer(cmd)), uintptr(unsafe.Pointer(argc)), 0)
- argv = (*[8192]*[8192]uint16)(unsafe.Pointer(r0))
- if argv == nil {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func LocalFree(hmem Handle) (handle Handle, err error) {
- r0, _, e1 := Syscall(procLocalFree.Addr(), 1, uintptr(hmem), 0, 0)
- handle = Handle(r0)
- if handle != 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func SetHandleInformation(handle Handle, mask uint32, flags uint32) (err error) {
- r1, _, e1 := Syscall(procSetHandleInformation.Addr(), 3, uintptr(handle), uintptr(mask), uintptr(flags))
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func FlushFileBuffers(handle Handle) (err error) {
- r1, _, e1 := Syscall(procFlushFileBuffers.Addr(), 1, uintptr(handle), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetFullPathName(path *uint16, buflen uint32, buf *uint16, fname **uint16) (n uint32, err error) {
- r0, _, e1 := Syscall6(procGetFullPathNameW.Addr(), 4, uintptr(unsafe.Pointer(path)), uintptr(buflen), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(fname)), 0, 0)
- n = uint32(r0)
- if n == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetLongPathName(path *uint16, buf *uint16, buflen uint32) (n uint32, err error) {
- r0, _, e1 := Syscall(procGetLongPathNameW.Addr(), 3, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(buf)), uintptr(buflen))
- n = uint32(r0)
- if n == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetShortPathName(longpath *uint16, shortpath *uint16, buflen uint32) (n uint32, err error) {
- r0, _, e1 := Syscall(procGetShortPathNameW.Addr(), 3, uintptr(unsafe.Pointer(longpath)), uintptr(unsafe.Pointer(shortpath)), uintptr(buflen))
- n = uint32(r0)
- if n == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func CreateFileMapping(fhandle Handle, sa *SecurityAttributes, prot uint32, maxSizeHigh uint32, maxSizeLow uint32, name *uint16) (handle Handle, err error) {
- r0, _, e1 := Syscall6(procCreateFileMappingW.Addr(), 6, uintptr(fhandle), uintptr(unsafe.Pointer(sa)), uintptr(prot), uintptr(maxSizeHigh), uintptr(maxSizeLow), uintptr(unsafe.Pointer(name)))
- handle = Handle(r0)
- if handle == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func MapViewOfFile(handle Handle, access uint32, offsetHigh uint32, offsetLow uint32, length uintptr) (addr uintptr, err error) {
- r0, _, e1 := Syscall6(procMapViewOfFile.Addr(), 5, uintptr(handle), uintptr(access), uintptr(offsetHigh), uintptr(offsetLow), uintptr(length), 0)
- addr = uintptr(r0)
- if addr == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func UnmapViewOfFile(addr uintptr) (err error) {
- r1, _, e1 := Syscall(procUnmapViewOfFile.Addr(), 1, uintptr(addr), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func FlushViewOfFile(addr uintptr, length uintptr) (err error) {
- r1, _, e1 := Syscall(procFlushViewOfFile.Addr(), 2, uintptr(addr), uintptr(length), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func VirtualLock(addr uintptr, length uintptr) (err error) {
- r1, _, e1 := Syscall(procVirtualLock.Addr(), 2, uintptr(addr), uintptr(length), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func VirtualUnlock(addr uintptr, length uintptr) (err error) {
- r1, _, e1 := Syscall(procVirtualUnlock.Addr(), 2, uintptr(addr), uintptr(length), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func TransmitFile(s Handle, handle Handle, bytesToWrite uint32, bytsPerSend uint32, overlapped *Overlapped, transmitFileBuf *TransmitFileBuffers, flags uint32) (err error) {
- r1, _, e1 := Syscall9(procTransmitFile.Addr(), 7, uintptr(s), uintptr(handle), uintptr(bytesToWrite), uintptr(bytsPerSend), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(transmitFileBuf)), uintptr(flags), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func ReadDirectoryChanges(handle Handle, buf *byte, buflen uint32, watchSubTree bool, mask uint32, retlen *uint32, overlapped *Overlapped, completionRoutine uintptr) (err error) {
- var _p0 uint32
- if watchSubTree {
- _p0 = 1
- } else {
- _p0 = 0
- }
- r1, _, e1 := Syscall9(procReadDirectoryChangesW.Addr(), 8, uintptr(handle), uintptr(unsafe.Pointer(buf)), uintptr(buflen), uintptr(_p0), uintptr(mask), uintptr(unsafe.Pointer(retlen)), uintptr(unsafe.Pointer(overlapped)), uintptr(completionRoutine), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func CertOpenSystemStore(hprov Handle, name *uint16) (store Handle, err error) {
- r0, _, e1 := Syscall(procCertOpenSystemStoreW.Addr(), 2, uintptr(hprov), uintptr(unsafe.Pointer(name)), 0)
- store = Handle(r0)
- if store == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func CertOpenStore(storeProvider uintptr, msgAndCertEncodingType uint32, cryptProv uintptr, flags uint32, para uintptr) (handle Handle, err error) {
- r0, _, e1 := Syscall6(procCertOpenStore.Addr(), 5, uintptr(storeProvider), uintptr(msgAndCertEncodingType), uintptr(cryptProv), uintptr(flags), uintptr(para), 0)
- handle = Handle(r0)
- if handle == InvalidHandle {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func CertEnumCertificatesInStore(store Handle, prevContext *CertContext) (context *CertContext, err error) {
- r0, _, e1 := Syscall(procCertEnumCertificatesInStore.Addr(), 2, uintptr(store), uintptr(unsafe.Pointer(prevContext)), 0)
- context = (*CertContext)(unsafe.Pointer(r0))
- if context == nil {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func CertAddCertificateContextToStore(store Handle, certContext *CertContext, addDisposition uint32, storeContext **CertContext) (err error) {
- r1, _, e1 := Syscall6(procCertAddCertificateContextToStore.Addr(), 4, uintptr(store), uintptr(unsafe.Pointer(certContext)), uintptr(addDisposition), uintptr(unsafe.Pointer(storeContext)), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func CertCloseStore(store Handle, flags uint32) (err error) {
- r1, _, e1 := Syscall(procCertCloseStore.Addr(), 2, uintptr(store), uintptr(flags), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func CertGetCertificateChain(engine Handle, leaf *CertContext, time *Filetime, additionalStore Handle, para *CertChainPara, flags uint32, reserved uintptr, chainCtx **CertChainContext) (err error) {
- r1, _, e1 := Syscall9(procCertGetCertificateChain.Addr(), 8, uintptr(engine), uintptr(unsafe.Pointer(leaf)), uintptr(unsafe.Pointer(time)), uintptr(additionalStore), uintptr(unsafe.Pointer(para)), uintptr(flags), uintptr(reserved), uintptr(unsafe.Pointer(chainCtx)), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func CertFreeCertificateChain(ctx *CertChainContext) {
- Syscall(procCertFreeCertificateChain.Addr(), 1, uintptr(unsafe.Pointer(ctx)), 0, 0)
- return
-}
-
-func CertCreateCertificateContext(certEncodingType uint32, certEncoded *byte, encodedLen uint32) (context *CertContext, err error) {
- r0, _, e1 := Syscall(procCertCreateCertificateContext.Addr(), 3, uintptr(certEncodingType), uintptr(unsafe.Pointer(certEncoded)), uintptr(encodedLen))
- context = (*CertContext)(unsafe.Pointer(r0))
- if context == nil {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func CertFreeCertificateContext(ctx *CertContext) (err error) {
- r1, _, e1 := Syscall(procCertFreeCertificateContext.Addr(), 1, uintptr(unsafe.Pointer(ctx)), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func CertVerifyCertificateChainPolicy(policyOID uintptr, chain *CertChainContext, para *CertChainPolicyPara, status *CertChainPolicyStatus) (err error) {
- r1, _, e1 := Syscall6(procCertVerifyCertificateChainPolicy.Addr(), 4, uintptr(policyOID), uintptr(unsafe.Pointer(chain)), uintptr(unsafe.Pointer(para)), uintptr(unsafe.Pointer(status)), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func RegOpenKeyEx(key Handle, subkey *uint16, options uint32, desiredAccess uint32, result *Handle) (regerrno error) {
- r0, _, _ := Syscall6(procRegOpenKeyExW.Addr(), 5, uintptr(key), uintptr(unsafe.Pointer(subkey)), uintptr(options), uintptr(desiredAccess), uintptr(unsafe.Pointer(result)), 0)
- if r0 != 0 {
- regerrno = Errno(r0)
- }
- return
-}
-
-func RegCloseKey(key Handle) (regerrno error) {
- r0, _, _ := Syscall(procRegCloseKey.Addr(), 1, uintptr(key), 0, 0)
- if r0 != 0 {
- regerrno = Errno(r0)
- }
- return
-}
-
-func RegQueryInfoKey(key Handle, class *uint16, classLen *uint32, reserved *uint32, subkeysLen *uint32, maxSubkeyLen *uint32, maxClassLen *uint32, valuesLen *uint32, maxValueNameLen *uint32, maxValueLen *uint32, saLen *uint32, lastWriteTime *Filetime) (regerrno error) {
- r0, _, _ := Syscall12(procRegQueryInfoKeyW.Addr(), 12, uintptr(key), uintptr(unsafe.Pointer(class)), uintptr(unsafe.Pointer(classLen)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(subkeysLen)), uintptr(unsafe.Pointer(maxSubkeyLen)), uintptr(unsafe.Pointer(maxClassLen)), uintptr(unsafe.Pointer(valuesLen)), uintptr(unsafe.Pointer(maxValueNameLen)), uintptr(unsafe.Pointer(maxValueLen)), uintptr(unsafe.Pointer(saLen)), uintptr(unsafe.Pointer(lastWriteTime)))
- if r0 != 0 {
- regerrno = Errno(r0)
- }
- return
-}
-
-func RegEnumKeyEx(key Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, classLen *uint32, lastWriteTime *Filetime) (regerrno error) {
- r0, _, _ := Syscall9(procRegEnumKeyExW.Addr(), 8, uintptr(key), uintptr(index), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(class)), uintptr(unsafe.Pointer(classLen)), uintptr(unsafe.Pointer(lastWriteTime)), 0)
- if r0 != 0 {
- regerrno = Errno(r0)
- }
- return
-}
-
-func RegQueryValueEx(key Handle, name *uint16, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) {
- r0, _, _ := Syscall6(procRegQueryValueExW.Addr(), 6, uintptr(key), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(valtype)), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(buflen)))
- if r0 != 0 {
- regerrno = Errno(r0)
- }
- return
-}
-
-func getCurrentProcessId() (pid uint32) {
- r0, _, _ := Syscall(procGetCurrentProcessId.Addr(), 0, 0, 0, 0)
- pid = uint32(r0)
- return
-}
-
-func GetConsoleMode(console Handle, mode *uint32) (err error) {
- r1, _, e1 := Syscall(procGetConsoleMode.Addr(), 2, uintptr(console), uintptr(unsafe.Pointer(mode)), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func WriteConsole(console Handle, buf *uint16, towrite uint32, written *uint32, reserved *byte) (err error) {
- r1, _, e1 := Syscall6(procWriteConsoleW.Addr(), 5, uintptr(console), uintptr(unsafe.Pointer(buf)), uintptr(towrite), uintptr(unsafe.Pointer(written)), uintptr(unsafe.Pointer(reserved)), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func ReadConsole(console Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) (err error) {
- r1, _, e1 := Syscall6(procReadConsoleW.Addr(), 5, uintptr(console), uintptr(unsafe.Pointer(buf)), uintptr(toread), uintptr(unsafe.Pointer(read)), uintptr(unsafe.Pointer(inputControl)), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func CreateToolhelp32Snapshot(flags uint32, processId uint32) (handle Handle, err error) {
- r0, _, e1 := Syscall(procCreateToolhelp32Snapshot.Addr(), 2, uintptr(flags), uintptr(processId), 0)
- handle = Handle(r0)
- if handle == InvalidHandle {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func Process32First(snapshot Handle, procEntry *ProcessEntry32) (err error) {
- r1, _, e1 := Syscall(procProcess32FirstW.Addr(), 2, uintptr(snapshot), uintptr(unsafe.Pointer(procEntry)), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func Process32Next(snapshot Handle, procEntry *ProcessEntry32) (err error) {
- r1, _, e1 := Syscall(procProcess32NextW.Addr(), 2, uintptr(snapshot), uintptr(unsafe.Pointer(procEntry)), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func DeviceIoControl(handle Handle, ioControlCode uint32, inBuffer *byte, inBufferSize uint32, outBuffer *byte, outBufferSize uint32, bytesReturned *uint32, overlapped *Overlapped) (err error) {
- r1, _, e1 := Syscall9(procDeviceIoControl.Addr(), 8, uintptr(handle), uintptr(ioControlCode), uintptr(unsafe.Pointer(inBuffer)), uintptr(inBufferSize), uintptr(unsafe.Pointer(outBuffer)), uintptr(outBufferSize), uintptr(unsafe.Pointer(bytesReturned)), uintptr(unsafe.Pointer(overlapped)), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func CreateSymbolicLink(symlinkfilename *uint16, targetfilename *uint16, flags uint32) (err error) {
- r1, _, e1 := Syscall(procCreateSymbolicLinkW.Addr(), 3, uintptr(unsafe.Pointer(symlinkfilename)), uintptr(unsafe.Pointer(targetfilename)), uintptr(flags))
- if r1&0xff == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func CreateHardLink(filename *uint16, existingfilename *uint16, reserved uintptr) (err error) {
- r1, _, e1 := Syscall(procCreateHardLinkW.Addr(), 3, uintptr(unsafe.Pointer(filename)), uintptr(unsafe.Pointer(existingfilename)), uintptr(reserved))
- if r1&0xff == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func WSAStartup(verreq uint32, data *WSAData) (sockerr error) {
- r0, _, _ := Syscall(procWSAStartup.Addr(), 2, uintptr(verreq), uintptr(unsafe.Pointer(data)), 0)
- if r0 != 0 {
- sockerr = Errno(r0)
- }
- return
-}
-
-func WSACleanup() (err error) {
- r1, _, e1 := Syscall(procWSACleanup.Addr(), 0, 0, 0, 0)
- if r1 == socket_error {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func WSAIoctl(s Handle, iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbob uint32, cbbr *uint32, overlapped *Overlapped, completionRoutine uintptr) (err error) {
- r1, _, e1 := Syscall9(procWSAIoctl.Addr(), 9, uintptr(s), uintptr(iocc), uintptr(unsafe.Pointer(inbuf)), uintptr(cbif), uintptr(unsafe.Pointer(outbuf)), uintptr(cbob), uintptr(unsafe.Pointer(cbbr)), uintptr(unsafe.Pointer(overlapped)), uintptr(completionRoutine))
- if r1 == socket_error {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func socket(af int32, typ int32, protocol int32) (handle Handle, err error) {
- r0, _, e1 := Syscall(procsocket.Addr(), 3, uintptr(af), uintptr(typ), uintptr(protocol))
- handle = Handle(r0)
- if handle == InvalidHandle {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func Setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32) (err error) {
- r1, _, e1 := Syscall6(procsetsockopt.Addr(), 5, uintptr(s), uintptr(level), uintptr(optname), uintptr(unsafe.Pointer(optval)), uintptr(optlen), 0)
- if r1 == socket_error {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func Getsockopt(s Handle, level int32, optname int32, optval *byte, optlen *int32) (err error) {
- r1, _, e1 := Syscall6(procgetsockopt.Addr(), 5, uintptr(s), uintptr(level), uintptr(optname), uintptr(unsafe.Pointer(optval)), uintptr(unsafe.Pointer(optlen)), 0)
- if r1 == socket_error {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func bind(s Handle, name unsafe.Pointer, namelen int32) (err error) {
- r1, _, e1 := Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen))
- if r1 == socket_error {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func connect(s Handle, name unsafe.Pointer, namelen int32) (err error) {
- r1, _, e1 := Syscall(procconnect.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen))
- if r1 == socket_error {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func getsockname(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) {
- r1, _, e1 := Syscall(procgetsockname.Addr(), 3, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
- if r1 == socket_error {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func getpeername(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) {
- r1, _, e1 := Syscall(procgetpeername.Addr(), 3, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
- if r1 == socket_error {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func listen(s Handle, backlog int32) (err error) {
- r1, _, e1 := Syscall(proclisten.Addr(), 2, uintptr(s), uintptr(backlog), 0)
- if r1 == socket_error {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func shutdown(s Handle, how int32) (err error) {
- r1, _, e1 := Syscall(procshutdown.Addr(), 2, uintptr(s), uintptr(how), 0)
- if r1 == socket_error {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func Closesocket(s Handle) (err error) {
- r1, _, e1 := Syscall(procclosesocket.Addr(), 1, uintptr(s), 0, 0)
- if r1 == socket_error {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func AcceptEx(ls Handle, as Handle, buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, recvd *uint32, overlapped *Overlapped) (err error) {
- r1, _, e1 := Syscall9(procAcceptEx.Addr(), 8, uintptr(ls), uintptr(as), uintptr(unsafe.Pointer(buf)), uintptr(rxdatalen), uintptr(laddrlen), uintptr(raddrlen), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(overlapped)), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetAcceptExSockaddrs(buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, lrsa **RawSockaddrAny, lrsalen *int32, rrsa **RawSockaddrAny, rrsalen *int32) {
- Syscall9(procGetAcceptExSockaddrs.Addr(), 8, uintptr(unsafe.Pointer(buf)), uintptr(rxdatalen), uintptr(laddrlen), uintptr(raddrlen), uintptr(unsafe.Pointer(lrsa)), uintptr(unsafe.Pointer(lrsalen)), uintptr(unsafe.Pointer(rrsa)), uintptr(unsafe.Pointer(rrsalen)), 0)
- return
-}
-
-func WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, overlapped *Overlapped, croutine *byte) (err error) {
- r1, _, e1 := Syscall9(procWSARecv.Addr(), 7, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0, 0)
- if r1 == socket_error {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func WSASend(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, overlapped *Overlapped, croutine *byte) (err error) {
- r1, _, e1 := Syscall9(procWSASend.Addr(), 7, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(sent)), uintptr(flags), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0, 0)
- if r1 == socket_error {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func WSARecvFrom(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, from *RawSockaddrAny, fromlen *int32, overlapped *Overlapped, croutine *byte) (err error) {
- r1, _, e1 := Syscall9(procWSARecvFrom.Addr(), 9, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)))
- if r1 == socket_error {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func WSASendTo(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *RawSockaddrAny, tolen int32, overlapped *Overlapped, croutine *byte) (err error) {
- r1, _, e1 := Syscall9(procWSASendTo.Addr(), 9, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(sent)), uintptr(flags), uintptr(unsafe.Pointer(to)), uintptr(tolen), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)))
- if r1 == socket_error {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetHostByName(name string) (h *Hostent, err error) {
- var _p0 *byte
- _p0, err = BytePtrFromString(name)
- if err != nil {
- return
- }
- r0, _, e1 := Syscall(procgethostbyname.Addr(), 1, uintptr(unsafe.Pointer(_p0)), 0, 0)
- h = (*Hostent)(unsafe.Pointer(r0))
- if h == nil {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetServByName(name string, proto string) (s *Servent, err error) {
- var _p0 *byte
- _p0, err = BytePtrFromString(name)
- if err != nil {
- return
- }
- var _p1 *byte
- _p1, err = BytePtrFromString(proto)
- if err != nil {
- return
- }
- r0, _, e1 := Syscall(procgetservbyname.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
- s = (*Servent)(unsafe.Pointer(r0))
- if s == nil {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func Ntohs(netshort uint16) (u uint16) {
- r0, _, _ := Syscall(procntohs.Addr(), 1, uintptr(netshort), 0, 0)
- u = uint16(r0)
- return
-}
-
-func GetProtoByName(name string) (p *Protoent, err error) {
- var _p0 *byte
- _p0, err = BytePtrFromString(name)
- if err != nil {
- return
- }
- r0, _, e1 := Syscall(procgetprotobyname.Addr(), 1, uintptr(unsafe.Pointer(_p0)), 0, 0)
- p = (*Protoent)(unsafe.Pointer(r0))
- if p == nil {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func DnsQuery(name string, qtype uint16, options uint32, extra *byte, qrs **DNSRecord, pr *byte) (status error) {
- var _p0 *uint16
- _p0, status = UTF16PtrFromString(name)
- if status != nil {
- return
- }
- r0, _, _ := Syscall6(procDnsQuery_W.Addr(), 6, uintptr(unsafe.Pointer(_p0)), uintptr(qtype), uintptr(options), uintptr(unsafe.Pointer(extra)), uintptr(unsafe.Pointer(qrs)), uintptr(unsafe.Pointer(pr)))
- if r0 != 0 {
- status = Errno(r0)
- }
- return
-}
-
-func DnsRecordListFree(rl *DNSRecord, freetype uint32) {
- Syscall(procDnsRecordListFree.Addr(), 2, uintptr(unsafe.Pointer(rl)), uintptr(freetype), 0)
- return
-}
-
-func GetAddrInfoW(nodename *uint16, servicename *uint16, hints *AddrinfoW, result **AddrinfoW) (sockerr error) {
- r0, _, _ := Syscall6(procGetAddrInfoW.Addr(), 4, uintptr(unsafe.Pointer(nodename)), uintptr(unsafe.Pointer(servicename)), uintptr(unsafe.Pointer(hints)), uintptr(unsafe.Pointer(result)), 0, 0)
- if r0 != 0 {
- sockerr = Errno(r0)
- }
- return
-}
-
-func FreeAddrInfoW(addrinfo *AddrinfoW) {
- Syscall(procFreeAddrInfoW.Addr(), 1, uintptr(unsafe.Pointer(addrinfo)), 0, 0)
- return
-}
-
-func GetIfEntry(pIfRow *MibIfRow) (errcode error) {
- r0, _, _ := Syscall(procGetIfEntry.Addr(), 1, uintptr(unsafe.Pointer(pIfRow)), 0, 0)
- if r0 != 0 {
- errcode = Errno(r0)
- }
- return
-}
-
-func GetAdaptersInfo(ai *IpAdapterInfo, ol *uint32) (errcode error) {
- r0, _, _ := Syscall(procGetAdaptersInfo.Addr(), 2, uintptr(unsafe.Pointer(ai)), uintptr(unsafe.Pointer(ol)), 0)
- if r0 != 0 {
- errcode = Errno(r0)
- }
- return
-}
-
-func SetFileCompletionNotificationModes(handle Handle, flags uint8) (err error) {
- r1, _, e1 := Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(handle), uintptr(flags), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func WSAEnumProtocols(protocols *int32, protocolBuffer *WSAProtocolInfo, bufferLength *uint32) (n int32, err error) {
- r0, _, e1 := Syscall(procWSAEnumProtocolsW.Addr(), 3, uintptr(unsafe.Pointer(protocols)), uintptr(unsafe.Pointer(protocolBuffer)), uintptr(unsafe.Pointer(bufferLength)))
- n = int32(r0)
- if n == -1 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func TranslateName(accName *uint16, accNameFormat uint32, desiredNameFormat uint32, translatedName *uint16, nSize *uint32) (err error) {
- r1, _, e1 := Syscall6(procTranslateNameW.Addr(), 5, uintptr(unsafe.Pointer(accName)), uintptr(accNameFormat), uintptr(desiredNameFormat), uintptr(unsafe.Pointer(translatedName)), uintptr(unsafe.Pointer(nSize)), 0)
- if r1&0xff == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetUserNameEx(nameFormat uint32, nameBuffre *uint16, nSize *uint32) (err error) {
- r1, _, e1 := Syscall(procGetUserNameExW.Addr(), 3, uintptr(nameFormat), uintptr(unsafe.Pointer(nameBuffre)), uintptr(unsafe.Pointer(nSize)))
- if r1&0xff == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func NetUserGetInfo(serverName *uint16, userName *uint16, level uint32, buf **byte) (neterr error) {
- r0, _, _ := Syscall6(procNetUserGetInfo.Addr(), 4, uintptr(unsafe.Pointer(serverName)), uintptr(unsafe.Pointer(userName)), uintptr(level), uintptr(unsafe.Pointer(buf)), 0, 0)
- if r0 != 0 {
- neterr = Errno(r0)
- }
- return
-}
-
-func NetGetJoinInformation(server *uint16, name **uint16, bufType *uint32) (neterr error) {
- r0, _, _ := Syscall(procNetGetJoinInformation.Addr(), 3, uintptr(unsafe.Pointer(server)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(bufType)))
- if r0 != 0 {
- neterr = Errno(r0)
- }
- return
-}
-
-func NetApiBufferFree(buf *byte) (neterr error) {
- r0, _, _ := Syscall(procNetApiBufferFree.Addr(), 1, uintptr(unsafe.Pointer(buf)), 0, 0)
- if r0 != 0 {
- neterr = Errno(r0)
- }
- return
-}
-
-func LookupAccountSid(systemName *uint16, sid *SID, name *uint16, nameLen *uint32, refdDomainName *uint16, refdDomainNameLen *uint32, use *uint32) (err error) {
- r1, _, e1 := Syscall9(procLookupAccountSidW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(refdDomainName)), uintptr(unsafe.Pointer(refdDomainNameLen)), uintptr(unsafe.Pointer(use)), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func LookupAccountName(systemName *uint16, accountName *uint16, sid *SID, sidLen *uint32, refdDomainName *uint16, refdDomainNameLen *uint32, use *uint32) (err error) {
- r1, _, e1 := Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidLen)), uintptr(unsafe.Pointer(refdDomainName)), uintptr(unsafe.Pointer(refdDomainNameLen)), uintptr(unsafe.Pointer(use)), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func ConvertSidToStringSid(sid *SID, stringSid **uint16) (err error) {
- r1, _, e1 := Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(stringSid)), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func ConvertStringSidToSid(stringSid *uint16, sid **SID) (err error) {
- r1, _, e1 := Syscall(procConvertStringSidToSidW.Addr(), 2, uintptr(unsafe.Pointer(stringSid)), uintptr(unsafe.Pointer(sid)), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetLengthSid(sid *SID) (len uint32) {
- r0, _, _ := Syscall(procGetLengthSid.Addr(), 1, uintptr(unsafe.Pointer(sid)), 0, 0)
- len = uint32(r0)
- return
-}
-
-func CopySid(destSidLen uint32, destSid *SID, srcSid *SID) (err error) {
- r1, _, e1 := Syscall(procCopySid.Addr(), 3, uintptr(destSidLen), uintptr(unsafe.Pointer(destSid)), uintptr(unsafe.Pointer(srcSid)))
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func OpenProcessToken(h Handle, access uint32, token *Token) (err error) {
- r1, _, e1 := Syscall(procOpenProcessToken.Addr(), 3, uintptr(h), uintptr(access), uintptr(unsafe.Pointer(token)))
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetTokenInformation(t Token, infoClass uint32, info *byte, infoLen uint32, returnedLen *uint32) (err error) {
- r1, _, e1 := Syscall6(procGetTokenInformation.Addr(), 5, uintptr(t), uintptr(infoClass), uintptr(unsafe.Pointer(info)), uintptr(infoLen), uintptr(unsafe.Pointer(returnedLen)), 0)
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
-
-func GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) {
- r1, _, e1 := Syscall(procGetUserProfileDirectoryW.Addr(), 3, uintptr(t), uintptr(unsafe.Pointer(dir)), uintptr(unsafe.Pointer(dirLen)))
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = EINVAL
- }
- }
- return
-}
diff --git a/src/pkg/syscall/ztypes_windows.go b/src/pkg/syscall/ztypes_windows.go
index 8b3625f146..1363da01a8 100644
--- a/src/pkg/syscall/ztypes_windows.go
+++ b/src/pkg/syscall/ztypes_windows.go
@@ -689,6 +689,18 @@ const (
DNS_TYPE_NBSTAT = 0xff01
)
+const (
+ DNS_INFO_NO_RECORDS = 0x251D
+)
+
+const (
+ // flags inside DNSRecord.Dw
+ DnsSectionQuestion = 0x0000
+ DnsSectionAnswer = 0x0001
+ DnsSectionAuthority = 0x0002
+ DnsSectionAdditional = 0x0003
+)
+
type DNSSRVData struct {
Target *uint16
Priority uint16
diff --git a/src/pkg/text/scanner/scanner.go b/src/pkg/text/scanner/scanner.go
index 25b2c9e02a..5199ee4fc7 100644
--- a/src/pkg/text/scanner/scanner.go
+++ b/src/pkg/text/scanner/scanner.go
@@ -66,6 +66,12 @@ func (pos Position) String() string {
//
// ScanIdents | ScanInts | SkipComments
//
+// With the exceptions of comments, which are skipped if SkipComments is
+// set, unrecognized tokens are not ignored. Instead, the scanner simply
+// returns the respective individual characters (or possibly sub-tokens).
+// For instance, if the mode is ScanIdents (not ScanStrings), the string
+// "foo" is scanned as the token sequence '"' Ident '"'.
+//
const (
ScanIdents = 1 << -Ident
ScanInts = 1 << -Int
diff --git a/src/pkg/text/template/exec.go b/src/pkg/text/template/exec.go
index 2f32312645..8e155d478e 100644
--- a/src/pkg/text/template/exec.go
+++ b/src/pkg/text/template/exec.go
@@ -393,7 +393,7 @@ func (s *state) idealConstant(constant *parse.NumberNode) reflect.Value {
switch {
case constant.IsComplex:
return reflect.ValueOf(constant.Complex128) // incontrovertible.
- case constant.IsFloat && strings.IndexAny(constant.Text, ".eE") >= 0:
+ case constant.IsFloat && !isHexConstant(constant.Text) && strings.IndexAny(constant.Text, ".eE") >= 0:
return reflect.ValueOf(constant.Float64)
case constant.IsInt:
n := int(constant.Int64)
@@ -407,6 +407,10 @@ func (s *state) idealConstant(constant *parse.NumberNode) reflect.Value {
return zero
}
+func isHexConstant(s string) bool {
+ return len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')
+}
+
func (s *state) evalFieldNode(dot reflect.Value, field *parse.FieldNode, args []parse.Node, final reflect.Value) reflect.Value {
s.at(field)
return s.evalFieldChain(dot, dot, field, field.Ident, args, final)
diff --git a/src/pkg/text/template/exec_test.go b/src/pkg/text/template/exec_test.go
index 868f2cb94c..663aaf3af8 100644
--- a/src/pkg/text/template/exec_test.go
+++ b/src/pkg/text/template/exec_test.go
@@ -514,6 +514,11 @@ var execTests = []execTest{
{"bug10", "{{mapOfThree.three}}-{{(mapOfThree).three}}", "3-3", 0, true},
// Dereferencing nil pointer while evaluating function arguments should not panic. Issue 7333.
{"bug11", "{{valueString .PS}}", "", T{}, false},
+ // 0xef gave constant type float64. Issue 8622.
+ {"bug12xe", "{{printf `%T` 0xef}}", "int", T{}, true},
+ {"bug12xE", "{{printf `%T` 0xEE}}", "int", T{}, true},
+ {"bug12Xe", "{{printf `%T` 0Xef}}", "int", T{}, true},
+ {"bug12XE", "{{printf `%T` 0XEE}}", "int", T{}, true},
}
func zeroArgs() string {
diff --git a/src/pkg/text/template/parse/node.go b/src/pkg/text/template/parse/node.go
index dc6a3bb929..55c37f6dba 100644
--- a/src/pkg/text/template/parse/node.go
+++ b/src/pkg/text/template/parse/node.go
@@ -26,8 +26,9 @@ type Node interface {
// CopyXxx methods that return *XxxNode.
Copy() Node
Position() Pos // byte position of start of node in full original input string
- // Make sure only functions in this package can create Nodes.
- unexported()
+ // tree returns the containing *Tree.
+ // It is unexported so all implementations of Node are in this package.
+ tree() *Tree
}
// NodeType identifies the type of a parse tree node.
@@ -41,11 +42,6 @@ func (p Pos) Position() Pos {
return p
}
-// unexported keeps Node implementations local to the package.
-// All implementations embed Pos, so this takes care of it.
-func (Pos) unexported() {
-}
-
// Type returns itself and provides an easy default implementation
// for embedding in a Node. Embedded in all non-trivial Nodes.
func (t NodeType) Type() NodeType {
@@ -81,17 +77,22 @@ const (
type ListNode struct {
NodeType
Pos
+ tr *Tree
Nodes []Node // The element nodes in lexical order.
}
-func newList(pos Pos) *ListNode {
- return &ListNode{NodeType: NodeList, Pos: pos}
+func (t *Tree) newList(pos Pos) *ListNode {
+ return &ListNode{tr: t, NodeType: NodeList, Pos: pos}
}
func (l *ListNode) append(n Node) {
l.Nodes = append(l.Nodes, n)
}
+func (l *ListNode) tree() *Tree {
+ return l.tr
+}
+
func (l *ListNode) String() string {
b := new(bytes.Buffer)
for _, n := range l.Nodes {
@@ -104,7 +105,7 @@ func (l *ListNode) CopyList() *ListNode {
if l == nil {
return l
}
- n := newList(l.Pos)
+ n := l.tr.newList(l.Pos)
for _, elem := range l.Nodes {
n.append(elem.Copy())
}
@@ -119,32 +120,38 @@ func (l *ListNode) Copy() Node {
type TextNode struct {
NodeType
Pos
+ tr *Tree
Text []byte // The text; may span newlines.
}
-func newText(pos Pos, text string) *TextNode {
- return &TextNode{NodeType: NodeText, Pos: pos, Text: []byte(text)}
+func (t *Tree) newText(pos Pos, text string) *TextNode {
+ return &TextNode{tr: t, NodeType: NodeText, Pos: pos, Text: []byte(text)}
}
func (t *TextNode) String() string {
return fmt.Sprintf(textFormat, t.Text)
}
+func (t *TextNode) tree() *Tree {
+ return t.tr
+}
+
func (t *TextNode) Copy() Node {
- return &TextNode{NodeType: NodeText, Text: append([]byte{}, t.Text...)}
+ return &TextNode{tr: t.tr, NodeType: NodeText, Pos: t.Pos, Text: append([]byte{}, t.Text...)}
}
// PipeNode holds a pipeline with optional declaration
type PipeNode struct {
NodeType
Pos
+ tr *Tree
Line int // The line number in the input (deprecated; kept for compatibility)
Decl []*VariableNode // Variable declarations in lexical order.
Cmds []*CommandNode // The commands in lexical order.
}
-func newPipeline(pos Pos, line int, decl []*VariableNode) *PipeNode {
- return &PipeNode{NodeType: NodePipe, Pos: pos, Line: line, Decl: decl}
+func (t *Tree) newPipeline(pos Pos, line int, decl []*VariableNode) *PipeNode {
+ return &PipeNode{tr: t, NodeType: NodePipe, Pos: pos, Line: line, Decl: decl}
}
func (p *PipeNode) append(command *CommandNode) {
@@ -171,6 +178,10 @@ func (p *PipeNode) String() string {
return s
}
+func (p *PipeNode) tree() *Tree {
+ return p.tr
+}
+
func (p *PipeNode) CopyPipe() *PipeNode {
if p == nil {
return p
@@ -179,7 +190,7 @@ func (p *PipeNode) CopyPipe() *PipeNode {
for _, d := range p.Decl {
decl = append(decl, d.Copy().(*VariableNode))
}
- n := newPipeline(p.Pos, p.Line, decl)
+ n := p.tr.newPipeline(p.Pos, p.Line, decl)
for _, c := range p.Cmds {
n.append(c.Copy().(*CommandNode))
}
@@ -196,12 +207,13 @@ func (p *PipeNode) Copy() Node {
type ActionNode struct {
NodeType
Pos
+ tr *Tree
Line int // The line number in the input (deprecated; kept for compatibility)
Pipe *PipeNode // The pipeline in the action.
}
-func newAction(pos Pos, line int, pipe *PipeNode) *ActionNode {
- return &ActionNode{NodeType: NodeAction, Pos: pos, Line: line, Pipe: pipe}
+func (t *Tree) newAction(pos Pos, line int, pipe *PipeNode) *ActionNode {
+ return &ActionNode{tr: t, NodeType: NodeAction, Pos: pos, Line: line, Pipe: pipe}
}
func (a *ActionNode) String() string {
@@ -209,8 +221,12 @@ func (a *ActionNode) String() string {
}
+func (a *ActionNode) tree() *Tree {
+ return a.tr
+}
+
func (a *ActionNode) Copy() Node {
- return newAction(a.Pos, a.Line, a.Pipe.CopyPipe())
+ return a.tr.newAction(a.Pos, a.Line, a.Pipe.CopyPipe())
}
@@ -218,11 +234,12 @@ func (a *ActionNode) Copy() Node {
type CommandNode struct {
NodeType
Pos
+ tr *Tree
Args []Node // Arguments in lexical order: Identifier, field, or constant.
}
-func newCommand(pos Pos) *CommandNode {
- return &CommandNode{NodeType: NodeCommand, Pos: pos}
+func (t *Tree) newCommand(pos Pos) *CommandNode {
+ return &CommandNode{tr: t, NodeType: NodeCommand, Pos: pos}
}
func (c *CommandNode) append(arg Node) {
@@ -244,11 +261,15 @@ func (c *CommandNode) String() string {
return s
}
+func (c *CommandNode) tree() *Tree {
+ return c.tr
+}
+
func (c *CommandNode) Copy() Node {
if c == nil {
return c
}
- n := newCommand(c.Pos)
+ n := c.tr.newCommand(c.Pos)
for _, c := range c.Args {
n.append(c.Copy())
}
@@ -259,6 +280,7 @@ func (c *CommandNode) Copy() Node {
type IdentifierNode struct {
NodeType
Pos
+ tr *Tree
Ident string // The identifier's name.
}
@@ -275,12 +297,24 @@ func (i *IdentifierNode) SetPos(pos Pos) *IdentifierNode {
return i
}
+// SetTree sets the parent tree for the node. NewIdentifier is a public method so we can't modify its signature.
+// Chained for convenience.
+// TODO: fix one day?
+func (i *IdentifierNode) SetTree(t *Tree) *IdentifierNode {
+ i.tr = t
+ return i
+}
+
func (i *IdentifierNode) String() string {
return i.Ident
}
+func (i *IdentifierNode) tree() *Tree {
+ return i.tr
+}
+
func (i *IdentifierNode) Copy() Node {
- return NewIdentifier(i.Ident).SetPos(i.Pos)
+ return NewIdentifier(i.Ident).SetTree(i.tr).SetPos(i.Pos)
}
// VariableNode holds a list of variable names, possibly with chained field
@@ -288,11 +322,12 @@ func (i *IdentifierNode) Copy() Node {
type VariableNode struct {
NodeType
Pos
+ tr *Tree
Ident []string // Variable name and fields in lexical order.
}
-func newVariable(pos Pos, ident string) *VariableNode {
- return &VariableNode{NodeType: NodeVariable, Pos: pos, Ident: strings.Split(ident, ".")}
+func (t *Tree) newVariable(pos Pos, ident string) *VariableNode {
+ return &VariableNode{tr: t, NodeType: NodeVariable, Pos: pos, Ident: strings.Split(ident, ".")}
}
func (v *VariableNode) String() string {
@@ -306,20 +341,29 @@ func (v *VariableNode) String() string {
return s
}
+func (v *VariableNode) tree() *Tree {
+ return v.tr
+}
+
func (v *VariableNode) Copy() Node {
- return &VariableNode{NodeType: NodeVariable, Pos: v.Pos, Ident: append([]string{}, v.Ident...)}
+ return &VariableNode{tr: v.tr, NodeType: NodeVariable, Pos: v.Pos, Ident: append([]string{}, v.Ident...)}
}
// DotNode holds the special identifier '.'.
type DotNode struct {
+ NodeType
Pos
+ tr *Tree
}
-func newDot(pos Pos) *DotNode {
- return &DotNode{Pos: pos}
+func (t *Tree) newDot(pos Pos) *DotNode {
+ return &DotNode{tr: t, NodeType: NodeDot, Pos: pos}
}
func (d *DotNode) Type() NodeType {
+ // Override method on embedded NodeType for API compatibility.
+ // TODO: Not really a problem; could change API without effect but
+ // api tool complains.
return NodeDot
}
@@ -327,20 +371,29 @@ func (d *DotNode) String() string {
return "."
}
+func (d *DotNode) tree() *Tree {
+ return d.tr
+}
+
func (d *DotNode) Copy() Node {
- return newDot(d.Pos)
+ return d.tr.newDot(d.Pos)
}
// NilNode holds the special identifier 'nil' representing an untyped nil constant.
type NilNode struct {
+ NodeType
Pos
+ tr *Tree
}
-func newNil(pos Pos) *NilNode {
- return &NilNode{Pos: pos}
+func (t *Tree) newNil(pos Pos) *NilNode {
+ return &NilNode{tr: t, NodeType: NodeNil, Pos: pos}
}
func (n *NilNode) Type() NodeType {
+ // Override method on embedded NodeType for API compatibility.
+ // TODO: Not really a problem; could change API without effect but
+ // api tool complains.
return NodeNil
}
@@ -348,8 +401,12 @@ func (n *NilNode) String() string {
return "nil"
}
+func (n *NilNode) tree() *Tree {
+ return n.tr
+}
+
func (n *NilNode) Copy() Node {
- return newNil(n.Pos)
+ return n.tr.newNil(n.Pos)
}
// FieldNode holds a field (identifier starting with '.').
@@ -358,11 +415,12 @@ func (n *NilNode) Copy() Node {
type FieldNode struct {
NodeType
Pos
+ tr *Tree
Ident []string // The identifiers in lexical order.
}
-func newField(pos Pos, ident string) *FieldNode {
- return &FieldNode{NodeType: NodeField, Pos: pos, Ident: strings.Split(ident[1:], ".")} // [1:] to drop leading period
+func (t *Tree) newField(pos Pos, ident string) *FieldNode {
+ return &FieldNode{tr: t, NodeType: NodeField, Pos: pos, Ident: strings.Split(ident[1:], ".")} // [1:] to drop leading period
}
func (f *FieldNode) String() string {
@@ -373,8 +431,12 @@ func (f *FieldNode) String() string {
return s
}
+func (f *FieldNode) tree() *Tree {
+ return f.tr
+}
+
func (f *FieldNode) Copy() Node {
- return &FieldNode{NodeType: NodeField, Pos: f.Pos, Ident: append([]string{}, f.Ident...)}
+ return &FieldNode{tr: f.tr, NodeType: NodeField, Pos: f.Pos, Ident: append([]string{}, f.Ident...)}
}
// ChainNode holds a term followed by a chain of field accesses (identifier starting with '.').
@@ -383,12 +445,13 @@ func (f *FieldNode) Copy() Node {
type ChainNode struct {
NodeType
Pos
+ tr *Tree
Node Node
Field []string // The identifiers in lexical order.
}
-func newChain(pos Pos, node Node) *ChainNode {
- return &ChainNode{NodeType: NodeChain, Pos: pos, Node: node}
+func (t *Tree) newChain(pos Pos, node Node) *ChainNode {
+ return &ChainNode{tr: t, NodeType: NodeChain, Pos: pos, Node: node}
}
// Add adds the named field (which should start with a period) to the end of the chain.
@@ -414,19 +477,24 @@ func (c *ChainNode) String() string {
return s
}
+func (c *ChainNode) tree() *Tree {
+ return c.tr
+}
+
func (c *ChainNode) Copy() Node {
- return &ChainNode{NodeType: NodeChain, Pos: c.Pos, Node: c.Node, Field: append([]string{}, c.Field...)}
+ return &ChainNode{tr: c.tr, NodeType: NodeChain, Pos: c.Pos, Node: c.Node, Field: append([]string{}, c.Field...)}
}
// BoolNode holds a boolean constant.
type BoolNode struct {
NodeType
Pos
+ tr *Tree
True bool // The value of the boolean constant.
}
-func newBool(pos Pos, true bool) *BoolNode {
- return &BoolNode{NodeType: NodeBool, Pos: pos, True: true}
+func (t *Tree) newBool(pos Pos, true bool) *BoolNode {
+ return &BoolNode{tr: t, NodeType: NodeBool, Pos: pos, True: true}
}
func (b *BoolNode) String() string {
@@ -436,8 +504,12 @@ func (b *BoolNode) String() string {
return "false"
}
+func (b *BoolNode) tree() *Tree {
+ return b.tr
+}
+
func (b *BoolNode) Copy() Node {
- return newBool(b.Pos, b.True)
+ return b.tr.newBool(b.Pos, b.True)
}
// NumberNode holds a number: signed or unsigned integer, float, or complex.
@@ -446,6 +518,7 @@ func (b *BoolNode) Copy() Node {
type NumberNode struct {
NodeType
Pos
+ tr *Tree
IsInt bool // Number has an integral value.
IsUint bool // Number has an unsigned integral value.
IsFloat bool // Number has a floating-point value.
@@ -457,8 +530,8 @@ type NumberNode struct {
Text string // The original textual representation from the input.
}
-func newNumber(pos Pos, text string, typ itemType) (*NumberNode, error) {
- n := &NumberNode{NodeType: NodeNumber, Pos: pos, Text: text}
+func (t *Tree) newNumber(pos Pos, text string, typ itemType) (*NumberNode, error) {
+ n := &NumberNode{tr: t, NodeType: NodeNumber, Pos: pos, Text: text}
switch typ {
case itemCharConstant:
rune, _, tail, err := strconv.UnquoteChar(text[1:], text[0])
@@ -559,6 +632,10 @@ func (n *NumberNode) String() string {
return n.Text
}
+func (n *NumberNode) tree() *Tree {
+ return n.tr
+}
+
func (n *NumberNode) Copy() Node {
nn := new(NumberNode)
*nn = *n // Easy, fast, correct.
@@ -569,53 +646,61 @@ func (n *NumberNode) Copy() Node {
type StringNode struct {
NodeType
Pos
+ tr *Tree
Quoted string // The original text of the string, with quotes.
Text string // The string, after quote processing.
}
-func newString(pos Pos, orig, text string) *StringNode {
- return &StringNode{NodeType: NodeString, Pos: pos, Quoted: orig, Text: text}
+func (t *Tree) newString(pos Pos, orig, text string) *StringNode {
+ return &StringNode{tr: t, NodeType: NodeString, Pos: pos, Quoted: orig, Text: text}
}
func (s *StringNode) String() string {
return s.Quoted
}
+func (s *StringNode) tree() *Tree {
+ return s.tr
+}
+
func (s *StringNode) Copy() Node {
- return newString(s.Pos, s.Quoted, s.Text)
+ return s.tr.newString(s.Pos, s.Quoted, s.Text)
}
// endNode represents an {{end}} action.
// It does not appear in the final parse tree.
type endNode struct {
+ NodeType
Pos
+ tr *Tree
}
-func newEnd(pos Pos) *endNode {
- return &endNode{Pos: pos}
-}
-
-func (e *endNode) Type() NodeType {
- return nodeEnd
+func (t *Tree) newEnd(pos Pos) *endNode {
+ return &endNode{tr: t, NodeType: nodeEnd, Pos: pos}
}
func (e *endNode) String() string {
return "{{end}}"
}
+func (e *endNode) tree() *Tree {
+ return e.tr
+}
+
func (e *endNode) Copy() Node {
- return newEnd(e.Pos)
+ return e.tr.newEnd(e.Pos)
}
// elseNode represents an {{else}} action. Does not appear in the final tree.
type elseNode struct {
NodeType
Pos
+ tr *Tree
Line int // The line number in the input (deprecated; kept for compatibility)
}
-func newElse(pos Pos, line int) *elseNode {
- return &elseNode{NodeType: nodeElse, Pos: pos, Line: line}
+func (t *Tree) newElse(pos Pos, line int) *elseNode {
+ return &elseNode{tr: t, NodeType: nodeElse, Pos: pos, Line: line}
}
func (e *elseNode) Type() NodeType {
@@ -626,14 +711,19 @@ func (e *elseNode) String() string {
return "{{else}}"
}
+func (e *elseNode) tree() *Tree {
+ return e.tr
+}
+
func (e *elseNode) Copy() Node {
- return newElse(e.Pos, e.Line)
+ return e.tr.newElse(e.Pos, e.Line)
}
// BranchNode is the common representation of if, range, and with.
type BranchNode struct {
NodeType
Pos
+ tr *Tree
Line int // The line number in the input (deprecated; kept for compatibility)
Pipe *PipeNode // The pipeline to be evaluated.
List *ListNode // What to execute if the value is non-empty.
@@ -658,17 +748,34 @@ func (b *BranchNode) String() string {
return fmt.Sprintf("{{%s %s}}%s{{end}}", name, b.Pipe, b.List)
}
+func (b *BranchNode) tree() *Tree {
+ return b.tr
+}
+
+func (b *BranchNode) Copy() Node {
+ switch b.NodeType {
+ case NodeIf:
+ return b.tr.newIf(b.Pos, b.Line, b.Pipe, b.List, b.ElseList)
+ case NodeRange:
+ return b.tr.newRange(b.Pos, b.Line, b.Pipe, b.List, b.ElseList)
+ case NodeWith:
+ return b.tr.newWith(b.Pos, b.Line, b.Pipe, b.List, b.ElseList)
+ default:
+ panic("unknown branch type")
+ }
+}
+
// IfNode represents an {{if}} action and its commands.
type IfNode struct {
BranchNode
}
-func newIf(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *IfNode {
- return &IfNode{BranchNode{NodeType: NodeIf, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
+func (t *Tree) newIf(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *IfNode {
+ return &IfNode{BranchNode{tr: t, NodeType: NodeIf, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
}
func (i *IfNode) Copy() Node {
- return newIf(i.Pos, i.Line, i.Pipe.CopyPipe(), i.List.CopyList(), i.ElseList.CopyList())
+ return i.tr.newIf(i.Pos, i.Line, i.Pipe.CopyPipe(), i.List.CopyList(), i.ElseList.CopyList())
}
// RangeNode represents a {{range}} action and its commands.
@@ -676,12 +783,12 @@ type RangeNode struct {
BranchNode
}
-func newRange(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *RangeNode {
- return &RangeNode{BranchNode{NodeType: NodeRange, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
+func (t *Tree) newRange(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *RangeNode {
+ return &RangeNode{BranchNode{tr: t, NodeType: NodeRange, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
}
func (r *RangeNode) Copy() Node {
- return newRange(r.Pos, r.Line, r.Pipe.CopyPipe(), r.List.CopyList(), r.ElseList.CopyList())
+ return r.tr.newRange(r.Pos, r.Line, r.Pipe.CopyPipe(), r.List.CopyList(), r.ElseList.CopyList())
}
// WithNode represents a {{with}} action and its commands.
@@ -689,25 +796,26 @@ type WithNode struct {
BranchNode
}
-func newWith(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *WithNode {
- return &WithNode{BranchNode{NodeType: NodeWith, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
+func (t *Tree) newWith(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *WithNode {
+ return &WithNode{BranchNode{tr: t, NodeType: NodeWith, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
}
func (w *WithNode) Copy() Node {
- return newWith(w.Pos, w.Line, w.Pipe.CopyPipe(), w.List.CopyList(), w.ElseList.CopyList())
+ return w.tr.newWith(w.Pos, w.Line, w.Pipe.CopyPipe(), w.List.CopyList(), w.ElseList.CopyList())
}
// TemplateNode represents a {{template}} action.
type TemplateNode struct {
NodeType
Pos
+ tr *Tree
Line int // The line number in the input (deprecated; kept for compatibility)
Name string // The name of the template (unquoted).
Pipe *PipeNode // The command to evaluate as dot for the template.
}
-func newTemplate(pos Pos, line int, name string, pipe *PipeNode) *TemplateNode {
- return &TemplateNode{NodeType: NodeTemplate, Line: line, Pos: pos, Name: name, Pipe: pipe}
+func (t *Tree) newTemplate(pos Pos, line int, name string, pipe *PipeNode) *TemplateNode {
+ return &TemplateNode{tr: t, NodeType: NodeTemplate, Pos: pos, Line: line, Name: name, Pipe: pipe}
}
func (t *TemplateNode) String() string {
@@ -717,6 +825,10 @@ func (t *TemplateNode) String() string {
return fmt.Sprintf("{{template %q %s}}", t.Name, t.Pipe)
}
+func (t *TemplateNode) tree() *Tree {
+ return t.tr
+}
+
func (t *TemplateNode) Copy() Node {
- return newTemplate(t.Pos, t.Line, t.Name, t.Pipe.CopyPipe())
+ return t.tr.newTemplate(t.Pos, t.Line, t.Name, t.Pipe.CopyPipe())
}
diff --git a/src/pkg/text/template/parse/parse.go b/src/pkg/text/template/parse/parse.go
index 34112fb7b3..af33880c15 100644
--- a/src/pkg/text/template/parse/parse.go
+++ b/src/pkg/text/template/parse/parse.go
@@ -129,9 +129,15 @@ func New(name string, funcs ...map[string]interface{}) *Tree {
}
// ErrorContext returns a textual representation of the location of the node in the input text.
+// The receiver is only used when the node does not have a pointer to the tree inside,
+// which can occur in old code.
func (t *Tree) ErrorContext(n Node) (location, context string) {
pos := int(n.Position())
- text := t.text[:pos]
+ tree := n.tree()
+ if tree == nil {
+ tree = t
+ }
+ text := tree.text[:pos]
byteNum := strings.LastIndex(text, "\n")
if byteNum == -1 {
byteNum = pos // On first line.
@@ -144,7 +150,7 @@ func (t *Tree) ErrorContext(n Node) (location, context string) {
if len(context) > 20 {
context = fmt.Sprintf("%.20s...", context)
}
- return fmt.Sprintf("%s:%d:%d", t.ParseName, lineNum, byteNum), context
+ return fmt.Sprintf("%s:%d:%d", tree.ParseName, lineNum, byteNum), context
}
// errorf formats the error and terminates processing.
@@ -268,7 +274,7 @@ func IsEmptyTree(n Node) bool {
// as itemList except it also parses {{define}} actions.
// It runs to EOF.
func (t *Tree) parse(treeSet map[string]*Tree) (next Node) {
- t.Root = newList(t.peek().pos)
+ t.Root = t.newList(t.peek().pos)
for t.peek().typ != itemEOF {
if t.peek().typ == itemLeftDelim {
delim := t.next()
@@ -316,7 +322,7 @@ func (t *Tree) parseDefinition(treeSet map[string]*Tree) {
// textOrAction*
// Terminates at {{end}} or {{else}}, returned separately.
func (t *Tree) itemList() (list *ListNode, next Node) {
- list = newList(t.peekNonSpace().pos)
+ list = t.newList(t.peekNonSpace().pos)
for t.peekNonSpace().typ != itemEOF {
n := t.textOrAction()
switch n.Type() {
@@ -334,7 +340,7 @@ func (t *Tree) itemList() (list *ListNode, next Node) {
func (t *Tree) textOrAction() Node {
switch token := t.nextNonSpace(); token.typ {
case itemText:
- return newText(token.pos, token.val)
+ return t.newText(token.pos, token.val)
case itemLeftDelim:
return t.action()
default:
@@ -365,7 +371,7 @@ func (t *Tree) action() (n Node) {
}
t.backup()
// Do not pop variables; they persist until "end".
- return newAction(t.peek().pos, t.lex.lineNumber(), t.pipeline("command"))
+ return t.newAction(t.peek().pos, t.lex.lineNumber(), t.pipeline("command"))
}
// Pipeline:
@@ -384,7 +390,7 @@ func (t *Tree) pipeline(context string) (pipe *PipeNode) {
tokenAfterVariable := t.peek()
if next := t.peekNonSpace(); next.typ == itemColonEquals || (next.typ == itemChar && next.val == ",") {
t.nextNonSpace()
- variable := newVariable(v.pos, v.val)
+ variable := t.newVariable(v.pos, v.val)
decl = append(decl, variable)
t.vars = append(t.vars, v.val)
if next.typ == itemChar && next.val == "," {
@@ -401,7 +407,7 @@ func (t *Tree) pipeline(context string) (pipe *PipeNode) {
}
break
}
- pipe = newPipeline(pos, t.lex.lineNumber(), decl)
+ pipe = t.newPipeline(pos, t.lex.lineNumber(), decl)
for {
switch token := t.nextNonSpace(); token.typ {
case itemRightDelim, itemRightParen:
@@ -442,7 +448,7 @@ func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int
// TODO: Should we allow else-if in with and range?
if t.peek().typ == itemIf {
t.next() // Consume the "if" token.
- elseList = newList(next.Position())
+ elseList = t.newList(next.Position())
elseList.append(t.ifControl())
// Do not consume the next item - only one {{end}} required.
break
@@ -461,7 +467,7 @@ func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int
// {{if pipeline}} itemList {{else}} itemList {{end}}
// If keyword is past.
func (t *Tree) ifControl() Node {
- return newIf(t.parseControl(true, "if"))
+ return t.newIf(t.parseControl(true, "if"))
}
// Range:
@@ -469,7 +475,7 @@ func (t *Tree) ifControl() Node {
// {{range pipeline}} itemList {{else}} itemList {{end}}
// Range keyword is past.
func (t *Tree) rangeControl() Node {
- return newRange(t.parseControl(false, "range"))
+ return t.newRange(t.parseControl(false, "range"))
}
// With:
@@ -477,14 +483,14 @@ func (t *Tree) rangeControl() Node {
// {{with pipeline}} itemList {{else}} itemList {{end}}
// If keyword is past.
func (t *Tree) withControl() Node {
- return newWith(t.parseControl(false, "with"))
+ return t.newWith(t.parseControl(false, "with"))
}
// End:
// {{end}}
// End keyword is past.
func (t *Tree) endControl() Node {
- return newEnd(t.expect(itemRightDelim, "end").pos)
+ return t.newEnd(t.expect(itemRightDelim, "end").pos)
}
// Else:
@@ -495,9 +501,9 @@ func (t *Tree) elseControl() Node {
peek := t.peekNonSpace()
if peek.typ == itemIf {
// We see "{{else if ... " but in effect rewrite it to {{else}}{{if ... ".
- return newElse(peek.pos, t.lex.lineNumber())
+ return t.newElse(peek.pos, t.lex.lineNumber())
}
- return newElse(t.expect(itemRightDelim, "else").pos, t.lex.lineNumber())
+ return t.newElse(t.expect(itemRightDelim, "else").pos, t.lex.lineNumber())
}
// Template:
@@ -523,7 +529,7 @@ func (t *Tree) templateControl() Node {
// Do not pop variables; they persist until "end".
pipe = t.pipeline("template")
}
- return newTemplate(token.pos, t.lex.lineNumber(), name, pipe)
+ return t.newTemplate(token.pos, t.lex.lineNumber(), name, pipe)
}
// command:
@@ -531,7 +537,7 @@ func (t *Tree) templateControl() Node {
// space-separated arguments up to a pipeline character or right delimiter.
// we consume the pipe character but leave the right delim to terminate the action.
func (t *Tree) command() *CommandNode {
- cmd := newCommand(t.peekNonSpace().pos)
+ cmd := t.newCommand(t.peekNonSpace().pos)
for {
t.peekNonSpace() // skip leading spaces.
operand := t.operand()
@@ -568,7 +574,7 @@ func (t *Tree) operand() Node {
return nil
}
if t.peek().typ == itemField {
- chain := newChain(t.peek().pos, node)
+ chain := t.newChain(t.peek().pos, node)
for t.peek().typ == itemField {
chain.Add(t.next().val)
}
@@ -578,9 +584,9 @@ func (t *Tree) operand() Node {
// TODO: Switch to Chains always when we can.
switch node.Type() {
case NodeField:
- node = newField(chain.Position(), chain.String())
+ node = t.newField(chain.Position(), chain.String())
case NodeVariable:
- node = newVariable(chain.Position(), chain.String())
+ node = t.newVariable(chain.Position(), chain.String())
default:
node = chain
}
@@ -605,19 +611,19 @@ func (t *Tree) term() Node {
if !t.hasFunction(token.val) {
t.errorf("function %q not defined", token.val)
}
- return NewIdentifier(token.val).SetPos(token.pos)
+ return NewIdentifier(token.val).SetTree(t).SetPos(token.pos)
case itemDot:
- return newDot(token.pos)
+ return t.newDot(token.pos)
case itemNil:
- return newNil(token.pos)
+ return t.newNil(token.pos)
case itemVariable:
return t.useVar(token.pos, token.val)
case itemField:
- return newField(token.pos, token.val)
+ return t.newField(token.pos, token.val)
case itemBool:
- return newBool(token.pos, token.val == "true")
+ return t.newBool(token.pos, token.val == "true")
case itemCharConstant, itemComplex, itemNumber:
- number, err := newNumber(token.pos, token.val, token.typ)
+ number, err := t.newNumber(token.pos, token.val, token.typ)
if err != nil {
t.error(err)
}
@@ -633,7 +639,7 @@ func (t *Tree) term() Node {
if err != nil {
t.error(err)
}
- return newString(token.pos, token.val, s)
+ return t.newString(token.pos, token.val, s)
}
t.backup()
return nil
@@ -660,7 +666,7 @@ func (t *Tree) popVars(n int) {
// useVar returns a node for a variable reference. It errors if the
// variable is not defined.
func (t *Tree) useVar(pos Pos, name string) Node {
- v := newVariable(pos, name)
+ v := t.newVariable(pos, name)
for _, varName := range t.vars {
if varName == v.Ident[0] {
return v
diff --git a/src/pkg/text/template/parse/parse_test.go b/src/pkg/text/template/parse/parse_test.go
index ba1a18ec54..4a504fa7c8 100644
--- a/src/pkg/text/template/parse/parse_test.go
+++ b/src/pkg/text/template/parse/parse_test.go
@@ -69,6 +69,8 @@ var numberTests = []numberTest{
{text: "1+2."},
{text: "'x"},
{text: "'xx'"},
+ // Issue 8622 - 0xe parsed as floating point. Very embarrassing.
+ {"0xef", true, true, true, false, 0xef, 0xef, 0xef, 0},
}
func TestNumberParse(t *testing.T) {
@@ -77,6 +79,7 @@ func TestNumberParse(t *testing.T) {
// because imaginary comes out as a number.
var c complex128
typ := itemNumber
+ var tree *Tree
if test.text[0] == '\'' {
typ = itemCharConstant
} else {
@@ -85,7 +88,7 @@ func TestNumberParse(t *testing.T) {
typ = itemComplex
}
}
- n, err := newNumber(0, test.text, typ)
+ n, err := tree.newNumber(0, test.text, typ)
ok := test.isInt || test.isUint || test.isFloat || test.isComplex
if ok && err != nil {
t.Errorf("unexpected error for %q: %s", test.text, err)
diff --git a/src/pkg/time/Makefile b/src/pkg/time/Makefile
deleted file mode 100644
index cba58e4e03..0000000000
--- a/src/pkg/time/Makefile
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright 2013 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-genzabbrs: genzabbrs.go
- go build genzabbrs.go
-
-windows: genzabbrs
- ./genzabbrs | gofmt >zoneinfo_abbrs_windows.go
diff --git a/src/pkg/time/format.go b/src/pkg/time/format.go
index 14b1250cb0..04e79f32dc 100644
--- a/src/pkg/time/format.go
+++ b/src/pkg/time/format.go
@@ -556,7 +556,7 @@ func (t Time) Format(layout string) string {
b = append(b, '+')
}
b = appendUint(b, uint(zone/60), '0')
- if std == stdISO8601ColonTZ || std == stdNumColonTZ {
+ if std == stdISO8601ColonTZ || std == stdNumColonTZ || std == stdISO8601ColonSecondsTZ || std == stdNumColonSecondsTZ {
b = append(b, ':')
}
b = appendUint(b, uint(zone%60), '0')
diff --git a/src/pkg/time/format_test.go b/src/pkg/time/format_test.go
index 3bc8f42946..ecc5c8f28a 100644
--- a/src/pkg/time/format_test.go
+++ b/src/pkg/time/format_test.go
@@ -183,39 +183,45 @@ func TestParse(t *testing.T) {
}
}
-func TestParseInSydney(t *testing.T) {
- loc, err := LoadLocation("Australia/Sydney")
+func TestParseInLocation(t *testing.T) {
+ // Check that Parse (and ParseInLocation) understand that
+ // Feb 01 AST (Arabia Standard Time) and Feb 01 AST (Atlantic Standard Time)
+ // are in different time zones even though both are called AST
+
+ baghdad, err := LoadLocation("Asia/Baghdad")
if err != nil {
t.Fatal(err)
}
- // Check that Parse (and ParseInLocation) understand
- // that Feb EST and Aug EST are different time zones in Sydney
- // even though both are called EST.
- t1, err := ParseInLocation("Jan 02 2006 MST", "Feb 01 2013 EST", loc)
+ t1, err := ParseInLocation("Jan 02 2006 MST", "Feb 01 2013 AST", baghdad)
if err != nil {
t.Fatal(err)
}
- t2 := Date(2013, February, 1, 00, 00, 00, 0, loc)
+ t2 := Date(2013, February, 1, 00, 00, 00, 0, baghdad)
if t1 != t2 {
- t.Fatalf("ParseInLocation(Feb 01 2013 EST, Sydney) = %v, want %v", t1, t2)
+ t.Fatalf("ParseInLocation(Feb 01 2013 AST, Baghdad) = %v, want %v", t1, t2)
}
_, offset := t1.Zone()
- if offset != 11*60*60 {
- t.Fatalf("ParseInLocation(Feb 01 2013 EST, Sydney).Zone = _, %d, want _, %d", offset, 11*60*60)
+ if offset != 3*60*60 {
+ t.Fatalf("ParseInLocation(Feb 01 2013 AST, Baghdad).Zone = _, %d, want _, %d", offset, 3*60*60)
}
- t1, err = ParseInLocation("Jan 02 2006 MST", "Aug 01 2013 EST", loc)
+ blancSablon, err := LoadLocation("America/Blanc-Sablon")
if err != nil {
t.Fatal(err)
}
- t2 = Date(2013, August, 1, 00, 00, 00, 0, loc)
+
+ t1, err = ParseInLocation("Jan 02 2006 MST", "Feb 01 2013 AST", blancSablon)
+ if err != nil {
+ t.Fatal(err)
+ }
+ t2 = Date(2013, February, 1, 00, 00, 00, 0, blancSablon)
if t1 != t2 {
- t.Fatalf("ParseInLocation(Aug 01 2013 EST, Sydney) = %v, want %v", t1, t2)
+ t.Fatalf("ParseInLocation(Feb 01 2013 AST, Blanc-Sablon) = %v, want %v", t1, t2)
}
_, offset = t1.Zone()
- if offset != 10*60*60 {
- t.Fatalf("ParseInLocation(Aug 01 2013 EST, Sydney).Zone = _, %d, want _, %d", offset, 10*60*60)
+ if offset != -4*60*60 {
+ t.Fatalf("ParseInLocation(Feb 01 2013 AST, Blanc-Sablon).Zone = _, %d, want _, %d", offset, -4*60*60)
}
}
@@ -502,10 +508,11 @@ func TestParseSecondsInTimeZone(t *testing.T) {
}
func TestFormatSecondsInTimeZone(t *testing.T) {
- d := Date(1871, 9, 17, 20, 4, 26, 0, FixedZone("LMT", -(34*60+8)))
- timestr := d.Format("2006-01-02T15:04:05Z070000")
- expected := "1871-09-17T20:04:26-003408"
- if timestr != expected {
- t.Errorf("Got %s, want %s", timestr, expected)
+ for _, test := range secondsTimeZoneOffsetTests {
+ d := Date(1871, 1, 1, 5, 33, 2, 0, FixedZone("LMT", test.expectedoffset))
+ timestr := d.Format(test.format)
+ if timestr != test.value {
+ t.Errorf("Format = %s, want %s", timestr, test.value)
+ }
}
}
diff --git a/src/pkg/time/genzabbrs.go b/src/pkg/time/genzabbrs.go
index 7c637cb43a..9eb0728a42 100644
--- a/src/pkg/time/genzabbrs.go
+++ b/src/pkg/time/genzabbrs.go
@@ -7,22 +7,26 @@
//
// usage:
//
-// go run genzabbrs.go | gofmt > $GOROOT/src/pkg/time/zoneinfo_abbrs_windows.go
+// go run genzabbrs.go -output zoneinfo_abbrs_windows.go
//
package main
import (
+ "bytes"
"encoding/xml"
+ "flag"
+ "go/format"
"io/ioutil"
"log"
"net/http"
- "os"
"sort"
"text/template"
"time"
)
+var filename = flag.String("output", "zoneinfo_abbrs_windows.go", "output file name")
+
// getAbbrs finds timezone abbreviations (standard and daylight saving time)
// for location l.
func getAbbrs(l *time.Location) (st, dt string) {
@@ -105,6 +109,7 @@ func readWindowsZones() (zones, error) {
}
func main() {
+ flag.Parse()
zs, err := readWindowsZones()
if err != nil {
log.Fatal(err)
@@ -117,7 +122,16 @@ func main() {
wzURL,
zs,
}
- err = template.Must(template.New("prog").Parse(prog)).Execute(os.Stdout, v)
+ var buf bytes.Buffer
+ err = template.Must(template.New("prog").Parse(prog)).Execute(&buf, v)
+ if err != nil {
+ log.Fatal(err)
+ }
+ data, err := format.Source(buf.Bytes())
+ if err != nil {
+ log.Fatal(err)
+ }
+ err = ioutil.WriteFile(*filename, data, 0644)
if err != nil {
log.Fatal(err)
}
diff --git a/src/pkg/time/internal_test.go b/src/pkg/time/internal_test.go
index f09d30507f..edd523bc80 100644
--- a/src/pkg/time/internal_test.go
+++ b/src/pkg/time/internal_test.go
@@ -12,7 +12,7 @@ func init() {
var Interrupt = interrupt
var DaysIn = daysIn
-func empty(now int64, arg interface{}) {}
+func empty(arg interface{}, seq uintptr) {}
// Test that a runtimeTimer with a duration so large it overflows
// does not cause other timers to hang.
diff --git a/src/pkg/time/sleep.go b/src/pkg/time/sleep.go
index 6a03f417bd..33c349de46 100644
--- a/src/pkg/time/sleep.go
+++ b/src/pkg/time/sleep.go
@@ -14,11 +14,12 @@ func runtimeNano() int64
// Interface to timers implemented in package runtime.
// Must be in sync with ../runtime/runtime.h:/^struct.Timer$
type runtimeTimer struct {
- i int32
+ i int
when int64
period int64
- f func(int64, interface{}) // NOTE: must not be closure
+ f func(interface{}, uintptr) // NOTE: must not be closure
arg interface{}
+ seq uintptr
}
// when is a helper function for setting the 'when' field of a runtimeTimer.
@@ -83,7 +84,7 @@ func (t *Timer) Reset(d Duration) bool {
return active
}
-func sendTime(now int64, c interface{}) {
+func sendTime(c interface{}, seq uintptr) {
// Non-blocking send of time on c.
// Used in NewTimer, it cannot block anyway (buffer).
// Used in NewTicker, dropping sends on the floor is
@@ -117,6 +118,6 @@ func AfterFunc(d Duration, f func()) *Timer {
return t
}
-func goFunc(now int64, arg interface{}) {
+func goFunc(arg interface{}, seq uintptr) {
go arg.(func())()
}
diff --git a/src/pkg/time/zoneinfo_abbrs_windows.go b/src/pkg/time/zoneinfo_abbrs_windows.go
index 80334371fe..51a1a2f66d 100644
--- a/src/pkg/time/zoneinfo_abbrs_windows.go
+++ b/src/pkg/time/zoneinfo_abbrs_windows.go
@@ -18,6 +18,7 @@ var abbrs = map[string]abbr{
"South Africa Standard Time": {"SAST", "SAST"}, // Africa/Johannesburg
"W. Central Africa Standard Time": {"WAT", "WAT"}, // Africa/Lagos
"E. Africa Standard Time": {"EAT", "EAT"}, // Africa/Nairobi
+ "Libya Standard Time": {"EET", "EET"}, // Africa/Tripoli
"Namibia Standard Time": {"WAT", "WAST"}, // Africa/Windhoek
"Alaskan Standard Time": {"AKST", "AKDT"}, // America/Anchorage
"Paraguay Standard Time": {"PYT", "PYST"}, // America/Asuncion
@@ -63,7 +64,6 @@ var abbrs = map[string]abbr{
"Nepal Standard Time": {"NPT", "NPT"}, // Asia/Katmandu
"North Asia Standard Time": {"KRAT", "KRAT"}, // Asia/Krasnoyarsk
"Magadan Standard Time": {"MAGT", "MAGT"}, // Asia/Magadan
- "E. Europe Standard Time": {"EET", "EEST"}, // Asia/Nicosia
"N. Central Asia Standard Time": {"NOVT", "NOVT"}, // Asia/Novosibirsk
"Myanmar Standard Time": {"MMT", "MMT"}, // Asia/Rangoon
"Arab Standard Time": {"AST", "AST"}, // Asia/Riyadh
@@ -110,6 +110,7 @@ var abbrs = map[string]abbr{
"Fiji Standard Time": {"FJT", "FJT"}, // Pacific/Fiji
"Central Pacific Standard Time": {"SBT", "SBT"}, // Pacific/Guadalcanal
"Hawaiian Standard Time": {"HST", "HST"}, // Pacific/Honolulu
+ "Line Islands Standard Time": {"LINT", "LINT"}, // Pacific/Kiritimati
"West Pacific Standard Time": {"PGT", "PGT"}, // Pacific/Port_Moresby
"Tonga Standard Time": {"TOT", "TOT"}, // Pacific/Tongatapu
}
diff --git a/src/pkg/time/zoneinfo_windows.go b/src/pkg/time/zoneinfo_windows.go
index 6046743e67..02d8e0edcc 100644
--- a/src/pkg/time/zoneinfo_windows.go
+++ b/src/pkg/time/zoneinfo_windows.go
@@ -11,6 +11,8 @@ import (
"unsafe"
)
+//go:generate go run genzabbrs.go -output zoneinfo_abbrs_windows.go
+
// TODO(rsc): Fall back to copy of zoneinfo files.
// BUG(brainman,rsc): On Windows, the operating system does not provide complete
diff --git a/src/pkg/unicode/Makefile b/src/pkg/unicode/Makefile
deleted file mode 100644
index 33b06ca10d..0000000000
--- a/src/pkg/unicode/Makefile
+++ /dev/null
@@ -1,16 +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.
-
-maketables: maketables.go
- go build maketables.go
-
-tables: maketables
- ./maketables --tables=all > tables.go
- gofmt -w tables.go
-
-# Downloads from www.unicode.org, so not part
-# of standard test scripts.
-testtables: maketables
- @echo '***' Be sure to make tables and go install first
- ./maketables -test
diff --git a/src/pkg/unicode/letter.go b/src/pkg/unicode/letter.go
index 977bd2b3b0..7fe4241edd 100644
--- a/src/pkg/unicode/letter.go
+++ b/src/pkg/unicode/letter.go
@@ -6,6 +6,9 @@
// Unicode code points.
package unicode
+// Tables are regenerated each time we update the Unicode version.
+//go:generate go run maketables.go -tables=all -output tables.go
+
const (
MaxRune = '\U0010FFFF' // Maximum valid Unicode code point.
ReplacementChar = '\uFFFD' // Represents invalid code points.
diff --git a/src/pkg/unicode/maketables.go b/src/pkg/unicode/maketables.go
index 8116ab8a41..d1c9aa04ae 100644
--- a/src/pkg/unicode/maketables.go
+++ b/src/pkg/unicode/maketables.go
@@ -13,9 +13,11 @@ import (
"bufio"
"flag"
"fmt"
+ "io"
"log"
"net/http"
"os"
+ "os/exec"
"path/filepath"
"regexp"
"sort"
@@ -26,6 +28,7 @@ import (
func main() {
flag.Parse()
+ setupOutput()
loadChars() // always needed
loadCasefold()
printCategories()
@@ -35,12 +38,13 @@ func main() {
printLatinProperties()
printCasefold()
printSizes()
+ flushOutput()
}
var dataURL = flag.String("data", "", "full URL for UnicodeData.txt; defaults to --url/UnicodeData.txt")
var casefoldingURL = flag.String("casefolding", "", "full URL for CaseFolding.txt; defaults to --url/CaseFolding.txt")
var url = flag.String("url",
- "http://www.unicode.org/Public/6.3.0/ucd/",
+ "http://www.unicode.org/Public/7.0.0/ucd/",
"URL of Unicode database directory")
var tablelist = flag.String("tables",
"all",
@@ -60,10 +64,62 @@ var test = flag.Bool("test",
var localFiles = flag.Bool("local",
false,
"data files have been copied to current directory; for debugging only")
+var outputFile = flag.String("output",
+ "",
+ "output file for generated tables; default stdout")
var scriptRe = regexp.MustCompile(`^([0-9A-F]+)(\.\.[0-9A-F]+)? *; ([A-Za-z_]+)$`)
var logger = log.New(os.Stderr, "", log.Lshortfile)
+var output *bufio.Writer // points to os.Stdout or to "gofmt > outputFile"
+
+func setupOutput() {
+ output = bufio.NewWriter(startGofmt())
+}
+
+// startGofmt connects output to a gofmt process if -output is set.
+func startGofmt() io.Writer {
+ if *outputFile == "" {
+ return os.Stdout
+ }
+ stdout, err := os.Create(*outputFile)
+ if err != nil {
+ logger.Fatal(err)
+ }
+ // Pipe output to gofmt.
+ gofmt := exec.Command("gofmt")
+ fd, err := gofmt.StdinPipe()
+ if err != nil {
+ logger.Fatal(err)
+ }
+ gofmt.Stdout = stdout
+ gofmt.Stderr = os.Stderr
+ err = gofmt.Start()
+ if err != nil {
+ logger.Fatal(err)
+ }
+ return fd
+}
+
+func flushOutput() {
+ err := output.Flush()
+ if err != nil {
+ logger.Fatal(err)
+ }
+}
+
+func printf(format string, args ...interface{}) {
+ fmt.Fprintf(output, format, args...)
+}
+
+func print(args ...interface{}) {
+ fmt.Fprint(output, args...)
+}
+
+func println(args ...interface{}) {
+ fmt.Fprintln(output, args...)
+}
+
type reader struct {
*bufio.Reader
fd *os.File
@@ -245,11 +301,11 @@ func parseCategory(line string) (state State) {
}
func (char *Char) dump(s string) {
- fmt.Print(s, " ")
+ print(s, " ")
for i := 0; i < len(char.field); i++ {
- fmt.Printf("%s:%q ", fieldName[i], char.field[i])
+ printf("%s:%q ", fieldName[i], char.field[i])
}
- fmt.Print("\n")
+ print("\n")
}
func (char *Char) letter(u, l, t string) {
@@ -411,18 +467,18 @@ func printCategories() {
fullCategoryTest(list)
return
}
- fmt.Printf(progHeader, *tablelist, *dataURL, *casefoldingURL)
+ printf(progHeader, *tablelist, *dataURL, *casefoldingURL)
- fmt.Println("// Version is the Unicode edition from which the tables are derived.")
- fmt.Printf("const Version = %q\n\n", version())
+ println("// Version is the Unicode edition from which the tables are derived.")
+ printf("const Version = %q\n\n", version())
if *tablelist == "all" {
- fmt.Println("// Categories is the set of Unicode category tables.")
- fmt.Println("var Categories = map[string] *RangeTable {")
+ println("// Categories is the set of Unicode category tables.")
+ println("var Categories = map[string] *RangeTable {")
for _, k := range allCategories() {
- fmt.Printf("\t%q: %s,\n", k, k)
+ printf("\t%q: %s,\n", k, k)
}
- fmt.Print("}\n\n")
+ print("}\n\n")
}
decl := make(sort.StringSlice, len(list))
@@ -486,12 +542,12 @@ func printCategories() {
func(code rune) bool { return chars[code].category == name })
}
decl.Sort()
- fmt.Println("// These variables have type *RangeTable.")
- fmt.Println("var (")
+ println("// These variables have type *RangeTable.")
+ println("var (")
for _, d := range decl {
- fmt.Print(d)
+ print(d)
}
- fmt.Print(")\n\n")
+ print(")\n\n")
}
type Op func(code rune) bool
@@ -499,10 +555,10 @@ type Op func(code rune) bool
const format = "\t\t{0x%04x, 0x%04x, %d},\n"
func dumpRange(header string, inCategory Op) {
- fmt.Print(header)
+ print(header)
next := rune(0)
latinOffset := 0
- fmt.Print("\tR16: []Range16{\n")
+ print("\tR16: []Range16{\n")
// one Range for each iteration
count := &range16Count
size := 16
@@ -528,7 +584,7 @@ func dumpRange(header string, inCategory Op) {
}
if next >= rune(len(chars)) {
// no more characters
- fmt.Printf(format, lo, hi, stride)
+ printf(format, lo, hi, stride)
break
}
// set stride
@@ -552,11 +608,11 @@ func dumpRange(header string, inCategory Op) {
// next range: start looking where this range ends
next = hi + 1
}
- fmt.Print("\t},\n")
+ print("\t},\n")
if latinOffset > 0 {
- fmt.Printf("\tLatinOffset: %d,\n", latinOffset)
+ printf("\tLatinOffset: %d,\n", latinOffset)
}
- fmt.Print("}\n\n")
+ print("}\n\n")
}
func printRange(lo, hi, stride uint32, size int, count *int) (int, *int) {
@@ -568,17 +624,17 @@ func printRange(lo, hi, stride uint32, size int, count *int) (int, *int) {
// No range contains U+FFFF as an instance, so split
// the range into two entries. That way we can maintain
// the invariant that R32 contains only >= 1<<16.
- fmt.Printf(format, lo, lo, 1)
+ printf(format, lo, lo, 1)
lo = hi
stride = 1
*count++
}
- fmt.Print("\t},\n")
- fmt.Print("\tR32: []Range32{\n")
+ print("\t},\n")
+ print("\tR32: []Range32{\n")
size = 32
count = &range32Count
}
- fmt.Printf(format, lo, hi, stride)
+ printf(format, lo, hi, stride)
*count++
return size, count
}
@@ -727,7 +783,7 @@ func printScriptOrProperty(doProps bool) {
return
}
- fmt.Printf(
+ printf(
"// Generated by running\n"+
"// maketables --%s=%s --url=%s\n"+
"// DO NOT EDIT\n\n",
@@ -736,16 +792,16 @@ func printScriptOrProperty(doProps bool) {
*url)
if flaglist == "all" {
if doProps {
- fmt.Println("// Properties is the set of Unicode property tables.")
- fmt.Println("var Properties = map[string] *RangeTable{")
+ println("// Properties is the set of Unicode property tables.")
+ println("var Properties = map[string] *RangeTable{")
} else {
- fmt.Println("// Scripts is the set of Unicode script tables.")
- fmt.Println("var Scripts = map[string] *RangeTable{")
+ println("// Scripts is the set of Unicode script tables.")
+ println("var Scripts = map[string] *RangeTable{")
}
for _, k := range all(table) {
- fmt.Printf("\t%q: %s,\n", k, k)
+ printf("\t%q: %s,\n", k, k)
}
- fmt.Print("}\n\n")
+ print("}\n\n")
}
decl := make(sort.StringSlice, len(list))
@@ -761,27 +817,27 @@ func printScriptOrProperty(doProps bool) {
name, name, name, name)
}
ndecl++
- fmt.Printf("var _%s = &RangeTable {\n", name)
+ printf("var _%s = &RangeTable {\n", name)
ranges := foldAdjacent(table[name])
- fmt.Print("\tR16: []Range16{\n")
+ print("\tR16: []Range16{\n")
size := 16
count := &range16Count
for _, s := range ranges {
size, count = printRange(s.Lo, s.Hi, s.Stride, size, count)
}
- fmt.Print("\t},\n")
+ print("\t},\n")
if off := findLatinOffset(ranges); off > 0 {
- fmt.Printf("\tLatinOffset: %d,\n", off)
+ printf("\tLatinOffset: %d,\n", off)
}
- fmt.Print("}\n\n")
+ print("}\n\n")
}
decl.Sort()
- fmt.Println("// These variables have type *RangeTable.")
- fmt.Println("var (")
+ println("// These variables have type *RangeTable.")
+ println("var (")
for _, d := range decl {
- fmt.Print(d)
+ print(d)
}
- fmt.Print(")\n\n")
+ print(")\n\n")
}
func findLatinOffset(ranges []unicode.Range32) int {
@@ -940,7 +996,7 @@ func printCases() {
fullCaseTest()
return
}
- fmt.Printf(
+ printf(
"// Generated by running\n"+
"// maketables --data=%s --casefolding=%s\n"+
"// DO NOT EDIT\n\n"+
@@ -966,7 +1022,7 @@ func printCases() {
}
prevState = state
}
- fmt.Print("}\n")
+ print("}\n")
}
func printCaseRange(lo, hi *caseState) {
@@ -979,14 +1035,14 @@ func printCaseRange(lo, hi *caseState) {
}
switch {
case hi.point > lo.point && lo.isUpperLower():
- fmt.Printf("\t{0x%04X, 0x%04X, d{UpperLower, UpperLower, UpperLower}},\n",
+ printf("\t{0x%04X, 0x%04X, d{UpperLower, UpperLower, UpperLower}},\n",
lo.point, hi.point)
case hi.point > lo.point && lo.isLowerUpper():
logger.Fatalf("LowerUpper sequence: should not happen: %U. If it's real, need to fix To()", lo.point)
- fmt.Printf("\t{0x%04X, 0x%04X, d{LowerUpper, LowerUpper, LowerUpper}},\n",
+ printf("\t{0x%04X, 0x%04X, d{LowerUpper, LowerUpper, LowerUpper}},\n",
lo.point, hi.point)
default:
- fmt.Printf("\t{0x%04X, 0x%04X, d{%d, %d, %d}},\n",
+ printf("\t{0x%04X, 0x%04X, d{%d, %d, %d}},\n",
lo.point, hi.point,
lo.deltaToUpper, lo.deltaToLower, lo.deltaToTitle)
}
@@ -1025,7 +1081,7 @@ func printLatinProperties() {
if *test {
return
}
- fmt.Println("var properties = [MaxLatin1+1]uint8{")
+ println("var properties = [MaxLatin1+1]uint8{")
for code := 0; code <= unicode.MaxLatin1; code++ {
var property string
switch chars[code].category {
@@ -1054,9 +1110,9 @@ func printLatinProperties() {
if code == ' ' {
property = "pZ | pp"
}
- fmt.Printf("\t0x%02X: %s, // %q\n", code, property, code)
+ printf("\t0x%02X: %s, // %q\n", code, property, code)
}
- fmt.Printf("}\n\n")
+ printf("}\n\n")
}
type runeSlice []rune
@@ -1235,15 +1291,15 @@ func printCaseOrbit() {
return
}
- fmt.Printf("var caseOrbit = []foldPair{\n")
+ printf("var caseOrbit = []foldPair{\n")
for i := range chars {
c := &chars[i]
if c.caseOrbit != 0 {
- fmt.Printf("\t{0x%04X, 0x%04X},\n", i, c.caseOrbit)
+ printf("\t{0x%04X, 0x%04X},\n", i, c.caseOrbit)
foldPairCount++
}
}
- fmt.Printf("}\n\n")
+ printf("}\n\n")
}
func printCatFold(name string, m map[string]map[rune]bool) {
@@ -1288,12 +1344,12 @@ func printCatFold(name string, m map[string]map[rune]bool) {
return
}
- fmt.Print(comment[name])
- fmt.Printf("var %s = map[string]*RangeTable{\n", name)
+ print(comment[name])
+ printf("var %s = map[string]*RangeTable{\n", name)
for _, name := range allCatFold(m) {
- fmt.Printf("\t%q: fold%s,\n", name, name)
+ printf("\t%q: fold%s,\n", name, name)
}
- fmt.Printf("}\n\n")
+ printf("}\n\n")
for _, name := range allCatFold(m) {
class := m[name]
dumpRange(
@@ -1310,11 +1366,11 @@ func printSizes() {
if *test {
return
}
- fmt.Println()
- fmt.Printf("// Range entries: %d 16-bit, %d 32-bit, %d total.\n", range16Count, range32Count, range16Count+range32Count)
+ println()
+ printf("// Range entries: %d 16-bit, %d 32-bit, %d total.\n", range16Count, range32Count, range16Count+range32Count)
range16Bytes := range16Count * 3 * 2
range32Bytes := range32Count * 3 * 4
- fmt.Printf("// Range bytes: %d 16-bit, %d 32-bit, %d total.\n", range16Bytes, range32Bytes, range16Bytes+range32Bytes)
- fmt.Println()
- fmt.Printf("// Fold orbit bytes: %d pairs, %d bytes\n", foldPairCount, foldPairCount*2*2)
+ printf("// Range bytes: %d 16-bit, %d 32-bit, %d total.\n", range16Bytes, range32Bytes, range16Bytes+range32Bytes)
+ println()
+ printf("// Fold orbit bytes: %d pairs, %d bytes\n", foldPairCount, foldPairCount*2*2)
}
diff --git a/src/pkg/unicode/script_test.go b/src/pkg/unicode/script_test.go
index e2ba0011ac..795cb4e171 100644
--- a/src/pkg/unicode/script_test.go
+++ b/src/pkg/unicode/script_test.go
@@ -14,14 +14,15 @@ type T struct {
script string
}
-// Hand-chosen tests from Unicode 5.1.0, 6.0.0 and 6.2.0 mostly to discover when new
-// scripts and categories arise.
+// Hand-chosen tests from Unicode 5.1.0, 6.0.0, 6.2.0, 6.3.0 and 7.0.0 mostly to
+// discover when new scripts and categories arise.
var inTest = []T{
{0x06e2, "Arabic"},
{0x0567, "Armenian"},
{0x10b20, "Avestan"},
{0x1b37, "Balinese"},
{0xa6af, "Bamum"},
+ {0x16ada, "Bassa_Vah"},
{0x1be1, "Batak"},
{0x09c2, "Bengali"},
{0x3115, "Bopomofo"},
@@ -31,6 +32,7 @@ var inTest = []T{
{0x11011, "Brahmi"},
{0x156d, "Canadian_Aboriginal"},
{0x102a9, "Carian"},
+ {0x10563, "Caucasian_Albanian"},
{0x11111, "Chakma"},
{0xaa4d, "Cham"},
{0x13c2, "Cherokee"},
@@ -42,11 +44,14 @@ var inTest = []T{
{0xa663, "Cyrillic"},
{0x10430, "Deseret"},
{0x094a, "Devanagari"},
+ {0x1BC00, "Duployan"},
{0x13001, "Egyptian_Hieroglyphs"},
+ {0x10500, "Elbasan"},
{0x1271, "Ethiopic"},
{0x10fc, "Georgian"},
{0x2c40, "Glagolitic"},
{0x10347, "Gothic"},
+ {0x11303, "Grantha"},
{0x03ae, "Greek"},
{0x0abf, "Gujarati"},
{0x0a24, "Gurmukhi"},
@@ -66,40 +71,56 @@ var inTest = []T{
{0xa928, "Kayah_Li"},
{0x10a11, "Kharoshthi"},
{0x17c6, "Khmer"},
+ {0x11211, "Khojki"},
+ {0x112df, "Khudawadi"},
{0x0eaa, "Lao"},
{0x1d79, "Latin"},
{0x1c10, "Lepcha"},
{0x1930, "Limbu"},
+ {0x10755, "Linear_A"},
{0x1003c, "Linear_B"},
{0xa4e1, "Lisu"},
{0x10290, "Lycian"},
{0x10930, "Lydian"},
+ {0x11173, "Mahajani"},
{0x0d42, "Malayalam"},
{0x0843, "Mandaic"},
+ {0x10ac8, "Manichaean"},
{0xabd0, "Meetei_Mayek"},
+ {0x1e800, "Mende_Kikakui"},
{0x1099f, "Meroitic_Hieroglyphs"},
{0x109a0, "Meroitic_Cursive"},
{0x16f00, "Miao"},
+ {0x11611, "Modi"},
{0x1822, "Mongolian"},
+ {0x16a60, "Mro"},
{0x104c, "Myanmar"},
+ {0x10880, "Nabataean"},
{0x19c3, "New_Tai_Lue"},
{0x07f8, "Nko"},
{0x169b, "Ogham"},
{0x1c6a, "Ol_Chiki"},
{0x10310, "Old_Italic"},
+ {0x10a80, "Old_North_Arabian"},
+ {0x10350, "Old_Permic"},
{0x103c9, "Old_Persian"},
{0x10a6f, "Old_South_Arabian"},
{0x10c20, "Old_Turkic"},
{0x0b3e, "Oriya"},
{0x10491, "Osmanya"},
+ {0x16b2b, "Pahawh_Hmong"},
+ {0x10876, "Palmyrene"},
+ {0x11ACE, "Pau_Cin_Hau"},
{0xa860, "Phags_Pa"},
{0x10918, "Phoenician"},
+ {0x10baf, "Psalter_Pahlavi"},
{0xa949, "Rejang"},
{0x16c0, "Runic"},
{0x081d, "Samaritan"},
{0xa892, "Saurashtra"},
{0x111a0, "Sharada"},
{0x10463, "Shavian"},
+ {0x115c1, "Siddham"},
{0x0dbd, "Sinhala"},
{0x110d0, "Sora_Sompeng"},
{0x1ba3, "Sundanese"},
@@ -117,8 +138,10 @@ var inTest = []T{
{0x0e46, "Thai"},
{0x0f36, "Tibetan"},
{0x2d55, "Tifinagh"},
+ {0x114d9, "Tirhuta"},
{0x10388, "Ugaritic"},
{0xa60e, "Vai"},
+ {0x118ff, "Warang_Citi"},
{0xa216, "Yi"},
}
diff --git a/src/pkg/unicode/tables.go b/src/pkg/unicode/tables.go
index 5670d1c5b1..8b77dd6036 100644
--- a/src/pkg/unicode/tables.go
+++ b/src/pkg/unicode/tables.go
@@ -3,13 +3,13 @@
// license that can be found in the LICENSE file.
// Generated by running
-// maketables --tables=all --data=http://www.unicode.org/Public/6.3.0/ucd/UnicodeData.txt --casefolding=http://www.unicode.org/Public/6.3.0/ucd/CaseFolding.txt
+// maketables --tables=all --data=http://www.unicode.org/Public/7.0.0/ucd/UnicodeData.txt --casefolding=http://www.unicode.org/Public/7.0.0/ucd/CaseFolding.txt
// DO NOT EDIT
package unicode
// Version is the Unicode edition from which the tables are derived.
-const Version = "6.3.0"
+const Version = "7.0.0"
// Categories is the set of Unicode category tables.
var Categories = map[string]*RangeTable{
@@ -56,7 +56,7 @@ var _C = &RangeTable{
{0x0001, 0x001f, 1},
{0x007f, 0x009f, 1},
{0x00ad, 0x0600, 1363},
- {0x0601, 0x0604, 1},
+ {0x0601, 0x0605, 1},
{0x061c, 0x06dd, 193},
{0x070f, 0x180e, 4351},
{0x200b, 0x200f, 1},
@@ -68,8 +68,9 @@ var _C = &RangeTable{
{0xfffa, 0xfffb, 1},
},
R32: []Range32{
- {0x110bd, 0x1d173, 49334},
- {0x1d174, 0x1d17a, 1},
+ {0x110bd, 0x1bca0, 44003},
+ {0x1bca1, 0x1bca3, 1},
+ {0x1d173, 0x1d17a, 1},
{0xe0001, 0xe0020, 31},
{0xe0021, 0xe007f, 1},
{0xf0000, 0xffffd, 1},
@@ -89,7 +90,7 @@ var _Cc = &RangeTable{
var _Cf = &RangeTable{
R16: []Range16{
{0x00ad, 0x0600, 1363},
- {0x0601, 0x0604, 1},
+ {0x0601, 0x0605, 1},
{0x061c, 0x06dd, 193},
{0x070f, 0x180e, 4351},
{0x200b, 0x200f, 1},
@@ -100,8 +101,9 @@ var _Cf = &RangeTable{
{0xfffa, 0xfffb, 1},
},
R32: []Range32{
- {0x110bd, 0x1d173, 49334},
- {0x1d174, 0x1d17a, 1},
+ {0x110bd, 0x1bca0, 44003},
+ {0x1bca1, 0x1bca3, 1},
+ {0x1d173, 0x1d17a, 1},
{0xe0001, 0xe0020, 31},
{0xe0021, 0xe007f, 1},
},
@@ -138,13 +140,13 @@ var _L = &RangeTable{
{0x0370, 0x0374, 1},
{0x0376, 0x0377, 1},
{0x037a, 0x037d, 1},
- {0x0386, 0x0388, 2},
- {0x0389, 0x038a, 1},
+ {0x037f, 0x0386, 7},
+ {0x0388, 0x038a, 1},
{0x038c, 0x038e, 2},
{0x038f, 0x03a1, 1},
{0x03a3, 0x03f5, 1},
{0x03f7, 0x0481, 1},
- {0x048a, 0x0527, 1},
+ {0x048a, 0x052f, 1},
{0x0531, 0x0556, 1},
{0x0559, 0x0561, 8},
{0x0562, 0x0587, 1},
@@ -168,13 +170,11 @@ var _L = &RangeTable{
{0x081a, 0x0824, 10},
{0x0828, 0x0840, 24},
{0x0841, 0x0858, 1},
- {0x08a0, 0x08a2, 2},
- {0x08a3, 0x08ac, 1},
+ {0x08a0, 0x08b2, 1},
{0x0904, 0x0939, 1},
{0x093d, 0x0950, 19},
{0x0958, 0x0961, 1},
- {0x0971, 0x0977, 1},
- {0x0979, 0x097f, 1},
+ {0x0971, 0x0980, 1},
{0x0985, 0x098c, 1},
{0x098f, 0x0990, 1},
{0x0993, 0x09a8, 1},
@@ -226,8 +226,7 @@ var _L = &RangeTable{
{0x0c06, 0x0c0c, 1},
{0x0c0e, 0x0c10, 1},
{0x0c12, 0x0c28, 1},
- {0x0c2a, 0x0c33, 1},
- {0x0c35, 0x0c39, 1},
+ {0x0c2a, 0x0c39, 1},
{0x0c3d, 0x0c58, 27},
{0x0c59, 0x0c60, 7},
{0x0c61, 0x0c85, 36},
@@ -306,6 +305,7 @@ var _L = &RangeTable{
{0x166f, 0x167f, 1},
{0x1681, 0x169a, 1},
{0x16a0, 0x16ea, 1},
+ {0x16f1, 0x16f8, 1},
{0x1700, 0x170c, 1},
{0x170e, 0x1711, 1},
{0x1720, 0x1731, 1},
@@ -318,7 +318,7 @@ var _L = &RangeTable{
{0x1880, 0x18a8, 1},
{0x18aa, 0x18b0, 6},
{0x18b1, 0x18f5, 1},
- {0x1900, 0x191c, 1},
+ {0x1900, 0x191e, 1},
{0x1950, 0x196d, 1},
{0x1970, 0x1974, 1},
{0x1980, 0x19ab, 1},
@@ -406,14 +406,14 @@ var _L = &RangeTable{
{0xa610, 0xa61f, 1},
{0xa62a, 0xa62b, 1},
{0xa640, 0xa66e, 1},
- {0xa67f, 0xa697, 1},
+ {0xa67f, 0xa69d, 1},
{0xa6a0, 0xa6e5, 1},
{0xa717, 0xa71f, 1},
{0xa722, 0xa788, 1},
{0xa78b, 0xa78e, 1},
- {0xa790, 0xa793, 1},
- {0xa7a0, 0xa7aa, 1},
- {0xa7f8, 0xa801, 1},
+ {0xa790, 0xa7ad, 1},
+ {0xa7b0, 0xa7b1, 1},
+ {0xa7f7, 0xa801, 1},
{0xa803, 0xa805, 1},
{0xa807, 0xa80a, 1},
{0xa80c, 0xa822, 1},
@@ -425,13 +425,16 @@ var _L = &RangeTable{
{0xa930, 0xa946, 1},
{0xa960, 0xa97c, 1},
{0xa984, 0xa9b2, 1},
- {0xa9cf, 0xaa00, 49},
- {0xaa01, 0xaa28, 1},
+ {0xa9cf, 0xa9e0, 17},
+ {0xa9e1, 0xa9e4, 1},
+ {0xa9e6, 0xa9ef, 1},
+ {0xa9fa, 0xa9fe, 1},
+ {0xaa00, 0xaa28, 1},
{0xaa40, 0xaa42, 1},
{0xaa44, 0xaa4b, 1},
{0xaa60, 0xaa76, 1},
- {0xaa7a, 0xaa80, 6},
- {0xaa81, 0xaaaf, 1},
+ {0xaa7a, 0xaa7e, 4},
+ {0xaa7f, 0xaaaf, 1},
{0xaab1, 0xaab5, 4},
{0xaab6, 0xaab9, 3},
{0xaaba, 0xaabd, 1},
@@ -444,6 +447,9 @@ var _L = &RangeTable{
{0xab11, 0xab16, 1},
{0xab20, 0xab26, 1},
{0xab28, 0xab2e, 1},
+ {0xab30, 0xab5a, 1},
+ {0xab5c, 0xab5f, 1},
+ {0xab64, 0xab65, 1},
{0xabc0, 0xabe2, 1},
{0xac00, 0xd7a3, 1},
{0xd7b0, 0xd7c6, 1},
@@ -484,19 +490,27 @@ var _L = &RangeTable{
{0x10080, 0x100fa, 1},
{0x10280, 0x1029c, 1},
{0x102a0, 0x102d0, 1},
- {0x10300, 0x1031e, 1},
+ {0x10300, 0x1031f, 1},
{0x10330, 0x10340, 1},
{0x10342, 0x10349, 1},
+ {0x10350, 0x10375, 1},
{0x10380, 0x1039d, 1},
{0x103a0, 0x103c3, 1},
{0x103c8, 0x103cf, 1},
{0x10400, 0x1049d, 1},
+ {0x10500, 0x10527, 1},
+ {0x10530, 0x10563, 1},
+ {0x10600, 0x10736, 1},
+ {0x10740, 0x10755, 1},
+ {0x10760, 0x10767, 1},
{0x10800, 0x10805, 1},
{0x10808, 0x1080a, 2},
{0x1080b, 0x10835, 1},
{0x10837, 0x10838, 1},
{0x1083c, 0x1083f, 3},
{0x10840, 0x10855, 1},
+ {0x10860, 0x10876, 1},
+ {0x10880, 0x1089e, 1},
{0x10900, 0x10915, 1},
{0x10920, 0x10939, 1},
{0x10980, 0x109b7, 1},
@@ -506,24 +520,61 @@ var _L = &RangeTable{
{0x10a15, 0x10a17, 1},
{0x10a19, 0x10a33, 1},
{0x10a60, 0x10a7c, 1},
+ {0x10a80, 0x10a9c, 1},
+ {0x10ac0, 0x10ac7, 1},
+ {0x10ac9, 0x10ae4, 1},
{0x10b00, 0x10b35, 1},
{0x10b40, 0x10b55, 1},
{0x10b60, 0x10b72, 1},
+ {0x10b80, 0x10b91, 1},
{0x10c00, 0x10c48, 1},
{0x11003, 0x11037, 1},
{0x11083, 0x110af, 1},
{0x110d0, 0x110e8, 1},
{0x11103, 0x11126, 1},
- {0x11183, 0x111b2, 1},
+ {0x11150, 0x11172, 1},
+ {0x11176, 0x11183, 13},
+ {0x11184, 0x111b2, 1},
{0x111c1, 0x111c4, 1},
- {0x11680, 0x116aa, 1},
- {0x12000, 0x1236e, 1},
+ {0x111da, 0x11200, 38},
+ {0x11201, 0x11211, 1},
+ {0x11213, 0x1122b, 1},
+ {0x112b0, 0x112de, 1},
+ {0x11305, 0x1130c, 1},
+ {0x1130f, 0x11310, 1},
+ {0x11313, 0x11328, 1},
+ {0x1132a, 0x11330, 1},
+ {0x11332, 0x11333, 1},
+ {0x11335, 0x11339, 1},
+ {0x1133d, 0x1135d, 32},
+ {0x1135e, 0x11361, 1},
+ {0x11480, 0x114af, 1},
+ {0x114c4, 0x114c5, 1},
+ {0x114c7, 0x11580, 185},
+ {0x11581, 0x115ae, 1},
+ {0x11600, 0x1162f, 1},
+ {0x11644, 0x11680, 60},
+ {0x11681, 0x116aa, 1},
+ {0x118a0, 0x118df, 1},
+ {0x118ff, 0x11ac0, 449},
+ {0x11ac1, 0x11af8, 1},
+ {0x12000, 0x12398, 1},
{0x13000, 0x1342e, 1},
{0x16800, 0x16a38, 1},
+ {0x16a40, 0x16a5e, 1},
+ {0x16ad0, 0x16aed, 1},
+ {0x16b00, 0x16b2f, 1},
+ {0x16b40, 0x16b43, 1},
+ {0x16b63, 0x16b77, 1},
+ {0x16b7d, 0x16b8f, 1},
{0x16f00, 0x16f44, 1},
{0x16f50, 0x16f93, 67},
{0x16f94, 0x16f9f, 1},
{0x1b000, 0x1b001, 1},
+ {0x1bc00, 0x1bc6a, 1},
+ {0x1bc70, 0x1bc7c, 1},
+ {0x1bc80, 0x1bc88, 1},
+ {0x1bc90, 0x1bc99, 1},
{0x1d400, 0x1d454, 1},
{0x1d456, 0x1d49c, 1},
{0x1d49e, 0x1d49f, 1},
@@ -554,6 +605,7 @@ var _L = &RangeTable{
{0x1d78a, 0x1d7a8, 1},
{0x1d7aa, 0x1d7c2, 1},
{0x1d7c4, 0x1d7cb, 1},
+ {0x1e800, 0x1e8c4, 1},
{0x1ee00, 0x1ee03, 1},
{0x1ee05, 0x1ee1f, 1},
{0x1ee21, 0x1ee22, 1},
@@ -637,7 +689,7 @@ var _Ll = &RangeTable{
{0x0461, 0x0481, 2},
{0x048b, 0x04bf, 2},
{0x04c2, 0x04ce, 2},
- {0x04cf, 0x0527, 2},
+ {0x04cf, 0x052f, 2},
{0x0561, 0x0587, 1},
{0x1d00, 0x1d2b, 1},
{0x1d6b, 0x1d77, 1},
@@ -684,7 +736,7 @@ var _Ll = &RangeTable{
{0x2d00, 0x2d25, 1},
{0x2d27, 0x2d2d, 6},
{0xa641, 0xa66d, 2},
- {0xa681, 0xa697, 2},
+ {0xa681, 0xa69b, 2},
{0xa723, 0xa72f, 2},
{0xa730, 0xa731, 1},
{0xa733, 0xa771, 2},
@@ -693,14 +745,18 @@ var _Ll = &RangeTable{
{0xa77f, 0xa787, 2},
{0xa78c, 0xa78e, 2},
{0xa791, 0xa793, 2},
- {0xa7a1, 0xa7a9, 2},
- {0xa7fa, 0xfb00, 21254},
- {0xfb01, 0xfb06, 1},
+ {0xa794, 0xa795, 1},
+ {0xa797, 0xa7a9, 2},
+ {0xa7fa, 0xab30, 822},
+ {0xab31, 0xab5a, 1},
+ {0xab64, 0xab65, 1},
+ {0xfb00, 0xfb06, 1},
{0xfb13, 0xfb17, 1},
{0xff41, 0xff5a, 1},
},
R32: []Range32{
{0x10428, 0x1044f, 1},
+ {0x118c0, 0x118df, 1},
{0x1d41a, 0x1d433, 1},
{0x1d44e, 0x1d454, 1},
{0x1d456, 0x1d467, 1},
@@ -765,15 +821,20 @@ var _Lm = &RangeTable{
{0xa015, 0xa4f8, 1251},
{0xa4f9, 0xa4fd, 1},
{0xa60c, 0xa67f, 115},
+ {0xa69c, 0xa69d, 1},
{0xa717, 0xa71f, 1},
{0xa770, 0xa788, 24},
{0xa7f8, 0xa7f9, 1},
- {0xa9cf, 0xaa70, 161},
- {0xaadd, 0xaaf3, 22},
- {0xaaf4, 0xff70, 21628},
- {0xff9e, 0xff9f, 1},
+ {0xa9cf, 0xa9e6, 23},
+ {0xaa70, 0xaadd, 109},
+ {0xaaf3, 0xaaf4, 1},
+ {0xab5c, 0xab5f, 1},
+ {0xff70, 0xff9e, 46},
+ {0xff9f, 0xff9f, 1},
},
R32: []Range32{
+ {0x16b40, 0x16b40, 1},
+ {0x16b41, 0x16b43, 1},
{0x16f93, 0x16f9f, 1},
},
}
@@ -800,13 +861,11 @@ var _Lo = &RangeTable{
{0x07cb, 0x07ea, 1},
{0x0800, 0x0815, 1},
{0x0840, 0x0858, 1},
- {0x08a0, 0x08a2, 2},
- {0x08a3, 0x08ac, 1},
+ {0x08a0, 0x08b2, 1},
{0x0904, 0x0939, 1},
{0x093d, 0x0950, 19},
{0x0958, 0x0961, 1},
- {0x0972, 0x0977, 1},
- {0x0979, 0x097f, 1},
+ {0x0972, 0x0980, 1},
{0x0985, 0x098c, 1},
{0x098f, 0x0990, 1},
{0x0993, 0x09a8, 1},
@@ -858,8 +917,7 @@ var _Lo = &RangeTable{
{0x0c06, 0x0c0c, 1},
{0x0c0e, 0x0c10, 1},
{0x0c12, 0x0c28, 1},
- {0x0c2a, 0x0c33, 1},
- {0x0c35, 0x0c39, 1},
+ {0x0c2a, 0x0c39, 1},
{0x0c3d, 0x0c58, 27},
{0x0c59, 0x0c60, 7},
{0x0c61, 0x0c85, 36},
@@ -935,6 +993,7 @@ var _Lo = &RangeTable{
{0x166f, 0x167f, 1},
{0x1681, 0x169a, 1},
{0x16a0, 0x16ea, 1},
+ {0x16f1, 0x16f8, 1},
{0x1700, 0x170c, 1},
{0x170e, 0x1711, 1},
{0x1720, 0x1731, 1},
@@ -948,7 +1007,7 @@ var _Lo = &RangeTable{
{0x1880, 0x18a8, 1},
{0x18aa, 0x18b0, 6},
{0x18b1, 0x18f5, 1},
- {0x1900, 0x191c, 1},
+ {0x1900, 0x191e, 1},
{0x1950, 0x196d, 1},
{0x1970, 0x1974, 1},
{0x1980, 0x19ab, 1},
@@ -996,7 +1055,8 @@ var _Lo = &RangeTable{
{0xa62a, 0xa62b, 1},
{0xa66e, 0xa6a0, 50},
{0xa6a1, 0xa6e5, 1},
- {0xa7fb, 0xa801, 1},
+ {0xa7f7, 0xa7fb, 4},
+ {0xa7fc, 0xa801, 1},
{0xa803, 0xa805, 1},
{0xa807, 0xa80a, 1},
{0xa80c, 0xa822, 1},
@@ -1008,13 +1068,16 @@ var _Lo = &RangeTable{
{0xa930, 0xa946, 1},
{0xa960, 0xa97c, 1},
{0xa984, 0xa9b2, 1},
+ {0xa9e0, 0xa9e4, 1},
+ {0xa9e7, 0xa9ef, 1},
+ {0xa9fa, 0xa9fe, 1},
{0xaa00, 0xaa28, 1},
{0xaa40, 0xaa42, 1},
{0xaa44, 0xaa4b, 1},
{0xaa60, 0xaa6f, 1},
{0xaa71, 0xaa76, 1},
- {0xaa7a, 0xaa80, 6},
- {0xaa81, 0xaaaf, 1},
+ {0xaa7a, 0xaa7e, 4},
+ {0xaa7f, 0xaaaf, 1},
{0xaab1, 0xaab5, 4},
{0xaab6, 0xaab9, 3},
{0xaaba, 0xaabd, 1},
@@ -1065,19 +1128,27 @@ var _Lo = &RangeTable{
{0x10080, 0x100fa, 1},
{0x10280, 0x1029c, 1},
{0x102a0, 0x102d0, 1},
- {0x10300, 0x1031e, 1},
+ {0x10300, 0x1031f, 1},
{0x10330, 0x10340, 1},
{0x10342, 0x10349, 1},
+ {0x10350, 0x10375, 1},
{0x10380, 0x1039d, 1},
{0x103a0, 0x103c3, 1},
{0x103c8, 0x103cf, 1},
{0x10450, 0x1049d, 1},
+ {0x10500, 0x10527, 1},
+ {0x10530, 0x10563, 1},
+ {0x10600, 0x10736, 1},
+ {0x10740, 0x10755, 1},
+ {0x10760, 0x10767, 1},
{0x10800, 0x10805, 1},
{0x10808, 0x1080a, 2},
{0x1080b, 0x10835, 1},
{0x10837, 0x10838, 1},
{0x1083c, 0x1083f, 3},
{0x10840, 0x10855, 1},
+ {0x10860, 0x10876, 1},
+ {0x10880, 0x1089e, 1},
{0x10900, 0x10915, 1},
{0x10920, 0x10939, 1},
{0x10980, 0x109b7, 1},
@@ -1087,24 +1158,60 @@ var _Lo = &RangeTable{
{0x10a15, 0x10a17, 1},
{0x10a19, 0x10a33, 1},
{0x10a60, 0x10a7c, 1},
+ {0x10a80, 0x10a9c, 1},
+ {0x10ac0, 0x10ac7, 1},
+ {0x10ac9, 0x10ae4, 1},
{0x10b00, 0x10b35, 1},
{0x10b40, 0x10b55, 1},
{0x10b60, 0x10b72, 1},
+ {0x10b80, 0x10b91, 1},
{0x10c00, 0x10c48, 1},
{0x11003, 0x11037, 1},
{0x11083, 0x110af, 1},
{0x110d0, 0x110e8, 1},
{0x11103, 0x11126, 1},
- {0x11183, 0x111b2, 1},
+ {0x11150, 0x11172, 1},
+ {0x11176, 0x11183, 13},
+ {0x11184, 0x111b2, 1},
{0x111c1, 0x111c4, 1},
- {0x11680, 0x116aa, 1},
- {0x12000, 0x1236e, 1},
+ {0x111da, 0x11200, 38},
+ {0x11201, 0x11211, 1},
+ {0x11213, 0x1122b, 1},
+ {0x112b0, 0x112de, 1},
+ {0x11305, 0x1130c, 1},
+ {0x1130f, 0x11310, 1},
+ {0x11313, 0x11328, 1},
+ {0x1132a, 0x11330, 1},
+ {0x11332, 0x11333, 1},
+ {0x11335, 0x11339, 1},
+ {0x1133d, 0x1135d, 32},
+ {0x1135e, 0x11361, 1},
+ {0x11480, 0x114af, 1},
+ {0x114c4, 0x114c5, 1},
+ {0x114c7, 0x11580, 185},
+ {0x11581, 0x115ae, 1},
+ {0x11600, 0x1162f, 1},
+ {0x11644, 0x11680, 60},
+ {0x11681, 0x116aa, 1},
+ {0x118ff, 0x11ac0, 449},
+ {0x11ac1, 0x11af8, 1},
+ {0x12000, 0x12398, 1},
{0x13000, 0x1342e, 1},
{0x16800, 0x16a38, 1},
+ {0x16a40, 0x16a5e, 1},
+ {0x16ad0, 0x16aed, 1},
+ {0x16b00, 0x16b2f, 1},
+ {0x16b63, 0x16b77, 1},
+ {0x16b7d, 0x16b8f, 1},
{0x16f00, 0x16f44, 1},
{0x16f50, 0x1b000, 16560},
- {0x1b001, 0x1ee00, 15871},
- {0x1ee01, 0x1ee03, 1},
+ {0x1b001, 0x1bc00, 3071},
+ {0x1bc01, 0x1bc6a, 1},
+ {0x1bc70, 0x1bc7c, 1},
+ {0x1bc80, 0x1bc88, 1},
+ {0x1bc90, 0x1bc99, 1},
+ {0x1e800, 0x1e8c4, 1},
+ {0x1ee00, 0x1ee03, 1},
{0x1ee05, 0x1ee1f, 1},
{0x1ee21, 0x1ee22, 1},
{0x1ee24, 0x1ee27, 3},
@@ -1185,8 +1292,9 @@ var _Lu = &RangeTable{
{0x0244, 0x0246, 1},
{0x0248, 0x024e, 2},
{0x0370, 0x0372, 2},
- {0x0376, 0x0386, 16},
- {0x0388, 0x038a, 1},
+ {0x0376, 0x037f, 9},
+ {0x0386, 0x0388, 2},
+ {0x0389, 0x038a, 1},
{0x038c, 0x038e, 2},
{0x038f, 0x0391, 2},
{0x0392, 0x03a1, 1},
@@ -1200,7 +1308,7 @@ var _Lu = &RangeTable{
{0x0460, 0x0480, 2},
{0x048a, 0x04c0, 2},
{0x04c1, 0x04cd, 2},
- {0x04d0, 0x0526, 2},
+ {0x04d0, 0x052e, 2},
{0x0531, 0x0556, 1},
{0x10a0, 0x10c5, 1},
{0x10c7, 0x10cd, 6},
@@ -1239,18 +1347,21 @@ var _Lu = &RangeTable{
{0x2ceb, 0x2ced, 2},
{0x2cf2, 0xa640, 31054},
{0xa642, 0xa66c, 2},
- {0xa680, 0xa696, 2},
+ {0xa680, 0xa69a, 2},
{0xa722, 0xa72e, 2},
{0xa732, 0xa76e, 2},
{0xa779, 0xa77d, 2},
{0xa77e, 0xa786, 2},
{0xa78b, 0xa78d, 2},
{0xa790, 0xa792, 2},
- {0xa7a0, 0xa7aa, 2},
+ {0xa796, 0xa7aa, 2},
+ {0xa7ab, 0xa7ad, 1},
+ {0xa7b0, 0xa7b1, 1},
{0xff21, 0xff3a, 1},
},
R32: []Range32{
{0x10400, 0x10427, 1},
+ {0x118a0, 0x118bf, 1},
{0x1d400, 0x1d419, 1},
{0x1d434, 0x1d44d, 1},
{0x1d468, 0x1d481, 1},
@@ -1309,8 +1420,7 @@ var _M = &RangeTable{
{0x0825, 0x0827, 1},
{0x0829, 0x082d, 1},
{0x0859, 0x085b, 1},
- {0x08e4, 0x08fe, 1},
- {0x0900, 0x0903, 1},
+ {0x08e4, 0x0903, 1},
{0x093a, 0x093c, 1},
{0x093e, 0x094f, 1},
{0x0951, 0x0957, 1},
@@ -1346,21 +1456,21 @@ var _M = &RangeTable{
{0x0bbf, 0x0bc2, 1},
{0x0bc6, 0x0bc8, 1},
{0x0bca, 0x0bcd, 1},
- {0x0bd7, 0x0c01, 42},
- {0x0c02, 0x0c03, 1},
+ {0x0bd7, 0x0c00, 41},
+ {0x0c01, 0x0c03, 1},
{0x0c3e, 0x0c44, 1},
{0x0c46, 0x0c48, 1},
{0x0c4a, 0x0c4d, 1},
{0x0c55, 0x0c56, 1},
{0x0c62, 0x0c63, 1},
- {0x0c82, 0x0c83, 1},
+ {0x0c81, 0x0c83, 1},
{0x0cbc, 0x0cbe, 2},
{0x0cbf, 0x0cc4, 1},
{0x0cc6, 0x0cc8, 1},
{0x0cca, 0x0ccd, 1},
{0x0cd5, 0x0cd6, 1},
{0x0ce2, 0x0ce3, 1},
- {0x0d02, 0x0d03, 1},
+ {0x0d01, 0x0d03, 1},
{0x0d3e, 0x0d44, 1},
{0x0d46, 0x0d48, 1},
{0x0d4a, 0x0d4d, 1},
@@ -1411,8 +1521,9 @@ var _M = &RangeTable{
{0x1a17, 0x1a1b, 1},
{0x1a55, 0x1a5e, 1},
{0x1a60, 0x1a7c, 1},
- {0x1a7f, 0x1b00, 129},
- {0x1b01, 0x1b04, 1},
+ {0x1a7f, 0x1ab0, 49},
+ {0x1ab1, 0x1abe, 1},
+ {0x1b00, 0x1b04, 1},
{0x1b34, 0x1b44, 1},
{0x1b6b, 0x1b73, 1},
{0x1b80, 0x1b82, 1},
@@ -1423,7 +1534,8 @@ var _M = &RangeTable{
{0x1cd4, 0x1ce8, 1},
{0x1ced, 0x1cf2, 5},
{0x1cf3, 0x1cf4, 1},
- {0x1dc0, 0x1de6, 1},
+ {0x1cf8, 0x1cf9, 1},
+ {0x1dc0, 0x1df5, 1},
{0x1dfc, 0x1dff, 1},
{0x20d0, 0x20f0, 1},
{0x2cef, 0x2cf1, 1},
@@ -1444,9 +1556,11 @@ var _M = &RangeTable{
{0xa947, 0xa953, 1},
{0xa980, 0xa983, 1},
{0xa9b3, 0xa9c0, 1},
- {0xaa29, 0xaa36, 1},
+ {0xa9e5, 0xaa29, 68},
+ {0xaa2a, 0xaa36, 1},
{0xaa43, 0xaa4c, 9},
{0xaa4d, 0xaa7b, 46},
+ {0xaa7c, 0xaa7d, 1},
{0xaab0, 0xaab2, 2},
{0xaab3, 0xaab4, 1},
{0xaab7, 0xaab8, 1},
@@ -1458,32 +1572,54 @@ var _M = &RangeTable{
{0xabec, 0xabed, 1},
{0xfb1e, 0xfe00, 738},
{0xfe01, 0xfe0f, 1},
- {0xfe20, 0xfe26, 1},
+ {0xfe20, 0xfe2d, 1},
},
R32: []Range32{
- {0x101fd, 0x10a01, 2052},
- {0x10a02, 0x10a03, 1},
+ {0x101fd, 0x102e0, 227},
+ {0x10376, 0x1037a, 1},
+ {0x10a01, 0x10a03, 1},
{0x10a05, 0x10a06, 1},
{0x10a0c, 0x10a0f, 1},
{0x10a38, 0x10a3a, 1},
- {0x10a3f, 0x11000, 1473},
+ {0x10a3f, 0x10ae5, 166},
+ {0x10ae6, 0x11000, 1306},
{0x11001, 0x11002, 1},
{0x11038, 0x11046, 1},
- {0x11080, 0x11082, 1},
+ {0x1107f, 0x11082, 1},
{0x110b0, 0x110ba, 1},
{0x11100, 0x11102, 1},
{0x11127, 0x11134, 1},
- {0x11180, 0x11182, 1},
+ {0x11173, 0x11180, 13},
+ {0x11181, 0x11182, 1},
{0x111b3, 0x111c0, 1},
+ {0x1122c, 0x11237, 1},
+ {0x112df, 0x112ea, 1},
+ {0x11301, 0x11303, 1},
+ {0x1133c, 0x1133e, 2},
+ {0x1133f, 0x11344, 1},
+ {0x11347, 0x11348, 1},
+ {0x1134b, 0x1134d, 1},
+ {0x11357, 0x11362, 11},
+ {0x11363, 0x11366, 3},
+ {0x11367, 0x1136c, 1},
+ {0x11370, 0x11374, 1},
+ {0x114b0, 0x114c3, 1},
+ {0x115af, 0x115b5, 1},
+ {0x115b8, 0x115c0, 1},
+ {0x11630, 0x11640, 1},
{0x116ab, 0x116b7, 1},
+ {0x16af0, 0x16af4, 1},
+ {0x16b30, 0x16b36, 1},
{0x16f51, 0x16f7e, 1},
{0x16f8f, 0x16f92, 1},
+ {0x1bc9d, 0x1bc9e, 1},
{0x1d165, 0x1d169, 1},
{0x1d16d, 0x1d172, 1},
{0x1d17b, 0x1d182, 1},
{0x1d185, 0x1d18b, 1},
{0x1d1aa, 0x1d1ad, 1},
{0x1d242, 0x1d244, 1},
+ {0x1e8d0, 0x1e8d6, 1},
{0xe0100, 0xe01ef, 1},
},
}
@@ -1562,8 +1698,7 @@ var _Mc = &RangeTable{
{0x1b43, 0x1b44, 1},
{0x1b82, 0x1ba1, 31},
{0x1ba6, 0x1ba7, 1},
- {0x1baa, 0x1bac, 2},
- {0x1bad, 0x1be7, 58},
+ {0x1baa, 0x1be7, 61},
{0x1bea, 0x1bec, 1},
{0x1bee, 0x1bf2, 4},
{0x1bf3, 0x1c24, 49},
@@ -1583,24 +1718,45 @@ var _Mc = &RangeTable{
{0xaa2f, 0xaa30, 1},
{0xaa33, 0xaa34, 1},
{0xaa4d, 0xaa7b, 46},
- {0xaaeb, 0xaaee, 3},
- {0xaaef, 0xaaf5, 6},
- {0xabe3, 0xabe4, 1},
- {0xabe6, 0xabe7, 1},
- {0xabe9, 0xabea, 1},
- {0xabec, 0xabec, 1},
+ {0xaa7d, 0xaaeb, 110},
+ {0xaaee, 0xaaef, 1},
+ {0xaaf5, 0xabe3, 238},
+ {0xabe4, 0xabe6, 2},
+ {0xabe7, 0xabe9, 2},
+ {0xabea, 0xabec, 2},
},
R32: []Range32{
- {0x11000, 0x11000, 1},
- {0x11002, 0x11082, 128},
- {0x110b0, 0x110b2, 1},
+ {0x11000, 0x11002, 2},
+ {0x11082, 0x110b0, 46},
+ {0x110b1, 0x110b2, 1},
{0x110b7, 0x110b8, 1},
{0x1112c, 0x11182, 86},
{0x111b3, 0x111b5, 1},
{0x111bf, 0x111c0, 1},
- {0x116ac, 0x116ae, 2},
- {0x116af, 0x116b6, 7},
- {0x16f51, 0x16f7e, 1},
+ {0x1122c, 0x1122e, 1},
+ {0x11232, 0x11233, 1},
+ {0x11235, 0x112e0, 171},
+ {0x112e1, 0x112e2, 1},
+ {0x11302, 0x11303, 1},
+ {0x1133e, 0x1133f, 1},
+ {0x11341, 0x11344, 1},
+ {0x11347, 0x11348, 1},
+ {0x1134b, 0x1134d, 1},
+ {0x11357, 0x11362, 11},
+ {0x11363, 0x114b0, 333},
+ {0x114b1, 0x114b2, 1},
+ {0x114b9, 0x114bb, 2},
+ {0x114bc, 0x114be, 1},
+ {0x114c1, 0x115af, 238},
+ {0x115b0, 0x115b1, 1},
+ {0x115b8, 0x115bb, 1},
+ {0x115be, 0x11630, 114},
+ {0x11631, 0x11632, 1},
+ {0x1163b, 0x1163c, 1},
+ {0x1163e, 0x116ac, 110},
+ {0x116ae, 0x116af, 1},
+ {0x116b6, 0x16f51, 22683},
+ {0x16f52, 0x16f7e, 1},
{0x1d165, 0x1d166, 1},
{0x1d16d, 0x1d172, 1},
},
@@ -1609,7 +1765,8 @@ var _Mc = &RangeTable{
var _Me = &RangeTable{
R16: []Range16{
{0x0488, 0x0489, 1},
- {0x20dd, 0x20e0, 1},
+ {0x1abe, 0x20dd, 1567},
+ {0x20de, 0x20e0, 1},
{0x20e2, 0x20e4, 1},
{0xa670, 0xa672, 1},
},
@@ -1639,8 +1796,7 @@ var _Mn = &RangeTable{
{0x0825, 0x0827, 1},
{0x0829, 0x082d, 1},
{0x0859, 0x085b, 1},
- {0x08e4, 0x08fe, 1},
- {0x0900, 0x0902, 1},
+ {0x08e4, 0x0902, 1},
{0x093a, 0x093c, 2},
{0x0941, 0x0948, 1},
{0x094d, 0x0951, 4},
@@ -1667,16 +1823,17 @@ var _Mn = &RangeTable{
{0x0b4d, 0x0b56, 9},
{0x0b62, 0x0b63, 1},
{0x0b82, 0x0bc0, 62},
- {0x0bcd, 0x0c3e, 113},
- {0x0c3f, 0x0c40, 1},
+ {0x0bcd, 0x0c00, 51},
+ {0x0c3e, 0x0c40, 1},
{0x0c46, 0x0c48, 1},
{0x0c4a, 0x0c4d, 1},
{0x0c55, 0x0c56, 1},
{0x0c62, 0x0c63, 1},
- {0x0cbc, 0x0cbf, 3},
- {0x0cc6, 0x0ccc, 6},
- {0x0ccd, 0x0ce2, 21},
- {0x0ce3, 0x0d41, 94},
+ {0x0c81, 0x0cbc, 59},
+ {0x0cbf, 0x0cc6, 7},
+ {0x0ccc, 0x0ccd, 1},
+ {0x0ce2, 0x0ce3, 1},
+ {0x0d01, 0x0d41, 64},
{0x0d42, 0x0d44, 1},
{0x0d4d, 0x0d62, 21},
{0x0d63, 0x0dca, 103},
@@ -1728,8 +1885,9 @@ var _Mn = &RangeTable{
{0x1a60, 0x1a62, 2},
{0x1a65, 0x1a6c, 1},
{0x1a73, 0x1a7c, 1},
- {0x1a7f, 0x1b00, 129},
- {0x1b01, 0x1b03, 1},
+ {0x1a7f, 0x1ab0, 49},
+ {0x1ab1, 0x1abd, 1},
+ {0x1b00, 0x1b03, 1},
{0x1b34, 0x1b36, 2},
{0x1b37, 0x1b3a, 1},
{0x1b3c, 0x1b42, 6},
@@ -1737,17 +1895,18 @@ var _Mn = &RangeTable{
{0x1b80, 0x1b81, 1},
{0x1ba2, 0x1ba5, 1},
{0x1ba8, 0x1ba9, 1},
- {0x1bab, 0x1be6, 59},
- {0x1be8, 0x1be9, 1},
- {0x1bed, 0x1bef, 2},
- {0x1bf0, 0x1bf1, 1},
+ {0x1bab, 0x1bad, 1},
+ {0x1be6, 0x1be8, 2},
+ {0x1be9, 0x1bed, 4},
+ {0x1bef, 0x1bf1, 1},
{0x1c2c, 0x1c33, 1},
{0x1c36, 0x1c37, 1},
{0x1cd0, 0x1cd2, 1},
{0x1cd4, 0x1ce0, 1},
{0x1ce2, 0x1ce8, 1},
{0x1ced, 0x1cf4, 7},
- {0x1dc0, 0x1de6, 1},
+ {0x1cf8, 0x1cf9, 1},
+ {0x1dc0, 0x1df5, 1},
{0x1dfc, 0x1dff, 1},
{0x20d0, 0x20dc, 1},
{0x20e1, 0x20e5, 4},
@@ -1770,13 +1929,13 @@ var _Mn = &RangeTable{
{0xa980, 0xa982, 1},
{0xa9b3, 0xa9b6, 3},
{0xa9b7, 0xa9b9, 1},
- {0xa9bc, 0xaa29, 109},
- {0xaa2a, 0xaa2e, 1},
+ {0xa9bc, 0xa9e5, 41},
+ {0xaa29, 0xaa2e, 1},
{0xaa31, 0xaa32, 1},
{0xaa35, 0xaa36, 1},
{0xaa43, 0xaa4c, 9},
- {0xaab0, 0xaab2, 2},
- {0xaab3, 0xaab4, 1},
+ {0xaa7c, 0xaab0, 52},
+ {0xaab2, 0xaab4, 1},
{0xaab7, 0xaab8, 1},
{0xaabe, 0xaabf, 1},
{0xaac1, 0xaaec, 43},
@@ -1784,33 +1943,58 @@ var _Mn = &RangeTable{
{0xabe5, 0xabe8, 3},
{0xabed, 0xfb1e, 20273},
{0xfe00, 0xfe0f, 1},
- {0xfe20, 0xfe26, 1},
+ {0xfe20, 0xfe2d, 1},
},
R32: []Range32{
- {0x101fd, 0x10a01, 2052},
- {0x10a02, 0x10a03, 1},
+ {0x101fd, 0x102e0, 227},
+ {0x10376, 0x1037a, 1},
+ {0x10a01, 0x10a03, 1},
{0x10a05, 0x10a06, 1},
{0x10a0c, 0x10a0f, 1},
{0x10a38, 0x10a3a, 1},
- {0x10a3f, 0x11001, 1474},
+ {0x10a3f, 0x10ae5, 166},
+ {0x10ae6, 0x11001, 1307},
{0x11038, 0x11046, 1},
- {0x11080, 0x11081, 1},
+ {0x1107f, 0x11081, 1},
{0x110b3, 0x110b6, 1},
{0x110b9, 0x110ba, 1},
{0x11100, 0x11102, 1},
{0x11127, 0x1112b, 1},
{0x1112d, 0x11134, 1},
- {0x11180, 0x11181, 1},
- {0x111b6, 0x111be, 1},
- {0x116ab, 0x116ad, 2},
- {0x116b0, 0x116b5, 1},
- {0x116b7, 0x16f8f, 22744},
- {0x16f90, 0x16f92, 1},
+ {0x11173, 0x11180, 13},
+ {0x11181, 0x111b6, 53},
+ {0x111b7, 0x111be, 1},
+ {0x1122f, 0x11231, 1},
+ {0x11234, 0x11236, 2},
+ {0x11237, 0x112df, 168},
+ {0x112e3, 0x112ea, 1},
+ {0x11301, 0x1133c, 59},
+ {0x11340, 0x11366, 38},
+ {0x11367, 0x1136c, 1},
+ {0x11370, 0x11374, 1},
+ {0x114b3, 0x114b8, 1},
+ {0x114ba, 0x114bf, 5},
+ {0x114c0, 0x114c2, 2},
+ {0x114c3, 0x115b2, 239},
+ {0x115b3, 0x115b5, 1},
+ {0x115bc, 0x115bd, 1},
+ {0x115bf, 0x115c0, 1},
+ {0x11633, 0x1163a, 1},
+ {0x1163d, 0x1163f, 2},
+ {0x11640, 0x116ab, 107},
+ {0x116ad, 0x116b0, 3},
+ {0x116b1, 0x116b5, 1},
+ {0x116b7, 0x16af0, 21561},
+ {0x16af1, 0x16af4, 1},
+ {0x16b30, 0x16b36, 1},
+ {0x16f8f, 0x16f92, 1},
+ {0x1bc9d, 0x1bc9e, 1},
{0x1d167, 0x1d169, 1},
{0x1d17b, 0x1d182, 1},
{0x1d185, 0x1d18b, 1},
{0x1d1aa, 0x1d1ad, 1},
{0x1d242, 0x1d244, 1},
+ {0x1e8d0, 0x1e8d6, 1},
{0xe0100, 0xe01ef, 1},
},
}
@@ -1836,6 +2020,7 @@ var _N = &RangeTable{
{0x0c78, 0x0c7e, 1},
{0x0ce6, 0x0cef, 1},
{0x0d66, 0x0d75, 1},
+ {0x0de6, 0x0def, 1},
{0x0e50, 0x0e59, 1},
{0x0ed0, 0x0ed9, 1},
{0x0f20, 0x0f33, 1},
@@ -1877,6 +2062,7 @@ var _N = &RangeTable{
{0xa8d0, 0xa8d9, 1},
{0xa900, 0xa909, 1},
{0xa9d0, 0xa9d9, 1},
+ {0xa9f0, 0xa9f9, 1},
{0xaa50, 0xaa59, 1},
{0xabf0, 0xabf9, 1},
{0xff10, 0xff19, 1},
@@ -1884,27 +2070,42 @@ var _N = &RangeTable{
R32: []Range32{
{0x10107, 0x10133, 1},
{0x10140, 0x10178, 1},
- {0x1018a, 0x10320, 406},
- {0x10321, 0x10323, 1},
+ {0x1018a, 0x1018b, 1},
+ {0x102e1, 0x102fb, 1},
+ {0x10320, 0x10323, 1},
{0x10341, 0x1034a, 9},
{0x103d1, 0x103d5, 1},
{0x104a0, 0x104a9, 1},
{0x10858, 0x1085f, 1},
+ {0x10879, 0x1087f, 1},
+ {0x108a7, 0x108af, 1},
{0x10916, 0x1091b, 1},
{0x10a40, 0x10a47, 1},
{0x10a7d, 0x10a7e, 1},
+ {0x10a9d, 0x10a9f, 1},
+ {0x10aeb, 0x10aef, 1},
{0x10b58, 0x10b5f, 1},
{0x10b78, 0x10b7f, 1},
+ {0x10ba9, 0x10baf, 1},
{0x10e60, 0x10e7e, 1},
{0x11052, 0x1106f, 1},
{0x110f0, 0x110f9, 1},
{0x11136, 0x1113f, 1},
{0x111d0, 0x111d9, 1},
+ {0x111e1, 0x111f4, 1},
+ {0x112f0, 0x112f9, 1},
+ {0x114d0, 0x114d9, 1},
+ {0x11650, 0x11659, 1},
{0x116c0, 0x116c9, 1},
- {0x12400, 0x12462, 1},
+ {0x118e0, 0x118f2, 1},
+ {0x12400, 0x1246e, 1},
+ {0x16a60, 0x16a69, 1},
+ {0x16b50, 0x16b59, 1},
+ {0x16b5b, 0x16b61, 1},
{0x1d360, 0x1d371, 1},
{0x1d7ce, 0x1d7ff, 1},
- {0x1f100, 0x1f10a, 1},
+ {0x1e8c7, 0x1e8cf, 1},
+ {0x1f100, 0x1f10c, 1},
},
LatinOffset: 4,
}
@@ -1924,6 +2125,7 @@ var _Nd = &RangeTable{
{0x0c66, 0x0c6f, 1},
{0x0ce6, 0x0cef, 1},
{0x0d66, 0x0d6f, 1},
+ {0x0de6, 0x0def, 1},
{0x0e50, 0x0e59, 1},
{0x0ed0, 0x0ed9, 1},
{0x0f20, 0x0f29, 1},
@@ -1943,6 +2145,7 @@ var _Nd = &RangeTable{
{0xa8d0, 0xa8d9, 1},
{0xa900, 0xa909, 1},
{0xa9d0, 0xa9d9, 1},
+ {0xa9f0, 0xa9f9, 1},
{0xaa50, 0xaa59, 1},
{0xabf0, 0xabf9, 1},
{0xff10, 0xff19, 1},
@@ -1953,7 +2156,13 @@ var _Nd = &RangeTable{
{0x110f0, 0x110f9, 1},
{0x11136, 0x1113f, 1},
{0x111d0, 0x111d9, 1},
+ {0x112f0, 0x112f9, 1},
+ {0x114d0, 0x114d9, 1},
+ {0x11650, 0x11659, 1},
{0x116c0, 0x116c9, 1},
+ {0x118e0, 0x118e9, 1},
+ {0x16a60, 0x16a69, 1},
+ {0x16b50, 0x16b59, 1},
{0x1d7ce, 0x1d7ff, 1},
},
LatinOffset: 1,
@@ -1973,7 +2182,7 @@ var _Nl = &RangeTable{
{0x10140, 0x10174, 1},
{0x10341, 0x1034a, 9},
{0x103d1, 0x103d5, 1},
- {0x12400, 0x12462, 1},
+ {0x12400, 0x1246e, 1},
},
}
@@ -2010,18 +2219,28 @@ var _No = &RangeTable{
R32: []Range32{
{0x10107, 0x10133, 1},
{0x10175, 0x10178, 1},
- {0x1018a, 0x10320, 406},
- {0x10321, 0x10323, 1},
+ {0x1018a, 0x1018b, 1},
+ {0x102e1, 0x102fb, 1},
+ {0x10320, 0x10323, 1},
{0x10858, 0x1085f, 1},
+ {0x10879, 0x1087f, 1},
+ {0x108a7, 0x108af, 1},
{0x10916, 0x1091b, 1},
{0x10a40, 0x10a47, 1},
{0x10a7d, 0x10a7e, 1},
+ {0x10a9d, 0x10a9f, 1},
+ {0x10aeb, 0x10aef, 1},
{0x10b58, 0x10b5f, 1},
{0x10b78, 0x10b7f, 1},
+ {0x10ba9, 0x10baf, 1},
{0x10e60, 0x10e7e, 1},
{0x11052, 0x11065, 1},
+ {0x111e1, 0x111f4, 1},
+ {0x118ea, 0x118f2, 1},
+ {0x16b5b, 0x16b61, 1},
{0x1d360, 0x1d371, 1},
- {0x1f100, 0x1f10a, 1},
+ {0x1e8c7, 0x1e8cf, 1},
+ {0x1f100, 0x1f10c, 1},
},
LatinOffset: 3,
}
@@ -2104,7 +2323,7 @@ var _P = &RangeTable{
{0x2cfe, 0x2cff, 1},
{0x2d70, 0x2e00, 144},
{0x2e01, 0x2e2e, 1},
- {0x2e30, 0x2e3b, 1},
+ {0x2e30, 0x2e42, 1},
{0x3001, 0x3003, 1},
{0x3008, 0x3011, 1},
{0x3014, 0x301f, 1},
@@ -2144,17 +2363,29 @@ var _P = &RangeTable{
R32: []Range32{
{0x10100, 0x10102, 1},
{0x1039f, 0x103d0, 49},
- {0x10857, 0x1091f, 200},
- {0x1093f, 0x10a50, 273},
- {0x10a51, 0x10a58, 1},
- {0x10a7f, 0x10b39, 186},
- {0x10b3a, 0x10b3f, 1},
+ {0x1056f, 0x10857, 744},
+ {0x1091f, 0x1093f, 32},
+ {0x10a50, 0x10a58, 1},
+ {0x10a7f, 0x10af0, 113},
+ {0x10af1, 0x10af6, 1},
+ {0x10b39, 0x10b3f, 1},
+ {0x10b99, 0x10b9c, 1},
{0x11047, 0x1104d, 1},
{0x110bb, 0x110bc, 1},
{0x110be, 0x110c1, 1},
{0x11140, 0x11143, 1},
+ {0x11174, 0x11175, 1},
{0x111c5, 0x111c8, 1},
- {0x12470, 0x12473, 1},
+ {0x111cd, 0x11238, 107},
+ {0x11239, 0x1123d, 1},
+ {0x114c6, 0x115c1, 251},
+ {0x115c2, 0x115c9, 1},
+ {0x11641, 0x11643, 1},
+ {0x12470, 0x12474, 1},
+ {0x16a6e, 0x16a6f, 1},
+ {0x16af5, 0x16b37, 66},
+ {0x16b38, 0x16b3b, 1},
+ {0x16b44, 0x1bc9f, 20827},
},
LatinOffset: 11,
}
@@ -2177,10 +2408,11 @@ var _Pd = &RangeTable{
{0x2011, 0x2015, 1},
{0x2e17, 0x2e1a, 3},
{0x2e3a, 0x2e3b, 1},
- {0x301c, 0x3030, 20},
- {0x30a0, 0xfe31, 52625},
- {0xfe32, 0xfe58, 38},
- {0xfe63, 0xff0d, 170},
+ {0x2e40, 0x301c, 476},
+ {0x3030, 0x30a0, 112},
+ {0xfe31, 0xfe32, 1},
+ {0xfe58, 0xfe63, 11},
+ {0xff0d, 0xff0d, 1},
},
}
@@ -2202,7 +2434,7 @@ var _Pe = &RangeTable{
{0x3009, 0x3011, 2},
{0x3015, 0x301b, 2},
{0x301e, 0x301f, 1},
- {0xfd3f, 0xfe18, 217},
+ {0xfd3e, 0xfe18, 218},
{0xfe36, 0xfe44, 2},
{0xfe48, 0xfe5a, 18},
{0xfe5c, 0xfe5e, 2},
@@ -2307,7 +2539,9 @@ var _Po = &RangeTable{
{0x2e1f, 0x2e2a, 11},
{0x2e2b, 0x2e2e, 1},
{0x2e30, 0x2e39, 1},
- {0x3001, 0x3003, 1},
+ {0x2e3c, 0x2e3f, 1},
+ {0x2e41, 0x3001, 448},
+ {0x3002, 0x3003, 1},
{0x303d, 0x30fb, 190},
{0xa4fe, 0xa4ff, 1},
{0xa60d, 0xa60f, 1},
@@ -2346,17 +2580,29 @@ var _Po = &RangeTable{
{0x10100, 0x10100, 1},
{0x10101, 0x10102, 1},
{0x1039f, 0x103d0, 49},
- {0x10857, 0x1091f, 200},
- {0x1093f, 0x10a50, 273},
- {0x10a51, 0x10a58, 1},
- {0x10a7f, 0x10b39, 186},
- {0x10b3a, 0x10b3f, 1},
+ {0x1056f, 0x10857, 744},
+ {0x1091f, 0x1093f, 32},
+ {0x10a50, 0x10a58, 1},
+ {0x10a7f, 0x10af0, 113},
+ {0x10af1, 0x10af6, 1},
+ {0x10b39, 0x10b3f, 1},
+ {0x10b99, 0x10b9c, 1},
{0x11047, 0x1104d, 1},
{0x110bb, 0x110bc, 1},
{0x110be, 0x110c1, 1},
{0x11140, 0x11143, 1},
+ {0x11174, 0x11175, 1},
{0x111c5, 0x111c8, 1},
- {0x12470, 0x12473, 1},
+ {0x111cd, 0x11238, 107},
+ {0x11239, 0x1123d, 1},
+ {0x114c6, 0x115c1, 251},
+ {0x115c2, 0x115c9, 1},
+ {0x11641, 0x11643, 1},
+ {0x12470, 0x12474, 1},
+ {0x16a6e, 0x16a6f, 1},
+ {0x16af5, 0x16b37, 66},
+ {0x16b38, 0x16b3b, 1},
+ {0x16b44, 0x1bc9f, 20827},
},
LatinOffset: 8,
}
@@ -2377,9 +2623,10 @@ var _Ps = &RangeTable{
{0x29d8, 0x29da, 2},
{0x29fc, 0x2e22, 1062},
{0x2e24, 0x2e28, 2},
- {0x3008, 0x3010, 2},
+ {0x2e42, 0x3008, 454},
+ {0x300a, 0x3010, 2},
{0x3014, 0x301a, 2},
- {0x301d, 0xfd3e, 52513},
+ {0x301d, 0xfd3f, 52514},
{0xfe17, 0xfe35, 30},
{0xfe37, 0xfe43, 2},
{0xfe47, 0xfe59, 18},
@@ -2410,7 +2657,8 @@ var _S = &RangeTable{
{0x02f0, 0x02ff, 1},
{0x0375, 0x0384, 15},
{0x0385, 0x03f6, 113},
- {0x0482, 0x058f, 269},
+ {0x0482, 0x058d, 267},
+ {0x058e, 0x058f, 1},
{0x0606, 0x0608, 1},
{0x060b, 0x060e, 3},
{0x060f, 0x06de, 207},
@@ -2446,7 +2694,7 @@ var _S = &RangeTable{
{0x2044, 0x2052, 14},
{0x207a, 0x207c, 1},
{0x208a, 0x208c, 1},
- {0x20a0, 0x20ba, 1},
+ {0x20a0, 0x20bd, 1},
{0x2100, 0x2101, 1},
{0x2103, 0x2106, 1},
{0x2108, 0x2109, 1},
@@ -2461,19 +2709,21 @@ var _S = &RangeTable{
{0x214f, 0x2190, 65},
{0x2191, 0x2307, 1},
{0x230c, 0x2328, 1},
- {0x232b, 0x23f3, 1},
+ {0x232b, 0x23fa, 1},
{0x2400, 0x2426, 1},
{0x2440, 0x244a, 1},
{0x249c, 0x24e9, 1},
- {0x2500, 0x26ff, 1},
- {0x2701, 0x2767, 1},
+ {0x2500, 0x2767, 1},
{0x2794, 0x27c4, 1},
{0x27c7, 0x27e5, 1},
{0x27f0, 0x2982, 1},
{0x2999, 0x29d7, 1},
{0x29dc, 0x29fb, 1},
- {0x29fe, 0x2b4c, 1},
- {0x2b50, 0x2b59, 1},
+ {0x29fe, 0x2b73, 1},
+ {0x2b76, 0x2b95, 1},
+ {0x2b98, 0x2bb9, 1},
+ {0x2bbd, 0x2bc8, 1},
+ {0x2bca, 0x2bd1, 1},
{0x2ce5, 0x2cea, 1},
{0x2e80, 0x2e99, 1},
{0x2e9b, 0x2ef3, 1},
@@ -2502,8 +2752,8 @@ var _S = &RangeTable{
{0xa828, 0xa82b, 1},
{0xa836, 0xa839, 1},
{0xaa77, 0xaa79, 1},
- {0xfb29, 0xfbb2, 137},
- {0xfbb3, 0xfbc1, 1},
+ {0xab5b, 0xfb29, 20430},
+ {0xfbb2, 0xfbc1, 1},
{0xfdfc, 0xfdfd, 1},
{0xfe62, 0xfe64, 2},
{0xfe65, 0xfe66, 1},
@@ -2519,8 +2769,14 @@ var _S = &RangeTable{
R32: []Range32{
{0x10137, 0x1013f, 1},
{0x10179, 0x10189, 1},
- {0x10190, 0x1019b, 1},
- {0x101d0, 0x101fc, 1},
+ {0x1018c, 0x10190, 4},
+ {0x10191, 0x1019b, 1},
+ {0x101a0, 0x101d0, 48},
+ {0x101d1, 0x101fc, 1},
+ {0x10877, 0x10878, 1},
+ {0x10ac8, 0x16b3c, 24692},
+ {0x16b3d, 0x16b3f, 1},
+ {0x16b45, 0x1bc9c, 20823},
{0x1d000, 0x1d0f5, 1},
{0x1d100, 0x1d126, 1},
{0x1d129, 0x1d164, 1},
@@ -2540,9 +2796,9 @@ var _S = &RangeTable{
{0x1f000, 0x1f02b, 1},
{0x1f030, 0x1f093, 1},
{0x1f0a0, 0x1f0ae, 1},
- {0x1f0b1, 0x1f0be, 1},
+ {0x1f0b1, 0x1f0bf, 1},
{0x1f0c1, 0x1f0cf, 1},
- {0x1f0d1, 0x1f0df, 1},
+ {0x1f0d1, 0x1f0f5, 1},
{0x1f110, 0x1f12e, 1},
{0x1f130, 0x1f16b, 1},
{0x1f170, 0x1f19a, 1},
@@ -2550,24 +2806,25 @@ var _S = &RangeTable{
{0x1f210, 0x1f23a, 1},
{0x1f240, 0x1f248, 1},
{0x1f250, 0x1f251, 1},
- {0x1f300, 0x1f320, 1},
- {0x1f330, 0x1f335, 1},
- {0x1f337, 0x1f37c, 1},
- {0x1f380, 0x1f393, 1},
- {0x1f3a0, 0x1f3c4, 1},
- {0x1f3c6, 0x1f3ca, 1},
- {0x1f3e0, 0x1f3f0, 1},
- {0x1f400, 0x1f43e, 1},
- {0x1f440, 0x1f442, 2},
- {0x1f443, 0x1f4f7, 1},
- {0x1f4f9, 0x1f4fc, 1},
- {0x1f500, 0x1f53d, 1},
- {0x1f540, 0x1f543, 1},
- {0x1f550, 0x1f567, 1},
- {0x1f5fb, 0x1f640, 1},
- {0x1f645, 0x1f64f, 1},
- {0x1f680, 0x1f6c5, 1},
+ {0x1f300, 0x1f32c, 1},
+ {0x1f330, 0x1f37d, 1},
+ {0x1f380, 0x1f3ce, 1},
+ {0x1f3d4, 0x1f3f7, 1},
+ {0x1f400, 0x1f4fe, 1},
+ {0x1f500, 0x1f54a, 1},
+ {0x1f550, 0x1f579, 1},
+ {0x1f57b, 0x1f5a3, 1},
+ {0x1f5a5, 0x1f642, 1},
+ {0x1f645, 0x1f6cf, 1},
+ {0x1f6e0, 0x1f6ec, 1},
+ {0x1f6f0, 0x1f6f3, 1},
{0x1f700, 0x1f773, 1},
+ {0x1f780, 0x1f7d4, 1},
+ {0x1f800, 0x1f80b, 1},
+ {0x1f810, 0x1f847, 1},
+ {0x1f850, 0x1f859, 1},
+ {0x1f860, 0x1f887, 1},
+ {0x1f890, 0x1f8ad, 1},
},
LatinOffset: 10,
}
@@ -2581,7 +2838,7 @@ var _Sc = &RangeTable{
{0x09fb, 0x0af1, 246},
{0x0bf9, 0x0e3f, 582},
{0x17db, 0x20a0, 2245},
- {0x20a1, 0x20ba, 1},
+ {0x20a1, 0x20bd, 1},
{0xa838, 0xfdfc, 21956},
{0xfe69, 0xff04, 155},
{0xffe0, 0xffe1, 1},
@@ -2611,7 +2868,8 @@ var _Sk = &RangeTable{
{0xa700, 0xa716, 1},
{0xa720, 0xa721, 1},
{0xa789, 0xa78a, 1},
- {0xfbb2, 0xfbc1, 1},
+ {0xab5b, 0xfbb2, 20567},
+ {0xfbb3, 0xfbc1, 1},
{0xff3e, 0xff40, 2},
{0xffe3, 0xffe3, 1},
},
@@ -2679,7 +2937,8 @@ var _So = &RangeTable{
R16: []Range16{
{0x00a6, 0x00a9, 3},
{0x00ae, 0x00b0, 2},
- {0x0482, 0x060e, 396},
+ {0x0482, 0x058d, 267},
+ {0x058e, 0x060e, 128},
{0x060f, 0x06de, 207},
{0x06e9, 0x06fd, 20},
{0x06fe, 0x07f6, 248},
@@ -2728,7 +2987,7 @@ var _So = &RangeTable{
{0x232b, 0x237b, 1},
{0x237d, 0x239a, 1},
{0x23b4, 0x23db, 1},
- {0x23e2, 0x23f3, 1},
+ {0x23e2, 0x23fa, 1},
{0x2400, 0x2426, 1},
{0x2440, 0x244a, 1},
{0x249c, 0x24e9, 1},
@@ -2736,13 +2995,16 @@ var _So = &RangeTable{
{0x25b8, 0x25c0, 1},
{0x25c2, 0x25f7, 1},
{0x2600, 0x266e, 1},
- {0x2670, 0x26ff, 1},
- {0x2701, 0x2767, 1},
+ {0x2670, 0x2767, 1},
{0x2794, 0x27bf, 1},
{0x2800, 0x28ff, 1},
{0x2b00, 0x2b2f, 1},
{0x2b45, 0x2b46, 1},
- {0x2b50, 0x2b59, 1},
+ {0x2b4d, 0x2b73, 1},
+ {0x2b76, 0x2b95, 1},
+ {0x2b98, 0x2bb9, 1},
+ {0x2bbd, 0x2bc8, 1},
+ {0x2bca, 0x2bd1, 1},
{0x2ce5, 0x2cea, 1},
{0x2e80, 0x2e99, 1},
{0x2e9b, 0x2ef3, 1},
@@ -2777,8 +3039,14 @@ var _So = &RangeTable{
{0x10137, 0x10137, 1},
{0x10138, 0x1013f, 1},
{0x10179, 0x10189, 1},
- {0x10190, 0x1019b, 1},
- {0x101d0, 0x101fc, 1},
+ {0x1018c, 0x10190, 4},
+ {0x10191, 0x1019b, 1},
+ {0x101a0, 0x101d0, 48},
+ {0x101d1, 0x101fc, 1},
+ {0x10877, 0x10878, 1},
+ {0x10ac8, 0x16b3c, 24692},
+ {0x16b3d, 0x16b3f, 1},
+ {0x16b45, 0x1bc9c, 20823},
{0x1d000, 0x1d0f5, 1},
{0x1d100, 0x1d126, 1},
{0x1d129, 0x1d164, 1},
@@ -2792,9 +3060,9 @@ var _So = &RangeTable{
{0x1f000, 0x1f02b, 1},
{0x1f030, 0x1f093, 1},
{0x1f0a0, 0x1f0ae, 1},
- {0x1f0b1, 0x1f0be, 1},
+ {0x1f0b1, 0x1f0bf, 1},
{0x1f0c1, 0x1f0cf, 1},
- {0x1f0d1, 0x1f0df, 1},
+ {0x1f0d1, 0x1f0f5, 1},
{0x1f110, 0x1f12e, 1},
{0x1f130, 0x1f16b, 1},
{0x1f170, 0x1f19a, 1},
@@ -2802,24 +3070,25 @@ var _So = &RangeTable{
{0x1f210, 0x1f23a, 1},
{0x1f240, 0x1f248, 1},
{0x1f250, 0x1f251, 1},
- {0x1f300, 0x1f320, 1},
- {0x1f330, 0x1f335, 1},
- {0x1f337, 0x1f37c, 1},
- {0x1f380, 0x1f393, 1},
- {0x1f3a0, 0x1f3c4, 1},
- {0x1f3c6, 0x1f3ca, 1},
- {0x1f3e0, 0x1f3f0, 1},
- {0x1f400, 0x1f43e, 1},
- {0x1f440, 0x1f442, 2},
- {0x1f443, 0x1f4f7, 1},
- {0x1f4f9, 0x1f4fc, 1},
- {0x1f500, 0x1f53d, 1},
- {0x1f540, 0x1f543, 1},
- {0x1f550, 0x1f567, 1},
- {0x1f5fb, 0x1f640, 1},
- {0x1f645, 0x1f64f, 1},
- {0x1f680, 0x1f6c5, 1},
+ {0x1f300, 0x1f32c, 1},
+ {0x1f330, 0x1f37d, 1},
+ {0x1f380, 0x1f3ce, 1},
+ {0x1f3d4, 0x1f3f7, 1},
+ {0x1f400, 0x1f4fe, 1},
+ {0x1f500, 0x1f54a, 1},
+ {0x1f550, 0x1f579, 1},
+ {0x1f57b, 0x1f5a3, 1},
+ {0x1f5a5, 0x1f642, 1},
+ {0x1f645, 0x1f6cf, 1},
+ {0x1f6e0, 0x1f6ec, 1},
+ {0x1f6f0, 0x1f6f3, 1},
{0x1f700, 0x1f773, 1},
+ {0x1f780, 0x1f7d4, 1},
+ {0x1f800, 0x1f80b, 1},
+ {0x1f810, 0x1f847, 1},
+ {0x1f850, 0x1f859, 1},
+ {0x1f860, 0x1f887, 1},
+ {0x1f890, 0x1f8ad, 1},
},
LatinOffset: 2,
}
@@ -2911,7 +3180,7 @@ var (
)
// Generated by running
-// maketables --scripts=all --url=http://www.unicode.org/Public/6.3.0/ucd/
+// maketables --scripts=all --url=http://www.unicode.org/Public/7.0.0/ucd/
// DO NOT EDIT
// Scripts is the set of Unicode script tables.
@@ -2921,6 +3190,7 @@ var Scripts = map[string]*RangeTable{
"Avestan": Avestan,
"Balinese": Balinese,
"Bamum": Bamum,
+ "Bassa_Vah": Bassa_Vah,
"Batak": Batak,
"Bengali": Bengali,
"Bopomofo": Bopomofo,
@@ -2930,6 +3200,7 @@ var Scripts = map[string]*RangeTable{
"Buhid": Buhid,
"Canadian_Aboriginal": Canadian_Aboriginal,
"Carian": Carian,
+ "Caucasian_Albanian": Caucasian_Albanian,
"Chakma": Chakma,
"Cham": Cham,
"Cherokee": Cherokee,
@@ -2940,11 +3211,14 @@ var Scripts = map[string]*RangeTable{
"Cyrillic": Cyrillic,
"Deseret": Deseret,
"Devanagari": Devanagari,
+ "Duployan": Duployan,
"Egyptian_Hieroglyphs": Egyptian_Hieroglyphs,
+ "Elbasan": Elbasan,
"Ethiopic": Ethiopic,
"Georgian": Georgian,
"Glagolitic": Glagolitic,
"Gothic": Gothic,
+ "Grantha": Grantha,
"Greek": Greek,
"Gujarati": Gujarati,
"Gurmukhi": Gurmukhi,
@@ -2964,40 +3238,56 @@ var Scripts = map[string]*RangeTable{
"Kayah_Li": Kayah_Li,
"Kharoshthi": Kharoshthi,
"Khmer": Khmer,
+ "Khojki": Khojki,
+ "Khudawadi": Khudawadi,
"Lao": Lao,
"Latin": Latin,
"Lepcha": Lepcha,
"Limbu": Limbu,
+ "Linear_A": Linear_A,
"Linear_B": Linear_B,
"Lisu": Lisu,
"Lycian": Lycian,
"Lydian": Lydian,
+ "Mahajani": Mahajani,
"Malayalam": Malayalam,
"Mandaic": Mandaic,
+ "Manichaean": Manichaean,
"Meetei_Mayek": Meetei_Mayek,
+ "Mende_Kikakui": Mende_Kikakui,
"Meroitic_Cursive": Meroitic_Cursive,
"Meroitic_Hieroglyphs": Meroitic_Hieroglyphs,
"Miao": Miao,
+ "Modi": Modi,
"Mongolian": Mongolian,
+ "Mro": Mro,
"Myanmar": Myanmar,
+ "Nabataean": Nabataean,
"New_Tai_Lue": New_Tai_Lue,
"Nko": Nko,
"Ogham": Ogham,
"Ol_Chiki": Ol_Chiki,
"Old_Italic": Old_Italic,
+ "Old_North_Arabian": Old_North_Arabian,
+ "Old_Permic": Old_Permic,
"Old_Persian": Old_Persian,
"Old_South_Arabian": Old_South_Arabian,
"Old_Turkic": Old_Turkic,
"Oriya": Oriya,
"Osmanya": Osmanya,
+ "Pahawh_Hmong": Pahawh_Hmong,
+ "Palmyrene": Palmyrene,
+ "Pau_Cin_Hau": Pau_Cin_Hau,
"Phags_Pa": Phags_Pa,
"Phoenician": Phoenician,
+ "Psalter_Pahlavi": Psalter_Pahlavi,
"Rejang": Rejang,
"Runic": Runic,
"Samaritan": Samaritan,
"Saurashtra": Saurashtra,
"Sharada": Sharada,
"Shavian": Shavian,
+ "Siddham": Siddham,
"Sinhala": Sinhala,
"Sora_Sompeng": Sora_Sompeng,
"Sundanese": Sundanese,
@@ -3015,8 +3305,10 @@ var Scripts = map[string]*RangeTable{
"Thai": Thai,
"Tibetan": Tibetan,
"Tifinagh": Tifinagh,
+ "Tirhuta": Tirhuta,
"Ugaritic": Ugaritic,
"Vai": Vai,
+ "Warang_Citi": Warang_Citi,
"Yi": Yi,
}
@@ -3025,7 +3317,6 @@ var _Arabic = &RangeTable{
{0x0600, 0x0604, 1},
{0x0606, 0x060b, 1},
{0x060d, 0x061a, 1},
- {0x061c, 0x061c, 1},
{0x061e, 0x061e, 1},
{0x0620, 0x063f, 1},
{0x0641, 0x064a, 1},
@@ -3034,14 +3325,13 @@ var _Arabic = &RangeTable{
{0x0671, 0x06dc, 1},
{0x06de, 0x06ff, 1},
{0x0750, 0x077f, 1},
- {0x08a0, 0x08a0, 1},
- {0x08a2, 0x08ac, 1},
- {0x08e4, 0x08fe, 1},
+ {0x08a0, 0x08b2, 1},
+ {0x08e4, 0x08ff, 1},
{0xfb50, 0xfbc1, 1},
{0xfbd3, 0xfd3d, 1},
{0xfd50, 0xfd8f, 1},
{0xfd92, 0xfdc7, 1},
- {0xfdf0, 0xfdfc, 1},
+ {0xfdf0, 0xfdfd, 1},
{0xfe70, 0xfe74, 1},
{0xfe76, 0xfefc, 1},
},
@@ -3090,7 +3380,7 @@ var _Armenian = &RangeTable{
{0x0559, 0x055f, 1},
{0x0561, 0x0587, 1},
{0x058a, 0x058a, 1},
- {0x058f, 0x058f, 1},
+ {0x058d, 0x058f, 1},
{0xfb13, 0xfb17, 1},
},
}
@@ -3119,6 +3409,14 @@ var _Bamum = &RangeTable{
},
}
+var _Bassa_Vah = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x16ad0, 0x16aed, 1},
+ {0x16af0, 0x16af5, 1},
+ },
+}
+
var _Batak = &RangeTable{
R16: []Range16{
{0x1bc0, 0x1bf3, 1},
@@ -3128,7 +3426,7 @@ var _Batak = &RangeTable{
var _Bengali = &RangeTable{
R16: []Range16{
- {0x0981, 0x0983, 1},
+ {0x0980, 0x0983, 1},
{0x0985, 0x098c, 1},
{0x098f, 0x0990, 1},
{0x0993, 0x09a8, 1},
@@ -3158,6 +3456,7 @@ var _Brahmi = &RangeTable{
R32: []Range32{
{0x11000, 0x1104d, 1},
{0x11052, 0x1106f, 1},
+ {0x1107f, 0x1107f, 1},
},
}
@@ -3194,6 +3493,14 @@ var _Carian = &RangeTable{
},
}
+var _Caucasian_Albanian = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x10530, 0x10563, 1},
+ {0x1056f, 0x1056f, 1},
+ },
+}
+
var _Chakma = &RangeTable{
R16: []Range16{},
R32: []Range32{
@@ -3234,8 +3541,9 @@ var _Common = &RangeTable{
{0x0385, 0x0385, 1},
{0x0387, 0x0387, 1},
{0x0589, 0x0589, 1},
+ {0x0605, 0x0605, 1},
{0x060c, 0x060c, 1},
- {0x061b, 0x061b, 1},
+ {0x061b, 0x061c, 1},
{0x061f, 0x061f, 1},
{0x0640, 0x0640, 1},
{0x0660, 0x0669, 1},
@@ -3258,21 +3566,23 @@ var _Common = &RangeTable{
{0x2066, 0x2070, 1},
{0x2074, 0x207e, 1},
{0x2080, 0x208e, 1},
- {0x20a0, 0x20ba, 1},
+ {0x20a0, 0x20bd, 1},
{0x2100, 0x2125, 1},
{0x2127, 0x2129, 1},
{0x212c, 0x2131, 1},
{0x2133, 0x214d, 1},
{0x214f, 0x215f, 1},
{0x2189, 0x2189, 1},
- {0x2190, 0x23f3, 1},
+ {0x2190, 0x23fa, 1},
{0x2400, 0x2426, 1},
{0x2440, 0x244a, 1},
- {0x2460, 0x26ff, 1},
- {0x2701, 0x27ff, 1},
- {0x2900, 0x2b4c, 1},
- {0x2b50, 0x2b59, 1},
- {0x2e00, 0x2e3b, 1},
+ {0x2460, 0x27ff, 1},
+ {0x2900, 0x2b73, 1},
+ {0x2b76, 0x2b95, 1},
+ {0x2b98, 0x2bb9, 1},
+ {0x2bbd, 0x2bc8, 1},
+ {0x2bca, 0x2bd1, 1},
+ {0x2e00, 0x2e42, 1},
{0x2ff0, 0x2ffb, 1},
{0x3000, 0x3004, 1},
{0x3006, 0x3006, 1},
@@ -3291,9 +3601,10 @@ var _Common = &RangeTable{
{0xa700, 0xa721, 1},
{0xa788, 0xa78a, 1},
{0xa830, 0xa839, 1},
+ {0xa92e, 0xa92e, 1},
{0xa9cf, 0xa9cf, 1},
+ {0xab5b, 0xab5b, 1},
{0xfd3e, 0xfd3f, 1},
- {0xfdfd, 0xfdfd, 1},
{0xfe10, 0xfe19, 1},
{0xfe30, 0xfe52, 1},
{0xfe54, 0xfe66, 1},
@@ -3314,6 +3625,8 @@ var _Common = &RangeTable{
{0x10137, 0x1013f, 1},
{0x10190, 0x1019b, 1},
{0x101d0, 0x101fc, 1},
+ {0x102e1, 0x102fb, 1},
+ {0x1bca0, 0x1bca3, 1},
{0x1d000, 0x1d0f5, 1},
{0x1d100, 0x1d126, 1},
{0x1d129, 0x1d166, 1},
@@ -3347,10 +3660,10 @@ var _Common = &RangeTable{
{0x1f000, 0x1f02b, 1},
{0x1f030, 0x1f093, 1},
{0x1f0a0, 0x1f0ae, 1},
- {0x1f0b1, 0x1f0be, 1},
+ {0x1f0b1, 0x1f0bf, 1},
{0x1f0c1, 0x1f0cf, 1},
- {0x1f0d1, 0x1f0df, 1},
- {0x1f100, 0x1f10a, 1},
+ {0x1f0d1, 0x1f0f5, 1},
+ {0x1f100, 0x1f10c, 1},
{0x1f110, 0x1f12e, 1},
{0x1f130, 0x1f16b, 1},
{0x1f170, 0x1f19a, 1},
@@ -3359,24 +3672,25 @@ var _Common = &RangeTable{
{0x1f210, 0x1f23a, 1},
{0x1f240, 0x1f248, 1},
{0x1f250, 0x1f251, 1},
- {0x1f300, 0x1f320, 1},
- {0x1f330, 0x1f335, 1},
- {0x1f337, 0x1f37c, 1},
- {0x1f380, 0x1f393, 1},
- {0x1f3a0, 0x1f3c4, 1},
- {0x1f3c6, 0x1f3ca, 1},
- {0x1f3e0, 0x1f3f0, 1},
- {0x1f400, 0x1f43e, 1},
- {0x1f440, 0x1f440, 1},
- {0x1f442, 0x1f4f7, 1},
- {0x1f4f9, 0x1f4fc, 1},
- {0x1f500, 0x1f53d, 1},
- {0x1f540, 0x1f543, 1},
- {0x1f550, 0x1f567, 1},
- {0x1f5fb, 0x1f640, 1},
- {0x1f645, 0x1f64f, 1},
- {0x1f680, 0x1f6c5, 1},
+ {0x1f300, 0x1f32c, 1},
+ {0x1f330, 0x1f37d, 1},
+ {0x1f380, 0x1f3ce, 1},
+ {0x1f3d4, 0x1f3f7, 1},
+ {0x1f400, 0x1f4fe, 1},
+ {0x1f500, 0x1f54a, 1},
+ {0x1f550, 0x1f579, 1},
+ {0x1f57b, 0x1f5a3, 1},
+ {0x1f5a5, 0x1f642, 1},
+ {0x1f645, 0x1f6cf, 1},
+ {0x1f6e0, 0x1f6ec, 1},
+ {0x1f6f0, 0x1f6f3, 1},
{0x1f700, 0x1f773, 1},
+ {0x1f780, 0x1f7d4, 1},
+ {0x1f800, 0x1f80b, 1},
+ {0x1f810, 0x1f847, 1},
+ {0x1f850, 0x1f859, 1},
+ {0x1f860, 0x1f887, 1},
+ {0x1f890, 0x1f8ad, 1},
{0xe0001, 0xe0001, 1},
{0xe0020, 0xe007f, 1},
},
@@ -3394,9 +3708,9 @@ var _Coptic = &RangeTable{
var _Cuneiform = &RangeTable{
R16: []Range16{},
R32: []Range32{
- {0x12000, 0x1236e, 1},
- {0x12400, 0x12462, 1},
- {0x12470, 0x12473, 1},
+ {0x12000, 0x12398, 1},
+ {0x12400, 0x1246e, 1},
+ {0x12470, 0x12474, 1},
},
}
@@ -3415,11 +3729,11 @@ var _Cypriot = &RangeTable{
var _Cyrillic = &RangeTable{
R16: []Range16{
{0x0400, 0x0484, 1},
- {0x0487, 0x0527, 1},
+ {0x0487, 0x052f, 1},
{0x1d2b, 0x1d2b, 1},
{0x1d78, 0x1d78, 1},
{0x2de0, 0x2dff, 1},
- {0xa640, 0xa697, 1},
+ {0xa640, 0xa69d, 1},
{0xa69f, 0xa69f, 1},
},
}
@@ -3435,12 +3749,22 @@ var _Devanagari = &RangeTable{
R16: []Range16{
{0x0900, 0x0950, 1},
{0x0953, 0x0963, 1},
- {0x0966, 0x0977, 1},
- {0x0979, 0x097f, 1},
+ {0x0966, 0x097f, 1},
{0xa8e0, 0xa8fb, 1},
},
}
+var _Duployan = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x1bc00, 0x1bc6a, 1},
+ {0x1bc70, 0x1bc7c, 1},
+ {0x1bc80, 0x1bc88, 1},
+ {0x1bc90, 0x1bc99, 1},
+ {0x1bc9c, 0x1bc9f, 1},
+ },
+}
+
var _Egyptian_Hieroglyphs = &RangeTable{
R16: []Range16{},
R32: []Range32{
@@ -3448,6 +3772,13 @@ var _Egyptian_Hieroglyphs = &RangeTable{
},
}
+var _Elbasan = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x10500, 0x10527, 1},
+ },
+}
+
var _Ethiopic = &RangeTable{
R16: []Range16{
{0x1200, 0x1248, 1},
@@ -3512,11 +3843,32 @@ var _Gothic = &RangeTable{
},
}
+var _Grantha = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x11301, 0x11303, 1},
+ {0x11305, 0x1130c, 1},
+ {0x1130f, 0x11310, 1},
+ {0x11313, 0x11328, 1},
+ {0x1132a, 0x11330, 1},
+ {0x11332, 0x11333, 1},
+ {0x11335, 0x11339, 1},
+ {0x1133c, 0x11344, 1},
+ {0x11347, 0x11348, 1},
+ {0x1134b, 0x1134d, 1},
+ {0x11357, 0x11357, 1},
+ {0x1135d, 0x11363, 1},
+ {0x11366, 0x1136c, 1},
+ {0x11370, 0x11374, 1},
+ },
+}
+
var _Greek = &RangeTable{
R16: []Range16{
{0x0370, 0x0373, 1},
{0x0375, 0x0377, 1},
{0x037a, 0x037d, 1},
+ {0x037f, 0x037f, 1},
{0x0384, 0x0384, 1},
{0x0386, 0x0386, 1},
{0x0388, 0x038a, 1},
@@ -3545,9 +3897,11 @@ var _Greek = &RangeTable{
{0x1ff2, 0x1ff4, 1},
{0x1ff6, 0x1ffe, 1},
{0x2126, 0x2126, 1},
+ {0xab65, 0xab65, 1},
},
R32: []Range32{
- {0x10140, 0x1018a, 1},
+ {0x10140, 0x1018c, 1},
+ {0x101a0, 0x101a0, 1},
{0x1d200, 0x1d245, 1},
},
}
@@ -3678,22 +4032,25 @@ var _Inherited = &RangeTable{
{0x064b, 0x0655, 1},
{0x0670, 0x0670, 1},
{0x0951, 0x0952, 1},
+ {0x1ab0, 0x1abe, 1},
{0x1cd0, 0x1cd2, 1},
{0x1cd4, 0x1ce0, 1},
{0x1ce2, 0x1ce8, 1},
{0x1ced, 0x1ced, 1},
{0x1cf4, 0x1cf4, 1},
- {0x1dc0, 0x1de6, 1},
+ {0x1cf8, 0x1cf9, 1},
+ {0x1dc0, 0x1df5, 1},
{0x1dfc, 0x1dff, 1},
{0x200c, 0x200d, 1},
{0x20d0, 0x20f0, 1},
{0x302a, 0x302d, 1},
{0x3099, 0x309a, 1},
{0xfe00, 0xfe0f, 1},
- {0xfe20, 0xfe26, 1},
+ {0xfe20, 0xfe2d, 1},
},
R32: []Range32{
{0x101fd, 0x101fd, 1},
+ {0x102e0, 0x102e0, 1},
{0x1d167, 0x1d169, 1},
{0x1d17b, 0x1d182, 1},
{0x1d185, 0x1d18b, 1},
@@ -3735,7 +4092,7 @@ var _Kaithi = &RangeTable{
var _Kannada = &RangeTable{
R16: []Range16{
- {0x0c82, 0x0c83, 1},
+ {0x0c81, 0x0c83, 1},
{0x0c85, 0x0c8c, 1},
{0x0c8e, 0x0c90, 1},
{0x0c92, 0x0ca8, 1},
@@ -3769,7 +4126,8 @@ var _Katakana = &RangeTable{
var _Kayah_Li = &RangeTable{
R16: []Range16{
- {0xa900, 0xa92f, 1},
+ {0xa900, 0xa92d, 1},
+ {0xa92f, 0xa92f, 1},
},
}
@@ -3796,6 +4154,22 @@ var _Khmer = &RangeTable{
},
}
+var _Khojki = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x11200, 0x11211, 1},
+ {0x11213, 0x1123d, 1},
+ },
+}
+
+var _Khudawadi = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x112b0, 0x112ea, 1},
+ {0x112f0, 0x112f9, 1},
+ },
+}
+
var _Lao = &RangeTable{
R16: []Range16{
{0x0e81, 0x0e82, 1},
@@ -3845,9 +4219,12 @@ var _Latin = &RangeTable{
{0x2c60, 0x2c7f, 1},
{0xa722, 0xa787, 1},
{0xa78b, 0xa78e, 1},
- {0xa790, 0xa793, 1},
- {0xa7a0, 0xa7aa, 1},
- {0xa7f8, 0xa7ff, 1},
+ {0xa790, 0xa7ad, 1},
+ {0xa7b0, 0xa7b1, 1},
+ {0xa7f7, 0xa7ff, 1},
+ {0xab30, 0xab5a, 1},
+ {0xab5c, 0xab5f, 1},
+ {0xab64, 0xab64, 1},
{0xfb00, 0xfb06, 1},
{0xff21, 0xff3a, 1},
{0xff41, 0xff5a, 1},
@@ -3865,7 +4242,7 @@ var _Lepcha = &RangeTable{
var _Limbu = &RangeTable{
R16: []Range16{
- {0x1900, 0x191c, 1},
+ {0x1900, 0x191e, 1},
{0x1920, 0x192b, 1},
{0x1930, 0x193b, 1},
{0x1940, 0x1940, 1},
@@ -3873,6 +4250,15 @@ var _Limbu = &RangeTable{
},
}
+var _Linear_A = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x10600, 0x10736, 1},
+ {0x10740, 0x10755, 1},
+ {0x10760, 0x10767, 1},
+ },
+}
+
var _Linear_B = &RangeTable{
R16: []Range16{},
R32: []Range32{
@@ -3907,9 +4293,16 @@ var _Lydian = &RangeTable{
},
}
+var _Mahajani = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x11150, 0x11176, 1},
+ },
+}
+
var _Malayalam = &RangeTable{
R16: []Range16{
- {0x0d02, 0x0d03, 1},
+ {0x0d01, 0x0d03, 1},
{0x0d05, 0x0d0c, 1},
{0x0d0e, 0x0d10, 1},
{0x0d12, 0x0d3a, 1},
@@ -3930,6 +4323,14 @@ var _Mandaic = &RangeTable{
},
}
+var _Manichaean = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x10ac0, 0x10ae6, 1},
+ {0x10aeb, 0x10af6, 1},
+ },
+}
+
var _Meetei_Mayek = &RangeTable{
R16: []Range16{
{0xaae0, 0xaaf6, 1},
@@ -3938,6 +4339,14 @@ var _Meetei_Mayek = &RangeTable{
},
}
+var _Mende_Kikakui = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x1e800, 0x1e8c4, 1},
+ {0x1e8c7, 0x1e8d6, 1},
+ },
+}
+
var _Meroitic_Cursive = &RangeTable{
R16: []Range16{},
R32: []Range32{
@@ -3962,6 +4371,14 @@ var _Miao = &RangeTable{
},
}
+var _Modi = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x11600, 0x11644, 1},
+ {0x11650, 0x11659, 1},
+ },
+}
+
var _Mongolian = &RangeTable{
R16: []Range16{
{0x1800, 0x1801, 1},
@@ -3973,10 +4390,28 @@ var _Mongolian = &RangeTable{
},
}
+var _Mro = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x16a40, 0x16a5e, 1},
+ {0x16a60, 0x16a69, 1},
+ {0x16a6e, 0x16a6f, 1},
+ },
+}
+
var _Myanmar = &RangeTable{
R16: []Range16{
{0x1000, 0x109f, 1},
- {0xaa60, 0xaa7b, 1},
+ {0xa9e0, 0xa9fe, 1},
+ {0xaa60, 0xaa7f, 1},
+ },
+}
+
+var _Nabataean = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x10880, 0x1089e, 1},
+ {0x108a7, 0x108af, 1},
},
}
@@ -4010,8 +4445,21 @@ var _Ol_Chiki = &RangeTable{
var _Old_Italic = &RangeTable{
R16: []Range16{},
R32: []Range32{
- {0x10300, 0x1031e, 1},
- {0x10320, 0x10323, 1},
+ {0x10300, 0x10323, 1},
+ },
+}
+
+var _Old_North_Arabian = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x10a80, 0x10a9f, 1},
+ },
+}
+
+var _Old_Permic = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x10350, 0x1037a, 1},
},
}
@@ -4064,6 +4512,31 @@ var _Osmanya = &RangeTable{
},
}
+var _Pahawh_Hmong = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x16b00, 0x16b45, 1},
+ {0x16b50, 0x16b59, 1},
+ {0x16b5b, 0x16b61, 1},
+ {0x16b63, 0x16b77, 1},
+ {0x16b7d, 0x16b8f, 1},
+ },
+}
+
+var _Palmyrene = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x10860, 0x1087f, 1},
+ },
+}
+
+var _Pau_Cin_Hau = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x11ac0, 0x11af8, 1},
+ },
+}
+
var _Phags_Pa = &RangeTable{
R16: []Range16{
{0xa840, 0xa877, 1},
@@ -4078,6 +4551,15 @@ var _Phoenician = &RangeTable{
},
}
+var _Psalter_Pahlavi = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x10b80, 0x10b91, 1},
+ {0x10b99, 0x10b9c, 1},
+ {0x10ba9, 0x10baf, 1},
+ },
+}
+
var _Rejang = &RangeTable{
R16: []Range16{
{0xa930, 0xa953, 1},
@@ -4088,7 +4570,7 @@ var _Rejang = &RangeTable{
var _Runic = &RangeTable{
R16: []Range16{
{0x16a0, 0x16ea, 1},
- {0x16ee, 0x16f0, 1},
+ {0x16ee, 0x16f8, 1},
},
}
@@ -4110,7 +4592,8 @@ var _Sharada = &RangeTable{
R16: []Range16{},
R32: []Range32{
{0x11180, 0x111c8, 1},
- {0x111d0, 0x111d9, 1},
+ {0x111cd, 0x111cd, 1},
+ {0x111d0, 0x111da, 1},
},
}
@@ -4121,6 +4604,14 @@ var _Shavian = &RangeTable{
},
}
+var _Siddham = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x11580, 0x115b5, 1},
+ {0x115b8, 0x115c9, 1},
+ },
+}
+
var _Sinhala = &RangeTable{
R16: []Range16{
{0x0d82, 0x0d83, 1},
@@ -4133,8 +4624,12 @@ var _Sinhala = &RangeTable{
{0x0dcf, 0x0dd4, 1},
{0x0dd6, 0x0dd6, 1},
{0x0dd8, 0x0ddf, 1},
+ {0x0de6, 0x0def, 1},
{0x0df2, 0x0df4, 1},
},
+ R32: []Range32{
+ {0x111e1, 0x111f4, 1},
+ },
}
var _Sora_Sompeng = &RangeTable{
@@ -4236,12 +4731,11 @@ var _Tamil = &RangeTable{
var _Telugu = &RangeTable{
R16: []Range16{
- {0x0c01, 0x0c03, 1},
+ {0x0c00, 0x0c03, 1},
{0x0c05, 0x0c0c, 1},
{0x0c0e, 0x0c10, 1},
{0x0c12, 0x0c28, 1},
- {0x0c2a, 0x0c33, 1},
- {0x0c35, 0x0c39, 1},
+ {0x0c2a, 0x0c39, 1},
{0x0c3d, 0x0c44, 1},
{0x0c46, 0x0c48, 1},
{0x0c4a, 0x0c4d, 1},
@@ -4286,6 +4780,14 @@ var _Tifinagh = &RangeTable{
},
}
+var _Tirhuta = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x11480, 0x114c7, 1},
+ {0x114d0, 0x114d9, 1},
+ },
+}
+
var _Ugaritic = &RangeTable{
R16: []Range16{},
R32: []Range32{
@@ -4300,6 +4802,14 @@ var _Vai = &RangeTable{
},
}
+var _Warang_Citi = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x118a0, 0x118f2, 1},
+ {0x118ff, 0x118ff, 1},
+ },
+}
+
var _Yi = &RangeTable{
R16: []Range16{
{0xa000, 0xa48c, 1},
@@ -4314,6 +4824,7 @@ var (
Avestan = _Avestan // Avestan is the set of Unicode characters in script Avestan.
Balinese = _Balinese // Balinese is the set of Unicode characters in script Balinese.
Bamum = _Bamum // Bamum is the set of Unicode characters in script Bamum.
+ Bassa_Vah = _Bassa_Vah // Bassa_Vah is the set of Unicode characters in script Bassa_Vah.
Batak = _Batak // Batak is the set of Unicode characters in script Batak.
Bengali = _Bengali // Bengali is the set of Unicode characters in script Bengali.
Bopomofo = _Bopomofo // Bopomofo is the set of Unicode characters in script Bopomofo.
@@ -4323,6 +4834,7 @@ var (
Buhid = _Buhid // Buhid is the set of Unicode characters in script Buhid.
Canadian_Aboriginal = _Canadian_Aboriginal // Canadian_Aboriginal is the set of Unicode characters in script Canadian_Aboriginal.
Carian = _Carian // Carian is the set of Unicode characters in script Carian.
+ Caucasian_Albanian = _Caucasian_Albanian // Caucasian_Albanian is the set of Unicode characters in script Caucasian_Albanian.
Chakma = _Chakma // Chakma is the set of Unicode characters in script Chakma.
Cham = _Cham // Cham is the set of Unicode characters in script Cham.
Cherokee = _Cherokee // Cherokee is the set of Unicode characters in script Cherokee.
@@ -4333,11 +4845,14 @@ var (
Cyrillic = _Cyrillic // Cyrillic is the set of Unicode characters in script Cyrillic.
Deseret = _Deseret // Deseret is the set of Unicode characters in script Deseret.
Devanagari = _Devanagari // Devanagari is the set of Unicode characters in script Devanagari.
+ Duployan = _Duployan // Duployan is the set of Unicode characters in script Duployan.
Egyptian_Hieroglyphs = _Egyptian_Hieroglyphs // Egyptian_Hieroglyphs is the set of Unicode characters in script Egyptian_Hieroglyphs.
+ Elbasan = _Elbasan // Elbasan is the set of Unicode characters in script Elbasan.
Ethiopic = _Ethiopic // Ethiopic is the set of Unicode characters in script Ethiopic.
Georgian = _Georgian // Georgian is the set of Unicode characters in script Georgian.
Glagolitic = _Glagolitic // Glagolitic is the set of Unicode characters in script Glagolitic.
Gothic = _Gothic // Gothic is the set of Unicode characters in script Gothic.
+ Grantha = _Grantha // Grantha is the set of Unicode characters in script Grantha.
Greek = _Greek // Greek is the set of Unicode characters in script Greek.
Gujarati = _Gujarati // Gujarati is the set of Unicode characters in script Gujarati.
Gurmukhi = _Gurmukhi // Gurmukhi is the set of Unicode characters in script Gurmukhi.
@@ -4357,40 +4872,56 @@ var (
Kayah_Li = _Kayah_Li // Kayah_Li is the set of Unicode characters in script Kayah_Li.
Kharoshthi = _Kharoshthi // Kharoshthi is the set of Unicode characters in script Kharoshthi.
Khmer = _Khmer // Khmer is the set of Unicode characters in script Khmer.
+ Khojki = _Khojki // Khojki is the set of Unicode characters in script Khojki.
+ Khudawadi = _Khudawadi // Khudawadi is the set of Unicode characters in script Khudawadi.
Lao = _Lao // Lao is the set of Unicode characters in script Lao.
Latin = _Latin // Latin is the set of Unicode characters in script Latin.
Lepcha = _Lepcha // Lepcha is the set of Unicode characters in script Lepcha.
Limbu = _Limbu // Limbu is the set of Unicode characters in script Limbu.
+ Linear_A = _Linear_A // Linear_A is the set of Unicode characters in script Linear_A.
Linear_B = _Linear_B // Linear_B is the set of Unicode characters in script Linear_B.
Lisu = _Lisu // Lisu is the set of Unicode characters in script Lisu.
Lycian = _Lycian // Lycian is the set of Unicode characters in script Lycian.
Lydian = _Lydian // Lydian is the set of Unicode characters in script Lydian.
+ Mahajani = _Mahajani // Mahajani is the set of Unicode characters in script Mahajani.
Malayalam = _Malayalam // Malayalam is the set of Unicode characters in script Malayalam.
Mandaic = _Mandaic // Mandaic is the set of Unicode characters in script Mandaic.
+ Manichaean = _Manichaean // Manichaean is the set of Unicode characters in script Manichaean.
Meetei_Mayek = _Meetei_Mayek // Meetei_Mayek is the set of Unicode characters in script Meetei_Mayek.
+ Mende_Kikakui = _Mende_Kikakui // Mende_Kikakui is the set of Unicode characters in script Mende_Kikakui.
Meroitic_Cursive = _Meroitic_Cursive // Meroitic_Cursive is the set of Unicode characters in script Meroitic_Cursive.
Meroitic_Hieroglyphs = _Meroitic_Hieroglyphs // Meroitic_Hieroglyphs is the set of Unicode characters in script Meroitic_Hieroglyphs.
Miao = _Miao // Miao is the set of Unicode characters in script Miao.
+ Modi = _Modi // Modi is the set of Unicode characters in script Modi.
Mongolian = _Mongolian // Mongolian is the set of Unicode characters in script Mongolian.
+ Mro = _Mro // Mro is the set of Unicode characters in script Mro.
Myanmar = _Myanmar // Myanmar is the set of Unicode characters in script Myanmar.
+ Nabataean = _Nabataean // Nabataean is the set of Unicode characters in script Nabataean.
New_Tai_Lue = _New_Tai_Lue // New_Tai_Lue is the set of Unicode characters in script New_Tai_Lue.
Nko = _Nko // Nko is the set of Unicode characters in script Nko.
Ogham = _Ogham // Ogham is the set of Unicode characters in script Ogham.
Ol_Chiki = _Ol_Chiki // Ol_Chiki is the set of Unicode characters in script Ol_Chiki.
Old_Italic = _Old_Italic // Old_Italic is the set of Unicode characters in script Old_Italic.
+ Old_North_Arabian = _Old_North_Arabian // Old_North_Arabian is the set of Unicode characters in script Old_North_Arabian.
+ Old_Permic = _Old_Permic // Old_Permic is the set of Unicode characters in script Old_Permic.
Old_Persian = _Old_Persian // Old_Persian is the set of Unicode characters in script Old_Persian.
Old_South_Arabian = _Old_South_Arabian // Old_South_Arabian is the set of Unicode characters in script Old_South_Arabian.
Old_Turkic = _Old_Turkic // Old_Turkic is the set of Unicode characters in script Old_Turkic.
Oriya = _Oriya // Oriya is the set of Unicode characters in script Oriya.
Osmanya = _Osmanya // Osmanya is the set of Unicode characters in script Osmanya.
+ Pahawh_Hmong = _Pahawh_Hmong // Pahawh_Hmong is the set of Unicode characters in script Pahawh_Hmong.
+ Palmyrene = _Palmyrene // Palmyrene is the set of Unicode characters in script Palmyrene.
+ Pau_Cin_Hau = _Pau_Cin_Hau // Pau_Cin_Hau is the set of Unicode characters in script Pau_Cin_Hau.
Phags_Pa = _Phags_Pa // Phags_Pa is the set of Unicode characters in script Phags_Pa.
Phoenician = _Phoenician // Phoenician is the set of Unicode characters in script Phoenician.
+ Psalter_Pahlavi = _Psalter_Pahlavi // Psalter_Pahlavi is the set of Unicode characters in script Psalter_Pahlavi.
Rejang = _Rejang // Rejang is the set of Unicode characters in script Rejang.
Runic = _Runic // Runic is the set of Unicode characters in script Runic.
Samaritan = _Samaritan // Samaritan is the set of Unicode characters in script Samaritan.
Saurashtra = _Saurashtra // Saurashtra is the set of Unicode characters in script Saurashtra.
Sharada = _Sharada // Sharada is the set of Unicode characters in script Sharada.
Shavian = _Shavian // Shavian is the set of Unicode characters in script Shavian.
+ Siddham = _Siddham // Siddham is the set of Unicode characters in script Siddham.
Sinhala = _Sinhala // Sinhala is the set of Unicode characters in script Sinhala.
Sora_Sompeng = _Sora_Sompeng // Sora_Sompeng is the set of Unicode characters in script Sora_Sompeng.
Sundanese = _Sundanese // Sundanese is the set of Unicode characters in script Sundanese.
@@ -4408,13 +4939,15 @@ var (
Thai = _Thai // Thai is the set of Unicode characters in script Thai.
Tibetan = _Tibetan // Tibetan is the set of Unicode characters in script Tibetan.
Tifinagh = _Tifinagh // Tifinagh is the set of Unicode characters in script Tifinagh.
+ Tirhuta = _Tirhuta // Tirhuta is the set of Unicode characters in script Tirhuta.
Ugaritic = _Ugaritic // Ugaritic is the set of Unicode characters in script Ugaritic.
Vai = _Vai // Vai is the set of Unicode characters in script Vai.
+ Warang_Citi = _Warang_Citi // Warang_Citi is the set of Unicode characters in script Warang_Citi.
Yi = _Yi // Yi is the set of Unicode characters in script Yi.
)
// Generated by running
-// maketables --props=all --url=http://www.unicode.org/Public/6.3.0/ucd/
+// maketables --props=all --url=http://www.unicode.org/Public/7.0.0/ucd/
// DO NOT EDIT
// Properties is the set of Unicode property tables.
@@ -4486,6 +5019,7 @@ var _Dash = &RangeTable{
{0x2e17, 0x2e17, 1},
{0x2e1a, 0x2e1a, 1},
{0x2e3a, 0x2e3b, 1},
+ {0x2e40, 0x2e40, 1},
{0x301c, 0x301c, 1},
{0x3030, 0x3030, 1},
{0x30a0, 0x30a0, 1},
@@ -4583,6 +5117,7 @@ var _Diacritic = &RangeTable{
{0x1939, 0x193b, 1},
{0x1a75, 0x1a7c, 1},
{0x1a7f, 0x1a7f, 1},
+ {0x1ab0, 0x1abd, 1},
{0x1b34, 0x1b34, 1},
{0x1b44, 0x1b44, 1},
{0x1b6b, 0x1b73, 1},
@@ -4592,8 +5127,10 @@ var _Diacritic = &RangeTable{
{0x1cd0, 0x1ce8, 1},
{0x1ced, 0x1ced, 1},
{0x1cf4, 0x1cf4, 1},
+ {0x1cf8, 0x1cf9, 1},
{0x1d2c, 0x1d6a, 1},
{0x1dc4, 0x1dcf, 1},
+ {0x1df5, 0x1df5, 1},
{0x1dfd, 0x1dff, 1},
{0x1fbd, 0x1fbd, 1},
{0x1fbf, 0x1fc1, 1},
@@ -4609,6 +5146,7 @@ var _Diacritic = &RangeTable{
{0xa66f, 0xa66f, 1},
{0xa67c, 0xa67d, 1},
{0xa67f, 0xa67f, 1},
+ {0xa69c, 0xa69d, 1},
{0xa6f0, 0xa6f1, 1},
{0xa717, 0xa721, 1},
{0xa788, 0xa788, 1},
@@ -4619,12 +5157,14 @@ var _Diacritic = &RangeTable{
{0xa953, 0xa953, 1},
{0xa9b3, 0xa9b3, 1},
{0xa9c0, 0xa9c0, 1},
- {0xaa7b, 0xaa7b, 1},
+ {0xa9e5, 0xa9e5, 1},
+ {0xaa7b, 0xaa7d, 1},
{0xaabf, 0xaac2, 1},
{0xaaf6, 0xaaf6, 1},
+ {0xab5b, 0xab5f, 1},
{0xabec, 0xabed, 1},
{0xfb1e, 0xfb1e, 1},
- {0xfe20, 0xfe26, 1},
+ {0xfe20, 0xfe2d, 1},
{0xff3e, 0xff3e, 1},
{0xff40, 0xff40, 1},
{0xff70, 0xff70, 1},
@@ -4632,16 +5172,30 @@ var _Diacritic = &RangeTable{
{0xffe3, 0xffe3, 1},
},
R32: []Range32{
+ {0x102e0, 0x102e0, 1},
+ {0x10ae5, 0x10ae6, 1},
{0x110b9, 0x110ba, 1},
{0x11133, 0x11134, 1},
+ {0x11173, 0x11173, 1},
{0x111c0, 0x111c0, 1},
+ {0x11235, 0x11236, 1},
+ {0x112e9, 0x112ea, 1},
+ {0x1133c, 0x1133c, 1},
+ {0x1134d, 0x1134d, 1},
+ {0x11366, 0x1136c, 1},
+ {0x11370, 0x11374, 1},
+ {0x114c2, 0x114c3, 1},
+ {0x115bf, 0x115c0, 1},
+ {0x1163f, 0x1163f, 1},
{0x116b6, 0x116b7, 1},
+ {0x16af0, 0x16af4, 1},
{0x16f8f, 0x16f9f, 1},
{0x1d167, 0x1d169, 1},
{0x1d16d, 0x1d172, 1},
{0x1d17b, 0x1d182, 1},
{0x1d185, 0x1d18b, 1},
{0x1d1aa, 0x1d1ad, 1},
+ {0x1e8d0, 0x1e8d6, 1},
},
LatinOffset: 6,
}
@@ -4666,11 +5220,17 @@ var _Extender = &RangeTable{
{0xa015, 0xa015, 1},
{0xa60c, 0xa60c, 1},
{0xa9cf, 0xa9cf, 1},
+ {0xa9e6, 0xa9e6, 1},
{0xaa70, 0xaa70, 1},
{0xaadd, 0xaadd, 1},
{0xaaf3, 0xaaf4, 1},
{0xff70, 0xff70, 1},
},
+ R32: []Range32{
+ {0x1135d, 0x1135d, 1},
+ {0x115c6, 0x115c8, 1},
+ {0x16b42, 0x16b43, 1},
+ },
LatinOffset: 1,
}
@@ -4798,8 +5358,7 @@ var _Other_Alphabetic = &RangeTable{
{0x0825, 0x0827, 1},
{0x0829, 0x082c, 1},
{0x08e4, 0x08e9, 1},
- {0x08f0, 0x08fe, 1},
- {0x0900, 0x0903, 1},
+ {0x08f0, 0x0903, 1},
{0x093a, 0x093b, 1},
{0x093e, 0x094c, 1},
{0x094e, 0x094f, 1},
@@ -4834,19 +5393,19 @@ var _Other_Alphabetic = &RangeTable{
{0x0bc6, 0x0bc8, 1},
{0x0bca, 0x0bcc, 1},
{0x0bd7, 0x0bd7, 1},
- {0x0c01, 0x0c03, 1},
+ {0x0c00, 0x0c03, 1},
{0x0c3e, 0x0c44, 1},
{0x0c46, 0x0c48, 1},
{0x0c4a, 0x0c4c, 1},
{0x0c55, 0x0c56, 1},
{0x0c62, 0x0c63, 1},
- {0x0c82, 0x0c83, 1},
+ {0x0c81, 0x0c83, 1},
{0x0cbe, 0x0cc4, 1},
{0x0cc6, 0x0cc8, 1},
{0x0cca, 0x0ccc, 1},
{0x0cd5, 0x0cd6, 1},
{0x0ce2, 0x0ce3, 1},
- {0x0d02, 0x0d03, 1},
+ {0x0d01, 0x0d03, 1},
{0x0d3e, 0x0d44, 1},
{0x0d46, 0x0d48, 1},
{0x0d4a, 0x0d4c, 1},
@@ -4899,6 +5458,7 @@ var _Other_Alphabetic = &RangeTable{
{0x1be7, 0x1bf1, 1},
{0x1c24, 0x1c35, 1},
{0x1cf2, 0x1cf3, 1},
+ {0x1de7, 0x1df4, 1},
{0x24b6, 0x24e9, 1},
{0x2de0, 0x2dff, 1},
{0xa674, 0xa67b, 1},
@@ -4923,6 +5483,7 @@ var _Other_Alphabetic = &RangeTable{
{0xfb1e, 0xfb1e, 1},
},
R32: []Range32{
+ {0x10376, 0x1037a, 1},
{0x10a01, 0x10a03, 1},
{0x10a05, 0x10a06, 1},
{0x10a0c, 0x10a0f, 1},
@@ -4934,8 +5495,27 @@ var _Other_Alphabetic = &RangeTable{
{0x11127, 0x11132, 1},
{0x11180, 0x11182, 1},
{0x111b3, 0x111bf, 1},
+ {0x1122c, 0x11234, 1},
+ {0x11237, 0x11237, 1},
+ {0x112df, 0x112e8, 1},
+ {0x11301, 0x11303, 1},
+ {0x1133e, 0x11344, 1},
+ {0x11347, 0x11348, 1},
+ {0x1134b, 0x1134c, 1},
+ {0x11357, 0x11357, 1},
+ {0x11362, 0x11363, 1},
+ {0x114b0, 0x114c1, 1},
+ {0x115af, 0x115b5, 1},
+ {0x115b8, 0x115be, 1},
+ {0x11630, 0x1163e, 1},
+ {0x11640, 0x11640, 1},
{0x116ab, 0x116b5, 1},
+ {0x16b30, 0x16b36, 1},
{0x16f51, 0x16f7e, 1},
+ {0x1bc9e, 0x1bc9e, 1},
+ {0x1f130, 0x1f149, 1},
+ {0x1f150, 0x1f169, 1},
+ {0x1f170, 0x1f189, 1},
},
}
@@ -4976,6 +5556,11 @@ var _Other_Grapheme_Extend = &RangeTable{
{0xff9e, 0xff9f, 1},
},
R32: []Range32{
+ {0x1133e, 0x1133e, 1},
+ {0x11357, 0x11357, 1},
+ {0x114b0, 0x114b0, 1},
+ {0x114bd, 0x114bd, 1},
+ {0x115af, 0x115af, 1},
{0x1d165, 0x1d165, 1},
{0x1d16e, 0x1d172, 1},
},
@@ -5017,8 +5602,10 @@ var _Other_Lowercase = &RangeTable{
{0x2170, 0x217f, 1},
{0x24d0, 0x24e9, 1},
{0x2c7c, 0x2c7d, 1},
+ {0xa69c, 0xa69d, 1},
{0xa770, 0xa770, 1},
{0xa7f8, 0xa7f9, 1},
+ {0xab5c, 0xab5f, 1},
},
LatinOffset: 2,
}
@@ -5170,6 +5757,11 @@ var _Other_Uppercase = &RangeTable{
{0x2160, 0x216f, 1},
{0x24b6, 0x24cf, 1},
},
+ R32: []Range32{
+ {0x1f130, 0x1f149, 1},
+ {0x1f150, 0x1f169, 1},
+ {0x1f170, 0x1f189, 1},
+ },
}
var _Pattern_Syntax = &RangeTable{
@@ -5225,6 +5817,7 @@ var _Quotation_Mark = &RangeTable{
{0x00bb, 0x00bb, 1},
{0x2018, 0x201f, 1},
{0x2039, 0x203a, 1},
+ {0x2e42, 0x2e42, 1},
{0x300c, 0x300f, 1},
{0x301d, 0x301f, 1},
{0xfe41, 0xfe44, 1},
@@ -5248,8 +5841,6 @@ var _STerm = &RangeTable{
{0x0021, 0x0021, 1},
{0x002e, 0x002e, 1},
{0x003f, 0x003f, 1},
- {0x055c, 0x055c, 1},
- {0x055e, 0x055e, 1},
{0x0589, 0x0589, 1},
{0x061f, 0x061f, 1},
{0x06d4, 0x06d4, 1},
@@ -5272,6 +5863,7 @@ var _STerm = &RangeTable{
{0x203c, 0x203d, 1},
{0x2047, 0x2049, 1},
{0x2e2e, 0x2e2e, 1},
+ {0x2e3c, 0x2e3c, 1},
{0x3002, 0x3002, 1},
{0xa4ff, 0xa4ff, 1},
{0xa60e, 0xa60f, 1},
@@ -5297,6 +5889,17 @@ var _STerm = &RangeTable{
{0x110be, 0x110c1, 1},
{0x11141, 0x11143, 1},
{0x111c5, 0x111c6, 1},
+ {0x111cd, 0x111cd, 1},
+ {0x11238, 0x11239, 1},
+ {0x1123b, 0x1123c, 1},
+ {0x115c2, 0x115c3, 1},
+ {0x115c9, 0x115c9, 1},
+ {0x11641, 0x11642, 1},
+ {0x16a6e, 0x16a6f, 1},
+ {0x16af5, 0x16af5, 1},
+ {0x16b37, 0x16b38, 1},
+ {0x16b44, 0x16b44, 1},
+ {0x1bc9f, 0x1bc9f, 1},
},
LatinOffset: 3,
}
@@ -5368,6 +5971,7 @@ var _Terminal_Punctuation = &RangeTable{
{0x1361, 0x1368, 1},
{0x166d, 0x166e, 1},
{0x16eb, 0x16ed, 1},
+ {0x1735, 0x1736, 1},
{0x17d4, 0x17d6, 1},
{0x17da, 0x17da, 1},
{0x1802, 0x1805, 1},
@@ -5381,6 +5985,8 @@ var _Terminal_Punctuation = &RangeTable{
{0x203c, 0x203d, 1},
{0x2047, 0x2049, 1},
{0x2e2e, 0x2e2e, 1},
+ {0x2e3c, 0x2e3c, 1},
+ {0x2e41, 0x2e41, 1},
{0x3001, 0x3002, 1},
{0xa4fe, 0xa4ff, 1},
{0xa60d, 0xa60f, 1},
@@ -5408,12 +6014,25 @@ var _Terminal_Punctuation = &RangeTable{
{0x103d0, 0x103d0, 1},
{0x10857, 0x10857, 1},
{0x1091f, 0x1091f, 1},
+ {0x10a56, 0x10a57, 1},
+ {0x10af0, 0x10af5, 1},
{0x10b3a, 0x10b3f, 1},
+ {0x10b99, 0x10b9c, 1},
{0x11047, 0x1104d, 1},
{0x110be, 0x110c1, 1},
{0x11141, 0x11143, 1},
{0x111c5, 0x111c6, 1},
- {0x12470, 0x12473, 1},
+ {0x111cd, 0x111cd, 1},
+ {0x11238, 0x1123c, 1},
+ {0x115c2, 0x115c5, 1},
+ {0x115c9, 0x115c9, 1},
+ {0x11641, 0x11642, 1},
+ {0x12470, 0x12474, 1},
+ {0x16a6e, 0x16a6f, 1},
+ {0x16af5, 0x16af5, 1},
+ {0x16b37, 0x16b39, 1},
+ {0x16b44, 0x16b44, 1},
+ {0x1bc9f, 0x1bc9f, 1},
},
LatinOffset: 5,
}
@@ -5500,7 +6119,7 @@ var (
)
// Generated by running
-// maketables --data=http://www.unicode.org/Public/6.3.0/ucd/UnicodeData.txt --casefolding=http://www.unicode.org/Public/6.3.0/ucd/CaseFolding.txt
+// maketables --data=http://www.unicode.org/Public/7.0.0/ucd/UnicodeData.txt --casefolding=http://www.unicode.org/Public/7.0.0/ucd/CaseFolding.txt
// DO NOT EDIT
// CaseRanges is the table describing case mappings for all letters with
@@ -5598,13 +6217,16 @@ var _CaseRanges = []CaseRange{
{0x0256, 0x0257, d{-205, 0, -205}},
{0x0259, 0x0259, d{-202, 0, -202}},
{0x025B, 0x025B, d{-203, 0, -203}},
+ {0x025C, 0x025C, d{42319, 0, 42319}},
{0x0260, 0x0260, d{-205, 0, -205}},
+ {0x0261, 0x0261, d{42315, 0, 42315}},
{0x0263, 0x0263, d{-207, 0, -207}},
{0x0265, 0x0265, d{42280, 0, 42280}},
{0x0266, 0x0266, d{42308, 0, 42308}},
{0x0268, 0x0268, d{-209, 0, -209}},
{0x0269, 0x0269, d{-211, 0, -211}},
{0x026B, 0x026B, d{10743, 0, 10743}},
+ {0x026C, 0x026C, d{42305, 0, 42305}},
{0x026F, 0x026F, d{-211, 0, -211}},
{0x0271, 0x0271, d{10749, 0, 10749}},
{0x0272, 0x0272, d{-213, 0, -213}},
@@ -5612,15 +6234,18 @@ var _CaseRanges = []CaseRange{
{0x027D, 0x027D, d{10727, 0, 10727}},
{0x0280, 0x0280, d{-218, 0, -218}},
{0x0283, 0x0283, d{-218, 0, -218}},
+ {0x0287, 0x0287, d{42282, 0, 42282}},
{0x0288, 0x0288, d{-218, 0, -218}},
{0x0289, 0x0289, d{-69, 0, -69}},
{0x028A, 0x028B, d{-217, 0, -217}},
{0x028C, 0x028C, d{-71, 0, -71}},
{0x0292, 0x0292, d{-219, 0, -219}},
+ {0x029E, 0x029E, d{42258, 0, 42258}},
{0x0345, 0x0345, d{84, 0, 84}},
{0x0370, 0x0373, d{UpperLower, UpperLower, UpperLower}},
{0x0376, 0x0377, d{UpperLower, UpperLower, UpperLower}},
{0x037B, 0x037D, d{130, 0, 130}},
+ {0x037F, 0x037F, d{0, 116, 0}},
{0x0386, 0x0386, d{0, 38, 0}},
{0x0388, 0x038A, d{0, 37, 0}},
{0x038C, 0x038C, d{0, 64, 0}},
@@ -5644,6 +6269,7 @@ var _CaseRanges = []CaseRange{
{0x03F0, 0x03F0, d{-86, 0, -86}},
{0x03F1, 0x03F1, d{-80, 0, -80}},
{0x03F2, 0x03F2, d{7, 0, 7}},
+ {0x03F3, 0x03F3, d{-116, 0, -116}},
{0x03F4, 0x03F4, d{0, -60, 0}},
{0x03F5, 0x03F5, d{-96, 0, -96}},
{0x03F7, 0x03F8, d{UpperLower, UpperLower, UpperLower}},
@@ -5659,7 +6285,7 @@ var _CaseRanges = []CaseRange{
{0x04C0, 0x04C0, d{0, 15, 0}},
{0x04C1, 0x04CE, d{UpperLower, UpperLower, UpperLower}},
{0x04CF, 0x04CF, d{-15, 0, -15}},
- {0x04D0, 0x0527, d{UpperLower, UpperLower, UpperLower}},
+ {0x04D0, 0x052F, d{UpperLower, UpperLower, UpperLower}},
{0x0531, 0x0556, d{0, 48, 0}},
{0x0561, 0x0586, d{-48, 0, -48}},
{0x10A0, 0x10C5, d{0, 7264, 0}},
@@ -5757,7 +6383,7 @@ var _CaseRanges = []CaseRange{
{0x2D27, 0x2D27, d{-7264, 0, -7264}},
{0x2D2D, 0x2D2D, d{-7264, 0, -7264}},
{0xA640, 0xA66D, d{UpperLower, UpperLower, UpperLower}},
- {0xA680, 0xA697, d{UpperLower, UpperLower, UpperLower}},
+ {0xA680, 0xA69B, d{UpperLower, UpperLower, UpperLower}},
{0xA722, 0xA72F, d{UpperLower, UpperLower, UpperLower}},
{0xA732, 0xA76F, d{UpperLower, UpperLower, UpperLower}},
{0xA779, 0xA77C, d{UpperLower, UpperLower, UpperLower}},
@@ -5766,12 +6392,19 @@ var _CaseRanges = []CaseRange{
{0xA78B, 0xA78C, d{UpperLower, UpperLower, UpperLower}},
{0xA78D, 0xA78D, d{0, -42280, 0}},
{0xA790, 0xA793, d{UpperLower, UpperLower, UpperLower}},
- {0xA7A0, 0xA7A9, d{UpperLower, UpperLower, UpperLower}},
+ {0xA796, 0xA7A9, d{UpperLower, UpperLower, UpperLower}},
{0xA7AA, 0xA7AA, d{0, -42308, 0}},
+ {0xA7AB, 0xA7AB, d{0, -42319, 0}},
+ {0xA7AC, 0xA7AC, d{0, -42315, 0}},
+ {0xA7AD, 0xA7AD, d{0, -42305, 0}},
+ {0xA7B0, 0xA7B0, d{0, -42258, 0}},
+ {0xA7B1, 0xA7B1, d{0, -42282, 0}},
{0xFF21, 0xFF3A, d{0, 32, 0}},
{0xFF41, 0xFF5A, d{-32, 0, -32}},
{0x10400, 0x10427, d{0, 40, 0}},
{0x10428, 0x1044F, d{-40, 0, -40}},
+ {0x118A0, 0x118BF, d{0, 32, 0}},
+ {0x118C0, 0x118DF, d{-32, 0, -32}},
}
var properties = [MaxLatin1 + 1]uint8{
0x00: pC, // '\x00'
@@ -6181,8 +6814,8 @@ var foldLl = &RangeTable{
{0x0248, 0x024e, 2},
{0x0345, 0x0370, 43},
{0x0372, 0x0376, 4},
- {0x0386, 0x0388, 2},
- {0x0389, 0x038a, 1},
+ {0x037f, 0x0386, 7},
+ {0x0388, 0x038a, 1},
{0x038c, 0x038e, 2},
{0x038f, 0x0391, 2},
{0x0392, 0x03a1, 1},
@@ -6195,7 +6828,7 @@ var foldLl = &RangeTable{
{0x0460, 0x0480, 2},
{0x048a, 0x04c0, 2},
{0x04c1, 0x04cd, 2},
- {0x04d0, 0x0526, 2},
+ {0x04d0, 0x052e, 2},
{0x0531, 0x0556, 1},
{0x10a0, 0x10c5, 1},
{0x10c7, 0x10cd, 6},
@@ -6230,18 +6863,21 @@ var foldLl = &RangeTable{
{0x2ceb, 0x2ced, 2},
{0x2cf2, 0xa640, 31054},
{0xa642, 0xa66c, 2},
- {0xa680, 0xa696, 2},
+ {0xa680, 0xa69a, 2},
{0xa722, 0xa72e, 2},
{0xa732, 0xa76e, 2},
{0xa779, 0xa77d, 2},
{0xa77e, 0xa786, 2},
{0xa78b, 0xa78d, 2},
{0xa790, 0xa792, 2},
- {0xa7a0, 0xa7aa, 2},
+ {0xa796, 0xa7aa, 2},
+ {0xa7ab, 0xa7ad, 1},
+ {0xa7b0, 0xa7b1, 1},
{0xff21, 0xff3a, 1},
},
R32: []Range32{
{0x10400, 0x10427, 1},
+ {0x118a0, 0x118bf, 1},
},
LatinOffset: 3,
}
@@ -6297,30 +6933,31 @@ var foldLu = &RangeTable{
{0x0250, 0x0254, 1},
{0x0256, 0x0257, 1},
{0x0259, 0x025b, 2},
- {0x0260, 0x0263, 3},
- {0x0265, 0x0266, 1},
- {0x0268, 0x0269, 1},
- {0x026b, 0x026f, 4},
+ {0x025c, 0x0260, 4},
+ {0x0261, 0x0265, 2},
+ {0x0266, 0x0268, 2},
+ {0x0269, 0x026b, 2},
+ {0x026c, 0x026f, 3},
{0x0271, 0x0272, 1},
{0x0275, 0x027d, 8},
{0x0280, 0x0283, 3},
- {0x0288, 0x028c, 1},
- {0x0292, 0x0345, 179},
- {0x0371, 0x0373, 2},
- {0x0377, 0x037b, 4},
+ {0x0287, 0x028c, 1},
+ {0x0292, 0x029e, 12},
+ {0x0345, 0x0371, 44},
+ {0x0373, 0x037b, 4},
{0x037c, 0x037d, 1},
{0x03ac, 0x03af, 1},
{0x03b1, 0x03ce, 1},
{0x03d0, 0x03d1, 1},
{0x03d5, 0x03d7, 1},
{0x03d9, 0x03ef, 2},
- {0x03f0, 0x03f2, 1},
+ {0x03f0, 0x03f3, 1},
{0x03f5, 0x03fb, 3},
{0x0430, 0x045f, 1},
{0x0461, 0x0481, 2},
{0x048b, 0x04bf, 2},
{0x04c2, 0x04ce, 2},
- {0x04cf, 0x0527, 2},
+ {0x04cf, 0x052f, 2},
{0x0561, 0x0586, 1},
{0x1d79, 0x1d7d, 4},
{0x1e01, 0x1e95, 2},
@@ -6349,18 +6986,19 @@ var foldLu = &RangeTable{
{0x2d01, 0x2d25, 1},
{0x2d27, 0x2d2d, 6},
{0xa641, 0xa66d, 2},
- {0xa681, 0xa697, 2},
+ {0xa681, 0xa69b, 2},
{0xa723, 0xa72f, 2},
{0xa733, 0xa76f, 2},
{0xa77a, 0xa77c, 2},
{0xa77f, 0xa787, 2},
{0xa78c, 0xa791, 5},
- {0xa793, 0xa7a1, 14},
- {0xa7a3, 0xa7a9, 2},
+ {0xa793, 0xa797, 4},
+ {0xa799, 0xa7a9, 2},
{0xff41, 0xff5a, 1},
},
R32: []Range32{
{0x10428, 0x1044f, 1},
+ {0x118c0, 0x118df, 1},
},
LatinOffset: 4,
}
@@ -6385,7 +7023,7 @@ var foldMn = &RangeTable{
// If there is no entry for a script name, there are no such points.
var FoldScript = map[string]*RangeTable{}
-// Range entries: 3471 16-bit, 832 32-bit, 4303 total.
-// Range bytes: 20826 16-bit, 9984 32-bit, 30810 total.
+// Range entries: 3532 16-bit, 1204 32-bit, 4736 total.
+// Range bytes: 21192 16-bit, 14448 32-bit, 35640 total.
// Fold orbit bytes: 63 pairs, 252 bytes
diff --git a/src/run.bash b/src/run.bash
index d13161e9de..b5f061d885 100755
--- a/src/run.bash
+++ b/src/run.bash
@@ -162,7 +162,7 @@ android-arm | dragonfly-386 | dragonfly-amd64 | freebsd-386 | freebsd-amd64 | fr
esac
) || exit $?
-# This tests cgo -godefs. That mode is not supported,
+# This tests cgo -cdefs. That mode is not supported,
# so it's okay if it doesn't work on some systems.
# In particular, it works badly with clang on OS X.
[ "$CGO_ENABLED" != 1 ] || [ "$GOOS" == darwin ] ||
@@ -170,6 +170,11 @@ esac
./test.bash || exit 1
) || exit $?
+[ "$CGO_ENABLED" != 1 ] || [ "$GOOS" == darwin ] ||
+(xcd ../misc/cgo/testgodefs
+./test.bash || exit 1
+) || exit $?
+
[ "$CGO_ENABLED" != 1 ] ||
[ "$GOHOSTOS" == windows ] ||
(xcd ../misc/cgo/testso
diff --git a/test/fixedbugs/bug490.go b/test/fixedbugs/bug490.go
new file mode 100644
index 0000000000..7d05f3945c
--- /dev/null
+++ b/test/fixedbugs/bug490.go
@@ -0,0 +1,16 @@
+// compile
+
+// 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.
+
+// The gccgo compiler used to crash building a comparison between an
+// interface and an empty struct literal.
+
+package p
+
+type S struct{}
+
+func F(v interface{}) bool {
+ return v == S{}
+}
diff --git a/test/fixedbugs/issue4388.go b/test/fixedbugs/issue4388.go
index 2e052e138d..b18c98bacd 100644
--- a/test/fixedbugs/issue4388.go
+++ b/test/fixedbugs/issue4388.go
@@ -17,18 +17,18 @@ type T struct {
}
func f1() {
- // The 4 here and below depends on the number of internal runtime frames
+ // The 5 here and below depends on the number of internal runtime frames
// that sit between a deferred function called during panic and
// the original frame. If that changes, this test will start failing and
// the number here will need to be updated.
- defer checkLine(4)
+ defer checkLine(5)
var t *T
var c io.Closer = t
c.Close()
}
func f2() {
- defer checkLine(4)
+ defer checkLine(5)
var t T
var c io.Closer = t
c.Close()
diff --git a/test/fixedbugs/issue5856.go b/test/fixedbugs/issue5856.go
index 35cadf8c9e..78ca3b9f6a 100644
--- a/test/fixedbugs/issue5856.go
+++ b/test/fixedbugs/issue5856.go
@@ -29,7 +29,7 @@ func f() {
}
func g() {
- _, file, line, _ := runtime.Caller(2)
+ _, file, line, _ := runtime.Caller(3)
if !strings.HasSuffix(file, "issue5856.go") || line != 28 {
fmt.Printf("BUG: defer called from %s:%d, want issue5856.go:28\n", file, line)
os.Exit(1)
diff --git a/test/fixedbugs/issue7760.go b/test/fixedbugs/issue7760.go
new file mode 100644
index 0000000000..cccae48910
--- /dev/null
+++ b/test/fixedbugs/issue7760.go
@@ -0,0 +1,25 @@
+// errorcheck
+
+// 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.
+
+// Verify that pointers can't be used as constants.
+
+package main
+
+import "unsafe"
+
+type myPointer unsafe.Pointer
+
+const _ = unsafe.Pointer(uintptr(1)) // ERROR "is not (a )?constant"
+const _ = myPointer(uintptr(1)) // ERROR "is not (a )?constant"
+
+const _ = (*int)(unsafe.Pointer(uintptr(1))) // ERROR "is not (a )?constant"
+const _ = (*int)(myPointer(uintptr(1))) // ERROR "is not (a )?constant"
+
+const _ = uintptr(unsafe.Pointer(uintptr(1))) // ERROR "is not (a )?constant"
+const _ = uintptr(myPointer(uintptr(1))) // ERROR "is not (a )?constant"
+
+const _ = []byte("") // ERROR "is not (a )?constant"
+const _ = []rune("") // ERROR "is not (a )?constant"
diff --git a/test/fixedbugs/issue8325.go b/test/fixedbugs/issue8325.go
new file mode 100644
index 0000000000..e22fd319db
--- /dev/null
+++ b/test/fixedbugs/issue8325.go
@@ -0,0 +1,31 @@
+// run
+
+// 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.
+
+// Issue 8325: corrupted byte operations during optimization
+// pass.
+
+package main
+
+const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+func main() {
+ var bytes = []byte{10, 20, 30, 40, 50}
+
+ for i, b := range bytes {
+ bytes[i] = alphanum[b%byte(len(alphanum))]
+ }
+
+ for _, b := range bytes {
+ switch {
+ case '0' <= b && b <= '9',
+ 'A' <= b && b <= 'Z':
+ default:
+ println("found a bad character", string(b))
+ panic("BUG")
+ }
+
+ }
+}
diff --git a/test/fixedbugs/issue8336.go b/test/fixedbugs/issue8336.go
new file mode 100644
index 0000000000..26bdeabb25
--- /dev/null
+++ b/test/fixedbugs/issue8336.go
@@ -0,0 +1,29 @@
+// run
+
+// 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.
+
+// Issue 8336. Order of evaluation of receive channels in select.
+
+package main
+
+type X struct {
+ c chan int
+}
+
+func main() {
+ defer func() {
+ recover()
+ }()
+ var x *X
+ select {
+ case <-x.c: // should fault and panic before foo is called
+ case <-foo():
+ }
+}
+
+func foo() chan int {
+ println("BUG: foo must not be called")
+ return make(chan int)
+}
diff --git a/test/fixedbugs/issue8475.go b/test/fixedbugs/issue8475.go
new file mode 100644
index 0000000000..e69794534c
--- /dev/null
+++ b/test/fixedbugs/issue8475.go
@@ -0,0 +1,25 @@
+// build
+
+// 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.
+
+// Issue 8745: comma-ok assignments should produce untyped bool as 2nd result.
+
+package main
+
+type mybool bool
+
+func main() {
+ var ok mybool
+ _ = ok
+
+ var i interface{}
+ _, ok = i.(int)
+
+ var m map[int]int
+ _, ok = m[0]
+
+ var c chan int
+ _, ok = <-c
+}
diff --git a/test/fixedbugs/issue8612.go b/test/fixedbugs/issue8612.go
new file mode 100644
index 0000000000..93370cf669
--- /dev/null
+++ b/test/fixedbugs/issue8612.go
@@ -0,0 +1,34 @@
+//compile
+
+// 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.
+
+// Gccgo had a bug comparing a struct or array value with an interface
+// values, when the struct or array was not addressable.
+
+package p
+
+type A [10]int
+
+type S struct {
+ i int
+}
+
+func F1() S {
+ return S{0}
+}
+
+func F2() A {
+ return A{}
+}
+
+func Cmp(v interface{}) bool {
+ if F1() == v {
+ return true
+ }
+ if F2() == v {
+ return true
+ }
+ return false
+}
diff --git a/test/live.go b/test/live.go
index 6ac1d6a464..35099d18ba 100644
--- a/test/live.go
+++ b/test/live.go
@@ -118,7 +118,10 @@ var i9 interface{}
func f9() bool {
g8()
x := i9
- return x != 99
+ // using complex number in comparison so that
+ // there is always a convT2E, no matter what the
+ // interface rules are.
+ return x != 99.0i // ERROR "live at call to convT2E: x"
}
// liveness formerly confused by UNDEF followed by RET,
@@ -184,7 +187,7 @@ func f11c() *int {
func f12() *int {
if b {
- select{}
+ select {}
} else {
return nil
}
@@ -215,7 +218,7 @@ func f15() {
var x string
_ = &x
x = g15() // ERROR "live at call to g15: x"
- print(x) // ERROR "live at call to printstring: x"
+ print(x) // ERROR "live at call to printstring: x"
}
func g15() string
@@ -287,7 +290,7 @@ var ch chan *byte
func f19() {
// dest temporary for channel receive.
var z *byte
-
+
if b {
z = <-ch // ERROR "live at call to chanrecv1: autotmp_[0-9]+$"
}
@@ -348,21 +351,21 @@ func f25(b bool) {
var x string
_ = &x
x = g15() // ERROR "live at call to g15: x"
- print(x) // ERROR "live at call to printstring: x"
+ print(x) // ERROR "live at call to printstring: x"
} // ERROR "live at call to deferreturn: x"
func g25()
-
+
// non-escaping ... slices passed to function call should die on return,
// so that the temporaries do not stack and do not cause ambiguously
// live variables.
func f26(b bool) {
if b {
- print26(1,2,3) // ERROR "live at call to print26: autotmp_[0-9]+$"
+ print26((*int)(nil), (*int)(nil), (*int)(nil)) // ERROR "live at call to print26: autotmp_[0-9]+$"
}
- print26(4,5,6) // ERROR "live at call to print26: autotmp_[0-9]+$"
- print26(7,8,9) // ERROR "live at call to print26: autotmp_[0-9]+$"
+ print26((*int)(nil), (*int)(nil), (*int)(nil)) // ERROR "live at call to print26: autotmp_[0-9]+$"
+ print26((*int)(nil), (*int)(nil), (*int)(nil)) // ERROR "live at call to print26: autotmp_[0-9]+$"
println()
}
@@ -374,10 +377,10 @@ func print26(...interface{})
func f27(b bool) {
x := 0
if b {
- call27(func() {x++}) // ERROR "live at call to call27: autotmp_[0-9]+$"
+ call27(func() { x++ }) // ERROR "live at call to call27: autotmp_[0-9]+$"
}
- call27(func() {x++}) // ERROR "live at call to call27: autotmp_[0-9]+$"
- call27(func() {x++}) // ERROR "live at call to call27: autotmp_[0-9]+$"
+ call27(func() { x++ }) // ERROR "live at call to call27: autotmp_[0-9]+$"
+ call27(func() { x++ }) // ERROR "live at call to call27: autotmp_[0-9]+$"
println()
}
@@ -386,10 +389,10 @@ func f27(b bool) {
func f27defer(b bool) {
x := 0
if b {
- defer call27(func() {x++}) // ERROR "live at call to deferproc: autotmp_[0-9]+$" "live at call to deferreturn: autotmp_[0-9]+$"
+ defer call27(func() { x++ }) // ERROR "live at call to deferproc: autotmp_[0-9]+$" "live at call to deferreturn: autotmp_[0-9]+$"
}
- defer call27(func() {x++}) // ERROR "live at call to deferproc: autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to deferreturn: autotmp_[0-9]+ autotmp_[0-9]+$" "ambiguously live"
- println() // ERROR "live at call to printnl: autotmp_[0-9]+ autotmp_[0-9]+$"
+ defer call27(func() { x++ }) // ERROR "live at call to deferproc: autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to deferreturn: autotmp_[0-9]+ autotmp_[0-9]+$" "ambiguously live"
+ println() // ERROR "live at call to printnl: autotmp_[0-9]+ autotmp_[0-9]+$"
} // ERROR "live at call to deferreturn: autotmp_[0-9]+ autotmp_[0-9]+$"
// and newproc (go) escapes to the heap
@@ -397,9 +400,9 @@ func f27defer(b bool) {
func f27go(b bool) {
x := 0
if b {
- go call27(func() {x++}) // ERROR "live at call to newobject: &x" "live at call to newproc: &x$"
+ go call27(func() { x++ }) // ERROR "live at call to newobject: &x" "live at call to newproc: &x$"
}
- go call27(func() {x++}) // ERROR "live at call to newobject: &x"
+ go call27(func() { x++ }) // ERROR "live at call to newobject: &x"
println()
}
@@ -412,11 +415,11 @@ var s1, s2, s3, s4, s5, s6, s7, s8, s9, s10 string
func f28(b bool) {
if b {
- print(s1+s2+s3+s4+s5+s6+s7+s8+s9+s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$"
+ print(s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$"
}
- print(s1+s2+s3+s4+s5+s6+s7+s8+s9+s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$"
- print(s1+s2+s3+s4+s5+s6+s7+s8+s9+s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$"
-}
+ print(s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$"
+ print(s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$"
+}
// map iterator should die on end of range loop
@@ -464,7 +467,7 @@ func f31(b1, b2, b3 bool) {
h31("b") // ERROR "live at call to newobject: autotmp_[0-9]+$" "live at call to convT2E: autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to h31: autotmp_[0-9]+$"
}
if b3 {
- panic("asdf") // ERROR "live at call to convT2E: autotmp_[0-9]+$" "live at call to panic: autotmp_[0-9]+$"
+ panic("asdf") // ERROR "live at call to convT2E: autotmp_[0-9]+$" "live at call to gopanic: autotmp_[0-9]+$"
}
print(b3)
}
@@ -584,13 +587,13 @@ func f39a() (x []int) {
func f39b() (x [10]*int) {
x = [10]*int{new(int)} // ERROR "live at call to newobject: x"
- println() // ERROR "live at call to printnl: x"
+ println() // ERROR "live at call to printnl: x"
return x
}
func f39c() (x [10]*int) {
x = [10]*int{new(int)} // ERROR "live at call to newobject: x"
- println() // ERROR "live at call to printnl: x"
+ println() // ERROR "live at call to printnl: x"
return
}
@@ -603,7 +606,7 @@ type T40 struct {
func newT40() *T40 {
ret := T40{ // ERROR "live at call to makemap: &ret"
- make(map[int]int),
+ make(map[int]int),
}
return &ret
}
diff --git a/test/map.go b/test/map.go
index 485e743fe4..2c1cf8a140 100644
--- a/test/map.go
+++ b/test/map.go
@@ -5,7 +5,7 @@
// license that can be found in the LICENSE file.
// Test maps, almost exhaustively.
-// NaN complexity test is in mapnan.go.
+// Complexity (linearity) test is in maplinear.go.
package main
diff --git a/test/maplinear.go b/test/maplinear.go
new file mode 100644
index 0000000000..56e50951af
--- /dev/null
+++ b/test/maplinear.go
@@ -0,0 +1,143 @@
+// +build darwin linux
+// run
+
+// 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.
+
+// Test that maps don't go quadratic for NaNs and other values.
+
+package main
+
+import (
+ "fmt"
+ "math"
+ "time"
+)
+
+// checkLinear asserts that the running time of f(n) is in O(n).
+// tries is the initial number of iterations.
+func checkLinear(typ string, tries int, f func(n int)) {
+ // Depending on the machine and OS, this test might be too fast
+ // to measure with accurate enough granularity. On failure,
+ // make it run longer, hoping that the timing granularity
+ // is eventually sufficient.
+
+ timeF := func(n int) time.Duration {
+ t1 := time.Now()
+ f(n)
+ return time.Since(t1)
+ }
+
+ t0 := time.Now()
+
+ n := tries
+ fails := 0
+ for {
+ t1 := timeF(n)
+ t2 := timeF(2 * n)
+
+ // should be 2x (linear); allow up to 3x
+ if t2 < 3*t1 {
+ if false {
+ fmt.Println(typ, "\t", time.Since(t0))
+ }
+ return
+ }
+ fails++
+ if fails == 6 {
+ panic(fmt.Sprintf("%s: too slow: %d inserts: %v; %d inserts: %v\n",
+ typ, n, t1, 2*n, t2))
+ }
+ if fails < 4 {
+ n *= 2
+ }
+ }
+}
+
+type I interface {
+ f()
+}
+
+type C int
+
+func (C) f() {}
+
+func main() {
+ // NaNs. ~31ms on a 1.6GHz Zeon.
+ checkLinear("NaN", 30000, func(n int) {
+ m := map[float64]int{}
+ nan := math.NaN()
+ for i := 0; i < n; i++ {
+ m[nan] = 1
+ }
+ if len(m) != n {
+ panic("wrong size map after nan insertion")
+ }
+ })
+
+ // ~6ms on a 1.6GHz Zeon.
+ checkLinear("eface", 10000, func(n int) {
+ m := map[interface{}]int{}
+ for i := 0; i < n; i++ {
+ m[i] = 1
+ }
+ })
+
+ // ~7ms on a 1.6GHz Zeon.
+ // Regression test for CL 119360043.
+ checkLinear("iface", 10000, func(n int) {
+ m := map[I]int{}
+ for i := 0; i < n; i++ {
+ m[C(i)] = 1
+ }
+ })
+
+ // ~6ms on a 1.6GHz Zeon.
+ checkLinear("int", 10000, func(n int) {
+ m := map[int]int{}
+ for i := 0; i < n; i++ {
+ m[i] = 1
+ }
+ })
+
+ // ~18ms on a 1.6GHz Zeon.
+ checkLinear("string", 10000, func(n int) {
+ m := map[string]int{}
+ for i := 0; i < n; i++ {
+ m[fmt.Sprint(i)] = 1
+ }
+ })
+
+ // ~6ms on a 1.6GHz Zeon.
+ checkLinear("float32", 10000, func(n int) {
+ m := map[float32]int{}
+ for i := 0; i < n; i++ {
+ m[float32(i)] = 1
+ }
+ })
+
+ // ~6ms on a 1.6GHz Zeon.
+ checkLinear("float64", 10000, func(n int) {
+ m := map[float64]int{}
+ for i := 0; i < n; i++ {
+ m[float64(i)] = 1
+ }
+ })
+
+ // ~22ms on a 1.6GHz Zeon.
+ checkLinear("complex64", 10000, func(n int) {
+ m := map[complex64]int{}
+ for i := 0; i < n; i++ {
+ m[complex(float32(i), float32(i))] = 1
+ }
+ })
+
+ // ~32ms on a 1.6GHz Zeon.
+ checkLinear("complex128", 10000, func(n int) {
+ m := map[complex128]int{}
+ for i := 0; i < n; i++ {
+ m[complex(float64(i), float64(i))] = 1
+ }
+ })
+}
diff --git a/test/mapnan.go b/test/mapnan.go
deleted file mode 100644
index f081cab01d..0000000000
--- a/test/mapnan.go
+++ /dev/null
@@ -1,56 +0,0 @@
-// +build darwin linux
-// run
-
-// 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.
-
-// Test that NaNs in maps don't go quadratic.
-
-package main
-
-import (
- "fmt"
- "math"
- "time"
-)
-
-func main() {
-
- // Test that NaNs in maps don't go quadratic.
- t := func(n int) time.Duration {
- t1 := time.Now()
- m := map[float64]int{}
- nan := math.NaN()
- for i := 0; i < n; i++ {
- m[nan] = 1
- }
- if len(m) != n {
- panic("wrong size map after nan insertion")
- }
- return time.Since(t1)
- }
-
- // Depending on the machine and OS, this test might be too fast
- // to measure with accurate enough granularity. On failure,
- // make it run longer, hoping that the timing granularity
- // is eventually sufficient.
-
- n := 30000 // ~8ms user time on a Mid 2011 MacBook Air (1.8 GHz Core i7)
- fails := 0
- for {
- t1 := t(n)
- t2 := t(2 * n)
- // should be 2x (linear); allow up to 3x
- if t2 < 3*t1 {
- return
- }
- fails++
- if fails == 6 {
- panic(fmt.Sprintf("too slow: %d inserts: %v; %d inserts: %v\n", n, t1, 2*n, t2))
- }
- if fails < 4 {
- n *= 2
- }
- }
-}
diff --git a/test/named1.go b/test/named1.go
index 62b874c5cb..febad64ece 100644
--- a/test/named1.go
+++ b/test/named1.go
@@ -41,21 +41,21 @@ func main() {
asBool(1 != 2) // ok now
asBool(i < j) // ok now
- _, b = m[2] // ERROR "cannot .* bool.*type Bool"
+ _, b = m[2] // ok now
var inter interface{}
- _, b = inter.(Map) // ERROR "cannot .* bool.*type Bool"
+ _, b = inter.(Map) // ok now
_ = b
var minter interface {
M()
}
- _, b = minter.(Map) // ERROR "cannot .* bool.*type Bool"
+ _, b = minter.(Map) // ok now
_ = b
_, bb := <-c
asBool(bb) // ERROR "cannot use.*type bool.*as type Bool"
- _, b = <-c // ERROR "cannot .* bool.*type Bool"
+ _, b = <-c // ok now
_ = b
asString(String(slice)) // ok
diff --git a/test/nosplit.go b/test/nosplit.go
index b5399ad38d..8dab2fc7a7 100644
--- a/test/nosplit.go
+++ b/test/nosplit.go
@@ -251,7 +251,7 @@ TestCases:
if line == "" {
continue
}
- for _, subline := range strings.Split(line, ";") {
+ for i, subline := range strings.Split(line, ";") {
subline = strings.TrimSpace(subline)
if subline == "" {
continue
@@ -264,6 +264,14 @@ TestCases:
}
name := m[1]
size, _ := strconv.Atoi(m[2])
+
+ // The limit was originally 128 but is now 384.
+ // Instead of rewriting the test cases above, adjust
+ // the first stack frame to use up the extra 32 bytes.
+ if i == 0 {
+ size += 384 - 128
+ }
+
if size%ptrSize == 4 {
continue TestCases
}
diff --git a/test/recover.go b/test/recover.go
index 071be6667a..6287d65076 100644
--- a/test/recover.go
+++ b/test/recover.go
@@ -47,6 +47,7 @@ func main() {
test11reflect1()
test11reflect2()
}
+ test111()
test12()
if !interp {
test12reflect1()
@@ -77,7 +78,7 @@ func mustRecoverBody(v1, v2, v3, x interface{}) {
}
v = v2
if v == nil {
- println("missing recover")
+ println("missing recover", x.(int))
die() // panic is useless here
}
if v != x {
@@ -137,7 +138,7 @@ func test1WithClosures() {
mustNotRecover()
v := recover()
if v == nil {
- println("missing recover")
+ println("missing recover", x.(int))
die()
}
if v != x {
@@ -406,6 +407,49 @@ func test11reflect2() {
panic(11)
}
+// tiny receiver, so basic wrapper in i.M()
+type T3deeper struct{}
+
+func (T3deeper) M() {
+ badstate() // difference from T3
+ mustRecoverBody(doubleRecover(), recover(), recover(), 111)
+}
+
+func test111() {
+ var i I = T3deeper{}
+ defer i.M()
+ panic(111)
+}
+
+type Tiny struct{}
+
+func (Tiny) M() {
+ panic(112)
+}
+
+// i.M is a wrapper, and i.M panics.
+//
+// This is a torture test for an old implementation of recover that
+// tried to deal with wrapper functions by doing some argument
+// positioning math on both entry and exit. Doing anything on exit
+// is a problem because sometimes functions exit via panic instead
+// of an ordinary return, so panic would have to know to do the
+// same math when unwinding the stack. It gets complicated fast.
+// This particular test never worked with the old scheme, because
+// panic never did the right unwinding math.
+//
+// The new scheme adjusts Panic.argp on entry to a wrapper.
+// It has no exit work, so if a wrapper is interrupted by a panic,
+// there's no cleanup that panic itself must do.
+// This test just works now.
+func badstate() {
+ defer func() {
+ recover()
+ }()
+ var i I = Tiny{}
+ i.M()
+}
+
// large receiver, so basic wrapper in i.M()
type T4 [2]string
diff --git a/test/slice3.go b/test/slice3.go
index 3cf34b57e7..857eaf3a09 100644
--- a/test/slice3.go
+++ b/test/slice3.go
@@ -19,10 +19,10 @@ var bout *bufio.Writer
func main() {
bout = bufio.NewWriter(os.Stdout)
-
+
fmt.Fprintf(bout, "%s", programTop)
fmt.Fprintf(bout, "func main() {\n")
-
+
index := []string{
"0",
"1",
@@ -38,7 +38,7 @@ func main() {
"v10",
"v20",
}
-
+
parse := func(s string) (n int, isconst bool) {
if s == "vminus1" {
return -1, false
@@ -69,7 +69,7 @@ func main() {
iconst && kconst && iv > kv,
iconst && base == "array" && iv > Cap,
jconst && base == "array" && jv > Cap,
- kconst && base == "array" && kv > Cap:
+ kconst && base == "array" && kv > Cap:
continue
}
@@ -82,7 +82,7 @@ func main() {
xlen = jv - iv
xcap = kv - iv
}
- fmt.Fprintf(bout, "\tcheckSlice(%q, func() []byte { return %s }, %d, %d, %d)\n", expr, expr, xbase, xlen, xcap)
+ fmt.Fprintf(bout, "\tcheckSlice(%q, func() []byte { return %s }, %d, %d, %d)\n", expr, expr, xbase, xlen, xcap)
}
}
}
@@ -147,9 +147,13 @@ func checkSlice(desc string, f func() []byte, xbase, xlen, xcap int) {
println(desc, "=", base, len, cap, "want panic")
return
}
- if base != uintptr(xbase) || len != uintptr(xlen) || cap != uintptr(xcap) {
+ if cap != 0 && base != uintptr(xbase) || base >= 10 || len != uintptr(xlen) || cap != uintptr(xcap) {
notOK()
- println(desc, "=", base, len, cap, "want", xbase, xlen, xcap)
+ if cap == 0 {
+ println(desc, "=", base, len, cap, "want", "0-9", xlen, xcap)
+ } else {
+ println(desc, "=", base, len, cap, "want", xbase, xlen, xcap)
+ }
}
}
diff --git a/test/slicecap.go b/test/slicecap.go
new file mode 100644
index 0000000000..dceb7e2cca
--- /dev/null
+++ b/test/slicecap.go
@@ -0,0 +1,90 @@
+// run
+
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "unsafe"
+
+var (
+ hello = "hello"
+ bytes = []byte{1, 2, 3, 4, 5}
+ ints = []int32{1, 2, 3, 4, 5}
+
+ five = 5
+
+ ok = true
+)
+
+func notOK() {
+ if ok {
+ println("BUG:")
+ ok = false
+ }
+}
+
+func checkString(desc, s string) {
+ p1 := *(*uintptr)(unsafe.Pointer(&s))
+ p2 := *(*uintptr)(unsafe.Pointer(&hello))
+ if p1-p2 >= 5 {
+ notOK()
+ println("string", desc, "has invalid base")
+ }
+}
+
+func checkBytes(desc string, s []byte) {
+ p1 := *(*uintptr)(unsafe.Pointer(&s))
+ p2 := *(*uintptr)(unsafe.Pointer(&bytes))
+ if p1-p2 >= 5 {
+ println("byte slice", desc, "has invalid base")
+ }
+}
+
+func checkInts(desc string, s []int32) {
+ p1 := *(*uintptr)(unsafe.Pointer(&s))
+ p2 := *(*uintptr)(unsafe.Pointer(&ints))
+ if p1-p2 >= 5*4 {
+ println("int slice", desc, "has invalid base")
+ }
+}
+
+func main() {
+ {
+ x := hello
+ checkString("x", x)
+ checkString("x[5:]", x[5:])
+ checkString("x[five:]", x[five:])
+ checkString("x[5:five]", x[5:five])
+ checkString("x[five:5]", x[five:5])
+ checkString("x[five:five]", x[five:five])
+ checkString("x[1:][2:][2:]", x[1:][2:][2:])
+ y := x[4:]
+ checkString("y[1:]", y[1:])
+ }
+ {
+ x := bytes
+ checkBytes("x", x)
+ checkBytes("x[5:]", x[5:])
+ checkBytes("x[five:]", x[five:])
+ checkBytes("x[5:five]", x[5:five])
+ checkBytes("x[five:5]", x[five:5])
+ checkBytes("x[five:five]", x[five:five])
+ checkBytes("x[1:][2:][2:]", x[1:][2:][2:])
+ y := x[4:]
+ checkBytes("y[1:]", y[1:])
+ }
+ {
+ x := ints
+ checkInts("x", x)
+ checkInts("x[5:]", x[5:])
+ checkInts("x[five:]", x[five:])
+ checkInts("x[5:five]", x[5:five])
+ checkInts("x[five:5]", x[five:5])
+ checkInts("x[five:five]", x[five:five])
+ checkInts("x[1:][2:][2:]", x[1:][2:][2:])
+ y := x[4:]
+ checkInts("y[1:]", y[1:])
+ }
+}